Weather support

This commit is contained in:
proller 2013-07-27 22:34:30 +04:00
parent e65d8ad655
commit 3aedfac968
23 changed files with 552 additions and 91 deletions

@ -14,11 +14,11 @@ minetest.register_entity("__builtin:falling_node", {
visual_size = {x=0.667, y=0.667}, visual_size = {x=0.667, y=0.667},
}, },
nodename = "", node = {},
set_node = function(self, nodename) set_node = function(self, node)
self.nodename = nodename self.node = node
local stack = ItemStack(nodename) local stack = ItemStack(node.name)
local itemtable = stack:to_table() local itemtable = stack:to_table()
local itemname = nil local itemname = nil
if itemtable then if itemtable then
@ -32,20 +32,19 @@ minetest.register_entity("__builtin:falling_node", {
end end
prop = { prop = {
is_visible = true, is_visible = true,
textures = {nodename}, textures = {node.name},
} }
self.object:set_properties(prop) self.object:set_properties(prop)
end, end,
get_staticdata = function(self) get_staticdata = function(self)
return self.nodename return self.node.name
end, end,
on_activate = function(self, staticdata) on_activate = function(self, staticdata)
self.nodename = staticdata
self.object:set_armor_groups({immortal=1}) self.object:set_armor_groups({immortal=1})
--self.object:setacceleration({x=0, y=-10, z=0}) --self.object:setacceleration({x=0, y=-10, z=0})
self:set_node(self.nodename) self:set_node({name=staticdata})
end, end,
on_step = function(self, dtime) on_step = function(self, dtime)
@ -57,8 +56,10 @@ minetest.register_entity("__builtin:falling_node", {
local bcn = minetest.get_node(bcp) local bcn = minetest.get_node(bcp)
-- Note: walkable is in the node definition, not in item groups -- Note: walkable is in the node definition, not in item groups
if minetest.registered_nodes[bcn.name] and if minetest.registered_nodes[bcn.name] and
minetest.registered_nodes[bcn.name].walkable then minetest.registered_nodes[bcn.name].walkable or
if minetest.registered_nodes[bcn.name].buildable_to then (minetest.get_node_group(self.node.name, "float") ~= 0 and minetest.registered_nodes[bcn.name].liquidtype ~= "none")
then
if minetest.registered_nodes[bcn.name].buildable_to and (minetest.get_node_group(self.node.name, "float") == 0 or minetest.registered_nodes[bcn.name].liquidtype == "none") then
minetest.remove_node(bcp) minetest.remove_node(bcp)
return return
end end
@ -83,7 +84,7 @@ minetest.register_entity("__builtin:falling_node", {
end end
end end
-- Create node and remove entity -- Create node and remove entity
minetest.add_node(np, {name=self.nodename}) minetest.add_node(np, self.node)
self.object:remove() self.object:remove()
nodeupdate(np) nodeupdate(np)
else else
@ -92,9 +93,9 @@ minetest.register_entity("__builtin:falling_node", {
end end
}) })
function spawn_falling_node(p, nodename) function spawn_falling_node(p, node)
obj = minetest.add_entity(p, "__builtin:falling_node") obj = minetest.add_entity(p, "__builtin:falling_node")
obj:get_luaentity():set_node(nodename) obj:get_luaentity():set_node(node)
end end
function drop_attached_node(p) function drop_attached_node(p)
@ -150,13 +151,14 @@ function nodeupdate_single(p, delay)
n_bottom = minetest.get_node(p_bottom) n_bottom = minetest.get_node(p_bottom)
-- Note: walkable is in the node definition, not in item groups -- Note: walkable is in the node definition, not in item groups
if minetest.registered_nodes[n_bottom.name] and if minetest.registered_nodes[n_bottom.name] and
(minetest.get_node_group(n.name, "float") == 0 or minetest.registered_nodes[n_bottom.name].liquidtype == "none") and
(not minetest.registered_nodes[n_bottom.name].walkable or (not minetest.registered_nodes[n_bottom.name].walkable or
minetest.registered_nodes[n_bottom.name].buildable_to) then minetest.registered_nodes[n_bottom.name].buildable_to) then
if delay then if delay then
minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false) minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
else else
minetest.remove_node(p) minetest.remove_node(p)
spawn_falling_node(p, n.name) spawn_falling_node(p, n)
nodeupdate(p) nodeupdate(p)
end end
end end
@ -170,7 +172,7 @@ function nodeupdate_single(p, delay)
end end
end end
function nodeupdate(p) function nodeupdate(p, delay)
-- Round p to prevent falling entities to get stuck -- Round p to prevent falling entities to get stuck
p.x = math.floor(p.x+0.5) p.x = math.floor(p.x+0.5)
p.y = math.floor(p.y+0.5) p.y = math.floor(p.y+0.5)
@ -179,7 +181,7 @@ function nodeupdate(p)
for x = -1,1 do for x = -1,1 do
for y = -1,1 do for y = -1,1 do
for z = -1,1 do for z = -1,1 do
nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, not (x==0 and y==0 and z==0)) nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0))
end end
end end
end end

@ -322,6 +322,8 @@ param2 is reserved for the engine when any of these are used:
facedir modulo 4 = axisdir facedir modulo 4 = axisdir
0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y- 0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y-
facedir's two less significant bits are rotation around the axis facedir's two less significant bits are rotation around the axis
paramtype2 == "leveled"
^ The drawn node level is read from param2, like flowingliquid
Nodes can also contain extra data. See "Node Metadata". Nodes can also contain extra data. See "Node Metadata".
@ -353,7 +355,7 @@ Node selection boxes are defined using "node boxes"
The "nodebox" node drawtype allows defining visual of nodes consisting of The "nodebox" node drawtype allows defining visual of nodes consisting of
arbitrary number of boxes. It allows defining stuff like stairs. Only the arbitrary number of boxes. It allows defining stuff like stairs. Only the
"fixed" box type is supported for these. "fixed" and "leveled" box type is supported for these.
^ Please note that this is still experimental, and may be incompatibly ^ Please note that this is still experimental, and may be incompatibly
changed in the future. changed in the future.
@ -381,6 +383,8 @@ A box is defined as:
A box of a regular node would look like: A box of a regular node would look like:
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
type = "leveled" is same as "fixed", but y2 will be automaticaly setted to level from param2
Ore types Ore types
--------------- ---------------
These tell in what manner the ore is generated. These tell in what manner the ore is generated.
@ -1258,6 +1262,18 @@ minetest.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm)
^ algorithm: A*_noprefetch(default), A*, Dijkstra ^ algorithm: A*_noprefetch(default), A*, Dijkstra
minetest.spawn_tree (pos, {treedef}) minetest.spawn_tree (pos, {treedef})
^ spawns L-System tree at given pos with definition in treedef table ^ spawns L-System tree at given pos with definition in treedef table
minetest.transforming_liquid_add(pos)
^ add node to liquid update queue
minetest.get_node_max_level(pos)
^ get max available level for leveled node
minetest.get_node_level(pos)
^ get level of leveled node (water, snow)
minetest.add_node_level(pos, level)
^ increase level of leveled node by level, default level = 1, if totallevel > maxlevel returns rest (total-max). can be negative for decreasing
minetest.get_heat(pos)
^ heat at pos
minetest.get_humidity(pos)
^ humidity at pos
Inventory: Inventory:
minetest.get_inventory(location) -> InvRef minetest.get_inventory(location) -> InvRef
@ -1965,6 +1981,8 @@ Node definition (register_node)
liquid_alternative_source = "", -- Source version of flowing liquid liquid_alternative_source = "", -- Source version of flowing liquid
liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7) liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7)
liquid_renewable = true, -- Can new liquid source be created by placing liquid_renewable = true, -- Can new liquid source be created by placing
freezemelt = "", -- water for snow/ice, ice/snow for water
leveled = 0, -- Block contain level in param2. value - default level, used for snow. Dont forget use "leveled" type nodebox
liquid_range = 8, -- number of flowing nodes arround source (max. 8) liquid_range = 8, -- number of flowing nodes arround source (max. 8)
drowning = true, -- Player will drown in these drowning = true, -- Player will drown in these
two or more sources nearly? two or more sources nearly?

@ -107,6 +107,8 @@
#liquid_fast_flood = 1 #liquid_fast_flood = 1
# Underground water and lava springs, its infnity sources if liquid_finite enabled # Underground water and lava springs, its infnity sources if liquid_finite enabled
#underground_springs = 1 #underground_springs = 1
# Enable weather (cold-hot, water freeze-melt). use only with liquid_finite=1
#weather = false
# Enable nice leaves; disable for speed # Enable nice leaves; disable for speed
#new_style_leaves = true #new_style_leaves = true
# Enable smooth lighting with simple ambient occlusion; # Enable smooth lighting with simple ambient occlusion;
@ -268,7 +270,9 @@
# Interval of sending time of day to clients # Interval of sending time of day to clients
#time_send_interval = 5 #time_send_interval = 5
# Length of day/night cycle. 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged # Length of day/night cycle. 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged
#time_speed = 96 #time_speed = 72
# Length of year in days for seasons change. With default time_speed 365 days = 5 real days for year. 30 days = 10 real hours
#year_days = 30
#server_unload_unused_data_timeout = 29 #server_unload_unused_data_timeout = 29
# Interval of saving important changes in the world # Interval of saving important changes in the world
#server_map_save_interval = 5.3 #server_map_save_interval = 5.3

@ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "treegen.h" // For treegen::make_tree #include "treegen.h" // For treegen::make_tree
#include "main.h" // for g_settings #include "main.h" // for g_settings
#include "map.h" #include "map.h"
#include "cpp_api/scriptapi.h"
#include "log.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -166,86 +168,220 @@ public:
} }
}; };
class LiquidFlowABM : public ActiveBlockModifier class LiquidFlowABM : public ActiveBlockModifier {
{ private:
private: std::set<std::string> contents;
std::set<std::string> contents;
public: public:
LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
{ std::set<content_t> liquids;
std::set<content_t> liquids; nodemgr->getIds("group:liquid", liquids);
nodemgr->getIds("group:liquid", liquids); for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++) contents.insert(nodemgr->get(*k).liquid_alternative_flowing);
contents.insert(nodemgr->get(*k).liquid_alternative_flowing); }
virtual std::set<std::string> getTriggerContents() {
} return contents;
virtual std::set<std::string> getTriggerContents() }
{ virtual float getTriggerInterval()
return contents; { return 10.0; }
} virtual u32 getTriggerChance()
virtual float getTriggerInterval() { return 10; }
{ return 10.0; } virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
virtual u32 getTriggerChance() ServerMap *map = &env->getServerMap();
{ return 10; } if (map->transforming_liquid_size() > 500)
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) return;
{ map->transforming_liquid_add(p);
ServerMap *map = &env->getServerMap(); }
if (map->transforming_liquid_size() > 500)
return;
map->transforming_liquid_add(p);
//if ((*map).m_transforming_liquid.size() < 500) (*map).m_transforming_liquid.push_back(p);
}
}; };
class LiquidDropABM : public ActiveBlockModifier class LiquidDropABM : public ActiveBlockModifier {
{ private:
private: std::set<std::string> contents;
std::set<std::string> contents;
public: public:
LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
{ std::set<content_t> liquids;
std::set<content_t> liquids; nodemgr->getIds("group:liquid", liquids);
nodemgr->getIds("group:liquid", liquids); for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++) contents.insert(nodemgr->get(*k).liquid_alternative_source);
contents.insert(nodemgr->get(*k).liquid_alternative_source); }
} virtual std::set<std::string> getTriggerContents()
virtual std::set<std::string> getTriggerContents() { return contents; }
{ return contents; } virtual std::set<std::string> getRequiredNeighbors() {
virtual std::set<std::string> getRequiredNeighbors() std::set<std::string> neighbors;
{ neighbors.insert("mapgen_air");
std::set<std::string> neighbors; return neighbors;
neighbors.insert("mapgen_air"); }
return neighbors; virtual float getTriggerInterval()
} { return 20.0; }
virtual float getTriggerInterval() virtual u32 getTriggerChance()
{ return 20.0; } { return 10; }
virtual u32 getTriggerChance() virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
{ return 10; } ServerMap *map = &env->getServerMap();
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) if (map->transforming_liquid_size() > 500)
{ return;
ServerMap *map = &env->getServerMap(); if ( map->getNodeNoEx(p - v3s16(0, 1, 0 )).getContent() != CONTENT_AIR // below
if (map->transforming_liquid_size() > 500) && map->getNodeNoEx(p - v3s16(1, 0, 0 )).getContent() != CONTENT_AIR // right
return; && map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent() != CONTENT_AIR // left
if ( map->getNodeNoEx(p - v3s16(0, 1, 0 )).getContent() != CONTENT_AIR // below && map->getNodeNoEx(p - v3s16(0, 0, 1 )).getContent() != CONTENT_AIR // back
&& map->getNodeNoEx(p - v3s16(1, 0, 0 )).getContent() != CONTENT_AIR // right && map->getNodeNoEx(p - v3s16(0, 0, -1)).getContent() != CONTENT_AIR // front
&& map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent() != CONTENT_AIR // left )
&& map->getNodeNoEx(p - v3s16(0, 0, 1 )).getContent() != CONTENT_AIR // back return;
&& map->getNodeNoEx(p - v3s16(0, 0, -1)).getContent() != CONTENT_AIR // front map->transforming_liquid_add(p);
) }
return;
map->transforming_liquid_add(p);
}
}; };
void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) class LiquidFreeze : public ActiveBlockModifier {
{ public:
LiquidFreeze(ServerEnvironment *env, INodeDefManager *nodemgr) { }
virtual std::set<std::string> getTriggerContents() {
std::set<std::string> s;
s.insert("group:freezes");
return s;
}
virtual std::set<std::string> getRequiredNeighbors() {
std::set<std::string> s;
s.insert("mapgen_air");
s.insert("group:melts");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 20; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
ServerMap *map = &env->getServerMap();
INodeDefManager *ndef = env->getGameDef()->ndef();
float heat = map->getHeat(env, p);
//heater = rare
if (heat <= -1 && (heat <= -50 || ((myrand_range(-50, heat)) <= -30))) {
content_t c_self = n.getContent();
// making freeze not annoying, do not freeze random blocks in center of ocean
// todo: any block not water (dont freeze _source near _flowing)
content_t c;
bool allow = heat < -40;
// todo: make for(...)
if (!allow) {
c = map->getNodeNoEx(p - v3s16(0, 1, 0 )).getContent(); // below
if (c == CONTENT_AIR || c == CONTENT_IGNORE)
return; // do not freeze when falling
if (c != c_self && c != CONTENT_IGNORE) allow = 1;
if (!allow) {
c = map->getNodeNoEx(p - v3s16(1, 0, 0 )).getContent(); // right
if (c != c_self && c != CONTENT_IGNORE) allow = 1;
if (!allow) {
c = map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent(); // left
if (c != c_self && c != CONTENT_IGNORE) allow = 1;
if (!allow) {
c = map->getNodeNoEx(p - v3s16(0, 0, 1 )).getContent(); // back
if (c != c_self && c != CONTENT_IGNORE) allow = 1;
if (!allow) {
c = map->getNodeNoEx(p - v3s16(0, 0, -1)).getContent(); // front
if (c != c_self && c != CONTENT_IGNORE) allow = 1;
}
}
}
}
}
if (allow) {
n.setContent(ndef->getId(ndef->get(n).freezemelt));
map->addNodeWithEvent(p, n);
}
}
}
};
class LiquidMeltWeather : public ActiveBlockModifier {
public:
LiquidMeltWeather(ServerEnvironment *env, INodeDefManager *nodemgr) { }
virtual std::set<std::string> getTriggerContents() {
std::set<std::string> s;
s.insert("group:melts");
return s;
}
virtual std::set<std::string> getRequiredNeighbors() {
std::set<std::string> s;
s.insert("mapgen_air");
s.insert("group:freezes");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 20; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
ServerMap *map = &env->getServerMap();
INodeDefManager *ndef = env->getGameDef()->ndef();
float heat = map->getHeat(env, p);
if (heat >= 1 && (heat >= 40 || ((myrand_range(heat, 40)) >= 20))) {
n.setContent(ndef->getId(ndef->get(n).freezemelt));
if (!n.getLevel(ndef))
n.addLevel(ndef);
map->addNodeWithEvent(p, n);
env->getScriptIface()->node_falling_update(p);
}
}
};
class LiquidMeltHot : public ActiveBlockModifier {
public:
LiquidMeltHot(ServerEnvironment *env, INodeDefManager *nodemgr) { }
virtual std::set<std::string> getTriggerContents() {
std::set<std::string> s;
s.insert("group:melts");
return s;
}
virtual std::set<std::string> getRequiredNeighbors() {
std::set<std::string> s;
s.insert("group:igniter");
s.insert("group:hot");
return s;
}
virtual float getTriggerInterval()
{ return 2.0; }
virtual u32 getTriggerChance()
{ return 4; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
ServerMap *map = &env->getServerMap();
INodeDefManager *ndef = env->getGameDef()->ndef();
n.setContent(ndef->getId(ndef->get(n).freezemelt));
if (!n.getLevel(ndef))
n.addLevel(ndef);
map->addNodeWithEvent(p, n);
env->getScriptIface()->node_falling_update(p);
}
};
class LiquidMeltAround : public LiquidMeltHot {
public:
LiquidMeltAround(ServerEnvironment *env, INodeDefManager *nodemgr)
: LiquidMeltHot(env, nodemgr) { }
virtual std::set<std::string> getRequiredNeighbors() {
std::set<std::string> s;
s.insert("group:melt_around");
return s;
}
virtual float getTriggerInterval()
{ return 40.0; }
virtual u32 getTriggerChance()
{ return 60; }
};
void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) {
env->addActiveBlockModifier(new GrowGrassABM()); env->addActiveBlockModifier(new GrowGrassABM());
env->addActiveBlockModifier(new RemoveGrassABM()); env->addActiveBlockModifier(new RemoveGrassABM());
env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef)); env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
if (g_settings->getBool("liquid_finite")) { if (g_settings->getBool("liquid_finite")) {
env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef)); env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef));
env->addActiveBlockModifier(new LiquidDropABM(env, nodedef)); env->addActiveBlockModifier(new LiquidDropABM(env, nodedef));
env->addActiveBlockModifier(new LiquidMeltHot(env, nodedef));
env->addActiveBlockModifier(new LiquidMeltAround(env, nodedef));
if (g_settings->getBool("weather")) {
env->addActiveBlockModifier(new LiquidFreeze(env, nodedef));
env->addActiveBlockModifier(new LiquidMeltWeather(env, nodedef));
}
} }
} }

@ -178,6 +178,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096"); settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
settings->setDefault("time_send_interval", "5"); settings->setDefault("time_send_interval", "5");
settings->setDefault("time_speed", "72"); settings->setDefault("time_speed", "72");
settings->setDefault("year_days", "30");
settings->setDefault("server_unload_unused_data_timeout", "29"); settings->setDefault("server_unload_unused_data_timeout", "29");
settings->setDefault("server_map_save_interval", "5.3"); settings->setDefault("server_map_save_interval", "5.3");
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0"); settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
@ -214,6 +215,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("liquid_relax", "2"); settings->setDefault("liquid_relax", "2");
settings->setDefault("liquid_fast_flood", "1"); settings->setDefault("liquid_fast_flood", "1");
settings->setDefault("underground_springs", "1"); settings->setDefault("underground_springs", "1");
settings->setDefault("weather", "false");
//mapgen stuff //mapgen stuff
settings->setDefault("mg_name", "v6"); settings->setDefault("mg_name", "v6");

@ -488,6 +488,14 @@ void *EmergeThread::Thread() {
if (block) if (block)
modified_blocks[p] = block; modified_blocks[p] = block;
// Update weather data in mapblock
for(std::map<v3s16, MapBlock *>::iterator
i = modified_blocks.begin();
i != modified_blocks.end(); ++i) {
map->getHeat(m_server->m_env, MAP_BLOCKSIZE*i->first ,i->second);
map->getHumidity(m_server->m_env, MAP_BLOCKSIZE*i->first, i->second);
}
// Set the modified blocks unsent for all the clients // Set the modified blocks unsent for all the clients
for (std::map<u16, RemoteClient*>::iterator for (std::map<u16, RemoteClient*>::iterator
i = m_server->m_clients.begin(); i = m_server->m_clients.begin();

@ -303,6 +303,7 @@ public:
//check if there's a line of sight between two positions //check if there's a line of sight between two positions
bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0); bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0);
u32 getGameTime() { return m_game_time; }
private: private:
/* /*

@ -2455,6 +2455,7 @@ void the_game(
camera.step(dtime); camera.step(dtime);
v3f player_position = player->getPosition(); v3f player_position = player->getPosition();
v3s16 pos_i = floatToInt(player_position, BS);
v3f camera_position = camera.getPosition(); v3f camera_position = camera.getPosition();
v3f camera_direction = camera.getDirection(); v3f camera_direction = camera.getDirection();
f32 camera_fov = camera.getFovMax(); f32 camera_fov = camera.getFovMax();
@ -3034,7 +3035,9 @@ void the_game(
<<", "<<(player_position.Y/BS) <<", "<<(player_position.Y/BS)
<<", "<<(player_position.Z/BS) <<", "<<(player_position.Z/BS)
<<") (yaw="<<(wrapDegrees_0_360(camera_yaw)) <<") (yaw="<<(wrapDegrees_0_360(camera_yaw))
<<") (seed = "<<((unsigned long long)client.getMapSeed()) <<") (t="<<client.getEnv().getClientMap().getHeat(pos_i)
<<"C, h="<<client.getEnv().getClientMap().getHumidity(pos_i)
<<"%) (seed = "<<((unsigned long long)client.getMapSeed())
<<")"; <<")";
guitext2->setText(narrow_to_wide(os.str()).c_str()); guitext2->setText(narrow_to_wide(os.str()).c_str());
guitext2->setVisible(true); guitext2->setVisible(true);

@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "emerge.h" #include "emerge.h"
#include "mapgen_v6.h" #include "mapgen_v6.h"
#include "mapgen_indev.h" #include "mapgen_indev.h"
#include "biome.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -1087,6 +1088,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
/* /*
Add neighboring liquid nodes and the node itself if it is Add neighboring liquid nodes and the node itself if it is
liquid (=water node was added) to transform queue. liquid (=water node was added) to transform queue.
note: todo: for liquid_finite enough to add only self node
*/ */
v3s16 dirs[7] = { v3s16 dirs[7] = {
v3s16(0,0,0), // self v3s16(0,0,0), // self
@ -1278,6 +1280,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
/* /*
Add neighboring liquid nodes and this node to transform queue. Add neighboring liquid nodes and this node to transform queue.
(it's vital for the node itself to get updated last.) (it's vital for the node itself to get updated last.)
note: todo: for liquid_finite enough to add only self node
*/ */
v3s16 dirs[7] = { v3s16 dirs[7] = {
v3s16(0,0,1), // back v3s16(0,0,1), // back
@ -2364,6 +2367,26 @@ void Map::removeNodeTimer(v3s16 p)
block->m_node_timers.remove(p_rel); block->m_node_timers.remove(p_rel);
} }
s16 Map::getHeat(v3s16 p)
{
MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
return block->heat;
}
//errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
return 0;
}
s16 Map::getHumidity(v3s16 p)
{
MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
return block->humidity;
}
//errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
return 0;
}
/* /*
ServerMap ServerMap
*/ */
@ -3863,7 +3886,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
<<" (SerializationError). " <<" (SerializationError). "
<<"what()="<<e.what() <<"what()="<<e.what()
<<std::endl; <<std::endl;
//" Ignoring. A new one will be generated. // Ignoring. A new one will be generated.
assert(0); assert(0);
// TODO: Backup file; name is in fullpath. // TODO: Backup file; name is in fullpath.
@ -4039,6 +4062,63 @@ void ServerMap::PrintInfo(std::ostream &out)
out<<"ServerMap: "; out<<"ServerMap: ";
} }
s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
{
if(block == NULL)
block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
if (env->getGameTime() - block->heat_time < 10)
return block->heat;
}
//variant 1: full random
//f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
//variant 2: season change based on default heat map
f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z, m_emerge->params->seed);
heat += -30; // -30 - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
f32 base = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
base /= ( 86400 * g_settings->getS16("year_days") );
base += (f32)p.X / 3000;
heat += 30 * sin(base * M_PI); // season
heat += p.Y / -333; // upper=colder, lower=hotter
// daily change, hotter at sun +4, colder at night -4
heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5);
if(block != NULL) {
block->heat = heat;
block->heat_time = env->getGameTime();
}
return heat;
}
s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
{
if(block == NULL)
block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
if (env->getGameTime() - block->humidity_time < 10)
return block->humidity;
}
f32 humidity = NoisePerlin3D( m_emerge->biomedef->np_humidity,
p.X, env->getGameTime()/10, p.Z,
m_emerge->params->seed);
humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
//todo like heat//humidity += 20 * ( sin(((f32)p.Z / 300) * M_PI) - 0.5);
if (humidity > 100) humidity = 100;
if (humidity < 0) humidity = 0;
if(block != NULL) {
block->humidity = humidity;
block->humidity_time = env->getGameTime();
}
return humidity;
}
/* /*
MapVoxelManipulator MapVoxelManipulator
*/ */

@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "modifiedstate.h" #include "modifiedstate.h"
#include "util/container.h" #include "util/container.h"
#include "nodetimer.h" #include "nodetimer.h"
#include "environment.h"
extern "C" { extern "C" {
#include "sqlite3.h" #include "sqlite3.h"
@ -336,6 +337,9 @@ public:
void transforming_liquid_add(v3s16 p); void transforming_liquid_add(v3s16 p);
s32 transforming_liquid_size(); s32 transforming_liquid_size();
virtual s16 getHeat(v3s16 p);
virtual s16 getHumidity(v3s16 p);
protected: protected:
friend class LuaVoxelManip; friend class LuaVoxelManip;
@ -483,6 +487,10 @@ public:
// Parameters fed to the Mapgen // Parameters fed to the Mapgen
MapgenParams *m_mgparams; MapgenParams *m_mgparams;
virtual s16 getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block = NULL);
virtual s16 getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block = NULL);
private: private:
// Seed used for all kinds of randomness in generation // Seed used for all kinds of randomness in generation
u64 m_seed; u64 m_seed;

@ -58,7 +58,11 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0), m_usage_timer(0),
m_refcount(0) m_refcount(0),
heat_time(0),
heat(0),
humidity_time(0),
humidity(0)
{ {
data = NULL; data = NULL;
if(dummy == false) if(dummy == false)
@ -632,6 +636,11 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
// Node timers // Node timers
m_node_timers.serialize(os, version); m_node_timers.serialize(os, version);
} }
} else {
if(version >= 26){
writeF1000(os, heat);
writeF1000(os, humidity);
}
} }
} }
@ -734,6 +743,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
<<": Node timers (ver>=25)"<<std::endl); <<": Node timers (ver>=25)"<<std::endl);
m_node_timers.deSerialize(is, version); m_node_timers.deSerialize(is, version);
} }
} else {
if(version >= 26){
heat = readF1000(is);
humidity = readF1000(is);
}
} }
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())

@ -518,6 +518,11 @@ public:
NodeTimerList m_node_timers; NodeTimerList m_node_timers;
StaticObjectList m_static_objects; StaticObjectList m_static_objects;
s16 heat;
u32 heat_time;
s16 humidity;
u32 humidity_time;
private: private:
/* /*
Private member variables Private member variables

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_mapnode.h" // For mapnode_translate_*_internal #include "content_mapnode.h" // For mapnode_translate_*_internal
#include "serialization.h" // For ser_ver_supported #include "serialization.h" // For ser_ver_supported
#include "util/serialize.h" #include "util/serialize.h"
#include "log.h"
#include <string> #include <string>
#include <sstream> #include <sstream>
@ -359,9 +360,23 @@ std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
return transformNodeBox(*this, f.selection_box, nodemgr); return transformNodeBox(*this, f.selection_box, nodemgr);
} }
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
// todo: after update in all games leave only if (f.param_type_2 ==
if( f.liquid_type == LIQUID_SOURCE
|| f.liquid_type == LIQUID_FLOWING
|| f.param_type_2 == CPT2_FLOWINGLIQUID)
return LIQUID_LEVEL_MAX;
if(f.leveled || f.param_type_2 == CPT2_LEVELED)
return LEVELED_MAX;
return 0;
}
u8 MapNode::getLevel(INodeDefManager *nodemgr) const u8 MapNode::getLevel(INodeDefManager *nodemgr) const
{ {
const ContentFeatures &f = nodemgr->get(*this); const ContentFeatures &f = nodemgr->get(*this);
// todo: after update in all games leave only if (f.param_type_2 ==
if(f.liquid_type == LIQUID_SOURCE) if(f.liquid_type == LIQUID_SOURCE)
return LIQUID_LEVEL_SOURCE; return LIQUID_LEVEL_SOURCE;
if (f.param_type_2 == CPT2_FLOWINGLIQUID) if (f.param_type_2 == CPT2_FLOWINGLIQUID)
@ -377,6 +392,37 @@ u8 MapNode::getLevel(INodeDefManager *nodemgr) const
return 0; return 0;
} }
u8 MapNode::addLevel(INodeDefManager *nodemgr, s8 add)
{
s8 level = getLevel(nodemgr);
u8 rest = 0;
if (add == 0) level = 1;
level += add;
if (level < 1) {
setContent(CONTENT_AIR);
return 0;
}
const ContentFeatures &f = nodemgr->get(*this);
if ( f.param_type_2 == CPT2_FLOWINGLIQUID
|| f.liquid_type == LIQUID_FLOWING
|| f.liquid_type == LIQUID_SOURCE) {
if (level >= LIQUID_LEVEL_MAX) {
rest = level - LIQUID_LEVEL_MAX;
setContent(nodemgr->getId(f.liquid_alternative_source));
} else {
setContent(nodemgr->getId(f.liquid_alternative_flowing));
setParam2(level & LIQUID_LEVEL_MASK);
}
} else if (f.leveled || f.param_type_2 == CPT2_LEVELED) {
if (level > LEVELED_MAX) {
rest = level - LEVELED_MAX;
level = LEVELED_MAX;
}
setParam2(level & LEVELED_MASK);
}
return rest;
}
u32 MapNode::serializedLength(u8 version) u32 MapNode::serializedLength(u8 version)
{ {
if(!ser_ver_supported(version)) if(!ser_ver_supported(version))

@ -227,7 +227,9 @@ struct MapNode
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const; std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
/* Liquid helpers */ /* Liquid helpers */
u8 getMaxLevel(INodeDefManager *nodemgr) const;
u8 getLevel(INodeDefManager *nodemgr) const; u8 getLevel(INodeDefManager *nodemgr) const;
u8 addLevel(INodeDefManager *nodemgr, s8 add = 1);
/* /*
Serialization functions Serialization functions

@ -213,6 +213,7 @@ void ContentFeatures::reset()
liquid_alternative_source = ""; liquid_alternative_source = "";
liquid_viscosity = 0; liquid_viscosity = 0;
liquid_renewable = true; liquid_renewable = true;
freezemelt = "";
liquid_range = LIQUID_LEVEL_MAX+1; liquid_range = LIQUID_LEVEL_MAX+1;
drowning = true; drowning = true;
light_source = 0; light_source = 0;

@ -224,6 +224,8 @@ struct ContentFeatures
u8 liquid_viscosity; u8 liquid_viscosity;
// Is liquid renewable (new liquid source will be created between 2 existing) // Is liquid renewable (new liquid source will be created between 2 existing)
bool liquid_renewable; bool liquid_renewable;
// Ice for water, water for ice
std::string freezemelt;
// Number of flowing liquids surrounding source // Number of flowing liquids surrounding source
u8 liquid_range; u8 liquid_range;
bool drowning; bool drowning;

@ -397,6 +397,7 @@ ContentFeatures read_content_features(lua_State *L, int index)
f.leveled = getintfield_default(L, index, "leveled", f.leveled); f.leveled = getintfield_default(L, index, "leveled", f.leveled);
getboolfield(L, index, "liquid_renewable", f.liquid_renewable); getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
getstringfield(L, index, "freezemelt", f.freezemelt);
getboolfield(L, index, "drowning", f.drowning); getboolfield(L, index, "drowning", f.drowning);
// Amount of light the node emits // Amount of light the node emits
f.light_source = getintfield_default(L, index, f.light_source = getintfield_default(L, index,

@ -233,3 +233,20 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
scriptError("error: %s", lua_tostring(L, -1)); scriptError("error: %s", lua_tostring(L, -1));
} }
void ScriptApiNode::node_falling_update(v3s16 p)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "nodeupdate");
push_v3s16(L, p);
if(lua_pcall(L, 1, 0, 0))
scriptError("error: %s", lua_tostring(L, -1));
}
void ScriptApiNode::node_falling_update_single(v3s16 p)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "nodeupdate_single");
push_v3s16(L, p);
if(lua_pcall(L, 1, 0, 0))
scriptError("error: %s", lua_tostring(L, -1));
}

@ -49,6 +49,8 @@ public:
const std::string &formname, const std::string &formname,
const std::map<std::string, std::string> &fields, const std::map<std::string, std::string> &fields,
ServerActiveObject *sender); ServerActiveObject *sender);
void node_falling_update(v3s16 p);
void node_falling_update_single(v3s16 p);
public: public:
static struct EnumString es_DrawType[]; static struct EnumString es_DrawType[];
static struct EnumString es_ContentParamType[]; static struct EnumString es_ContentParamType[];

@ -263,6 +263,48 @@ int ModApiEnvMod::l_punch_node(lua_State *L)
return 1; return 1;
} }
// minetest.get_node_max_level(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_max_level(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
MapNode n = env->getMap().getNodeNoEx(pos);
lua_pushnumber(L, n.getMaxLevel(env->getGameDef()->ndef()));
return 1;
}
// minetest.get_node_level(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_level(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
MapNode n = env->getMap().getNodeNoEx(pos);
lua_pushnumber(L, n.getLevel(env->getGameDef()->ndef()));
return 1;
}
// minetest.add_node_level(pos, level)
// pos = {x=num, y=num, z=num}
// level: 0..8
int ModApiEnvMod::l_add_node_level(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
u8 level = 1;
if(lua_isnumber(L, 2))
level = lua_tonumber(L, 2);
MapNode n = env->getMap().getNodeNoEx(pos);
lua_pushnumber(L, n.addLevel(env->getGameDef()->ndef(), level));
env->setNode(pos, n);
return 1;
}
// minetest.get_meta(pos) // minetest.get_meta(pos)
int ModApiEnvMod::l_get_meta(lua_State *L) int ModApiEnvMod::l_get_meta(lua_State *L)
{ {
@ -820,6 +862,40 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
return 1; return 1;
} }
// minetest.transforming_liquid_add(pos)
int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
{
GET_ENV_PTR;
v3s16 p0 = read_v3s16(L, 1);
env->getMap().transforming_liquid_add(p0);
return 1;
}
// minetest.get_heat(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_heat(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
lua_pushnumber(L, env->getServerMap().getHeat(env, pos));
return 1;
}
// minetest.get_humidity(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_humidity(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
lua_pushnumber(L, env->getServerMap().getHumidity(env, pos));
return 1;
}
bool ModApiEnvMod::Initialize(lua_State *L,int top) bool ModApiEnvMod::Initialize(lua_State *L,int top)
{ {
@ -835,6 +911,9 @@ bool ModApiEnvMod::Initialize(lua_State *L,int top)
retval &= API_FCT(place_node); retval &= API_FCT(place_node);
retval &= API_FCT(dig_node); retval &= API_FCT(dig_node);
retval &= API_FCT(punch_node); retval &= API_FCT(punch_node);
retval &= API_FCT(get_node_max_level);
retval &= API_FCT(get_node_level);
retval &= API_FCT(add_node_level);
retval &= API_FCT(add_entity); retval &= API_FCT(add_entity);
retval &= API_FCT(get_meta); retval &= API_FCT(get_meta);
retval &= API_FCT(get_node_timer); retval &= API_FCT(get_node_timer);
@ -853,6 +932,9 @@ bool ModApiEnvMod::Initialize(lua_State *L,int top)
retval &= API_FCT(spawn_tree); retval &= API_FCT(spawn_tree);
retval &= API_FCT(find_path); retval &= API_FCT(find_path);
retval &= API_FCT(line_of_sight); retval &= API_FCT(line_of_sight);
retval &= API_FCT(transforming_liquid_add);
retval &= API_FCT(get_heat);
retval &= API_FCT(get_humidity);
return retval; return retval;
} }

@ -67,6 +67,19 @@ private:
// pos = {x=num, y=num, z=num} // pos = {x=num, y=num, z=num}
static int l_punch_node(lua_State *L); static int l_punch_node(lua_State *L);
// minetest.get_node_max_level(pos)
// pos = {x=num, y=num, z=num}
static int l_get_node_max_level(lua_State *L);
// minetest.get_node_level(pos)
// pos = {x=num, y=num, z=num}
static int l_get_node_level(lua_State *L);
// minetest.add_node_level(pos)
// pos = {x=num, y=num, z=num}
static int l_add_node_level(lua_State *L);
// minetest.get_meta(pos) // minetest.get_meta(pos)
static int l_get_meta(lua_State *L); static int l_get_meta(lua_State *L);
@ -136,6 +149,12 @@ private:
// max_jump, max_drop, algorithm) -> table containing path // max_jump, max_drop, algorithm) -> table containing path
static int l_find_path(lua_State *L); static int l_find_path(lua_State *L);
// minetest.transforming_liquid_add(pos)
static int l_transforming_liquid_add(lua_State *L);
static int l_get_heat(lua_State *L);
static int l_get_humidity(lua_State *L);
static struct EnumString es_MapgenObject[]; static struct EnumString es_MapgenObject[];
public: public:

@ -61,11 +61,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
23: new node metadata format 23: new node metadata format
24: 16-bit node ids and node timers (never released as stable) 24: 16-bit node ids and node timers (never released as stable)
25: Improved node timer format 25: Improved node timer format
26: MapBlocks contain heat and humidity
*/ */
// This represents an uninitialized or invalid format // This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255 #define SER_FMT_VER_INVALID 255
// Highest supported serialization version // Highest supported serialization version
#define SER_FMT_VER_HIGHEST 25 #define SER_FMT_VER_HIGHEST 26
// Lowest supported serialization version // Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0 #define SER_FMT_VER_LOWEST 0

@ -349,5 +349,12 @@ inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxi
} }
} }
inline float cycle_shift(float value, float by = 0, float max = 1)
{
if (value + by < 0) return max + by + value;
if (value + by > max) return value + by - max;
return value + by;
}
#endif #endif