Weather support
authorproller <proller@github.com>
Sat, 27 Jul 2013 18:34:30 +0000 (22:34 +0400)
committerproller <proller@github.com>
Sat, 27 Jul 2013 19:21:48 +0000 (23:21 +0400)
23 files changed:
builtin/falling.lua
doc/lua_api.txt
minetest.conf.example
src/content_abm.cpp
src/defaultsettings.cpp
src/emerge.cpp
src/environment.h
src/game.cpp
src/map.cpp
src/map.h
src/mapblock.cpp
src/mapblock.h
src/mapnode.cpp
src/mapnode.h
src/nodedef.cpp
src/nodedef.h
src/script/common/c_content.cpp
src/script/cpp_api/s_node.cpp
src/script/cpp_api/s_node.h
src/script/lua_api/l_env.cpp
src/script/lua_api/l_env.h
src/serialization.h
src/util/numeric.h

index 73087803f53d52ecb21114c7cb7f2526498d1c2c..91e732a4b1c07df160b84c8dd28b408e0db53473 100644 (file)
@@ -14,11 +14,11 @@ minetest.register_entity("__builtin:falling_node", {
                visual_size = {x=0.667, y=0.667},
        },
 
-       nodename = "",
+       node = {},
 
-       set_node = function(self, nodename)
-               self.nodename = nodename
-               local stack = ItemStack(nodename)
+       set_node = function(self, node)
+               self.node = node
+               local stack = ItemStack(node.name)
                local itemtable = stack:to_table()
                local itemname = nil
                if itemtable then
@@ -32,20 +32,19 @@ minetest.register_entity("__builtin:falling_node", {
                end
                prop = {
                        is_visible = true,
-                       textures = {nodename},
+                       textures = {node.name},
                }
                self.object:set_properties(prop)
        end,
 
        get_staticdata = function(self)
-               return self.nodename
+               return self.node.name
        end,
 
        on_activate = function(self, staticdata)
-               self.nodename = staticdata
                self.object:set_armor_groups({immortal=1})
                --self.object:setacceleration({x=0, y=-10, z=0})
-               self:set_node(self.nodename)
+               self:set_node({name=staticdata})
        end,
 
        on_step = function(self, dtime)
@@ -57,8 +56,10 @@ minetest.register_entity("__builtin:falling_node", {
                local bcn = minetest.get_node(bcp)
                -- Note: walkable is in the node definition, not in item groups
                if minetest.registered_nodes[bcn.name] and
-                               minetest.registered_nodes[bcn.name].walkable then
-                       if minetest.registered_nodes[bcn.name].buildable_to then
+                               minetest.registered_nodes[bcn.name].walkable or
+                               (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)
                                return
                        end
@@ -83,7 +84,7 @@ minetest.register_entity("__builtin:falling_node", {
                                end
                        end
                        -- Create node and remove entity
-                       minetest.add_node(np, {name=self.nodename})
+                       minetest.add_node(np, self.node)
                        self.object:remove()
                        nodeupdate(np)
                else
@@ -92,9 +93,9 @@ minetest.register_entity("__builtin:falling_node", {
        end
 })
 
-function spawn_falling_node(p, nodename)
+function spawn_falling_node(p, node)
        obj = minetest.add_entity(p, "__builtin:falling_node")
-       obj:get_luaentity():set_node(nodename)
+       obj:get_luaentity():set_node(node)
 end
 
 function drop_attached_node(p)
@@ -150,13 +151,14 @@ function nodeupdate_single(p, delay)
                n_bottom = minetest.get_node(p_bottom)
                -- Note: walkable is in the node definition, not in item groups
                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 
                                        minetest.registered_nodes[n_bottom.name].buildable_to) then
                        if delay then
                                minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
                        else
                                minetest.remove_node(p)
-                               spawn_falling_node(p, n.name)
+                               spawn_falling_node(p, n)
                                nodeupdate(p)
                        end
                end
@@ -170,7 +172,7 @@ function nodeupdate_single(p, delay)
        end
 end
 
-function nodeupdate(p)
+function nodeupdate(p, delay)
        -- Round p to prevent falling entities to get stuck
        p.x = math.floor(p.x+0.5)
        p.y = math.floor(p.y+0.5)
@@ -179,7 +181,7 @@ function nodeupdate(p)
        for x = -1,1 do
        for y = -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
index 9973a790b6ff067e8c970e8c2ab63a043279c8be..cf1ab7f052ef74dd7310db1128b93c076148bfb1 100644 (file)
@@ -322,6 +322,8 @@ param2 is reserved for the engine when any of these are used:
     facedir modulo 4 = axisdir
     0 = y+    1 = z+    2 = z-    3 = x+    4 = x-    5 = y-
     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".
 
@@ -353,7 +355,7 @@ Node selection boxes are defined using "node boxes"
 
 The "nodebox" node drawtype allows defining visual of nodes consisting of
 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
   changed in the future.
 
@@ -381,6 +383,8 @@ A box is defined as:
 A box of a regular node would look like:
   {-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
 ---------------
 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
 minetest.spawn_tree (pos, {treedef})
 ^ 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:
 minetest.get_inventory(location) -> InvRef
@@ -1965,6 +1981,8 @@ Node definition (register_node)
     liquid_alternative_source = "", -- Source version of flowing liquid
     liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7)
     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)
     drowning = true, -- Player will drown in these 
     two or more sources nearly?
index 5fad31be9b1e0396ab66e7d5ca9190504b788674..4c049f87db3c4144d35c7d845b76cad357a69b01 100644 (file)
 #liquid_fast_flood = 1
 # Underground water and lava springs, its infnity sources if liquid_finite enabled
 #underground_springs = 1
+# Enable weather (cold-hot, water freeze-melt). use only with liquid_finite=1
+#weather = false
 # Enable nice leaves; disable for speed
 #new_style_leaves = true
 # Enable smooth lighting with simple ambient occlusion;
 # Interval of sending time of day to clients
 #time_send_interval = 5
 # 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
 # Interval of saving important changes in the world
 #server_map_save_interval = 5.3
index 6adcbf708ef94c328957654216c2fb6fb3da6526..110ac1eea919d641591be89ff1635c4266f887bf 100644 (file)
@@ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "treegen.h" // For treegen::make_tree
 #include "main.h" // for g_settings
 #include "map.h"
+#include "cpp_api/scriptapi.h"
+#include "log.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -166,86 +168,220 @@ public:
        }
 };
 
-class LiquidFlowABM : public ActiveBlockModifier
-{
-private:
-       std::set<std::string> contents;
+class LiquidFlowABM : public ActiveBlockModifier {
+       private:
+               std::set<std::string> contents;
 
-public:
-       LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) 
-       {
-               std::set<content_t> liquids;
-               nodemgr->getIds("group:liquid", liquids);
-               for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
-                       contents.insert(nodemgr->get(*k).liquid_alternative_flowing);
-               
-       }
-       virtual std::set<std::string> getTriggerContents()
-       {
-               return contents;
-       }
-       virtual float getTriggerInterval()
-       { return 10.0; }
-       virtual u32 getTriggerChance()
-       { return 10; }
-       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
-       {
-               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);
-       }
+       public:
+               LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
+                       std::set<content_t> liquids;
+                       nodemgr->getIds("group:liquid", liquids);
+                       for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
+                               contents.insert(nodemgr->get(*k).liquid_alternative_flowing);
+               }
+               virtual std::set<std::string> getTriggerContents() {
+                       return contents;
+               }
+               virtual float getTriggerInterval()
+               { return 10.0; }
+               virtual u32 getTriggerChance()
+               { return 10; }
+               virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
+                       ServerMap *map = &env->getServerMap();
+                       if (map->transforming_liquid_size() > 500)
+                               return;
+                       map->transforming_liquid_add(p);
+               }
 };
 
-class LiquidDropABM : public ActiveBlockModifier
-{
-private:
-       std::set<std::string> contents;
+class LiquidDropABM : public ActiveBlockModifier {
+       private:
+               std::set<std::string> contents;
 
-public:
-       LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) 
-       {
-               std::set<content_t> liquids;
-               nodemgr->getIds("group:liquid", liquids);
-               for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
-                       contents.insert(nodemgr->get(*k).liquid_alternative_source);
-       }
-       virtual std::set<std::string> getTriggerContents()
-       { return contents; }
-       virtual std::set<std::string> getRequiredNeighbors()
-       {
-               std::set<std::string> neighbors;
-               neighbors.insert("mapgen_air");
-               return neighbors; 
-       }
-       virtual float getTriggerInterval()
-       { return 20.0; }
-       virtual u32 getTriggerChance()
-       { return 10; }
-       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) 
-       {
-               ServerMap *map = &env->getServerMap();
-               if (map->transforming_liquid_size() > 500)
-                       return;
-               if (       map->getNodeNoEx(p - v3s16(0,  1, 0 )).getContent() != CONTENT_AIR  // below
-                       && map->getNodeNoEx(p - v3s16(1,  0, 0 )).getContent() != CONTENT_AIR  // right
-                       && map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent() != CONTENT_AIR  // left
-                       && map->getNodeNoEx(p - v3s16(0,  0, 1 )).getContent() != CONTENT_AIR  // back 
-                       && map->getNodeNoEx(p - v3s16(0,  0, -1)).getContent() != CONTENT_AIR  // front
-               )
-                       return;
-               map->transforming_liquid_add(p);
-       }
+       public:
+               LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) {
+                       std::set<content_t> liquids;
+                       nodemgr->getIds("group:liquid", liquids);
+                       for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
+                               contents.insert(nodemgr->get(*k).liquid_alternative_source);
+               }
+               virtual std::set<std::string> getTriggerContents()
+               { return contents; }
+               virtual std::set<std::string> getRequiredNeighbors() {
+                       std::set<std::string> neighbors;
+                       neighbors.insert("mapgen_air");
+                       return neighbors;
+               }
+               virtual float getTriggerInterval()
+               { return 20.0; }
+               virtual u32 getTriggerChance()
+               { return 10; }
+               virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
+                       ServerMap *map = &env->getServerMap();
+                       if (map->transforming_liquid_size() > 500)
+                               return;
+                       if (   map->getNodeNoEx(p - v3s16(0,  1, 0 )).getContent() != CONTENT_AIR  // below
+                           && map->getNodeNoEx(p - v3s16(1,  0, 0 )).getContent() != CONTENT_AIR  // right
+                           && map->getNodeNoEx(p - v3s16(-1, 0, 0 )).getContent() != CONTENT_AIR  // left
+                           && map->getNodeNoEx(p - v3s16(0,  0, 1 )).getContent() != CONTENT_AIR  // back
+                           && map->getNodeNoEx(p - v3s16(0,  0, -1)).getContent() != CONTENT_AIR  // front
+                          )
+                               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 RemoveGrassABM());
        env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef));
        if (g_settings->getBool("liquid_finite")) {
                env->addActiveBlockModifier(new LiquidFlowABM(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));
+               }
        }
 }
index a537732db002bbd2e142e28d062819eb0cd312ad..326e11b8fde0dd4688df09f33fdf15fcd1a429ae 100644 (file)
@@ -178,6 +178,7 @@ void set_default_settings(Settings *settings)
        settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
        settings->setDefault("time_send_interval", "5");
        settings->setDefault("time_speed", "72");
+       settings->setDefault("year_days", "30");
        settings->setDefault("server_unload_unused_data_timeout", "29");
        settings->setDefault("server_map_save_interval", "5.3");
        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_fast_flood", "1");
        settings->setDefault("underground_springs", "1");
+       settings->setDefault("weather", "false");
 
        //mapgen stuff
        settings->setDefault("mg_name", "v6");
index c0560ba3bd9b2d11711e5681c0382591d4d85a3a..f97763718eacebb79d86f3653de7494c9b25a7a4 100644 (file)
@@ -488,6 +488,14 @@ void *EmergeThread::Thread() {
                if (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
                for (std::map<u16, RemoteClient*>::iterator
                         i = m_server->m_clients.begin();
index e175d70d9d2c81ec0c4a2ad43ed683b1c0559b42..8fc5611e45a4ca83cbd42d998c0bc38fd9b6d8ec 100644 (file)
@@ -303,6 +303,7 @@ public:
        //check if there's a line of sight between two positions
        bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0);
 
+       u32 getGameTime() { return m_game_time; }
 private:
 
        /*
index 3f14f09d42403ac4c6df666e9898d70ac312e66a..0c7d15d0cde86f8a50e9333fd94d4f04ea68f095 100644 (file)
@@ -2455,6 +2455,7 @@ void the_game(
                camera.step(dtime);
 
                v3f player_position = player->getPosition();
+               v3s16 pos_i = floatToInt(player_position, BS);
                v3f camera_position = camera.getPosition();
                v3f camera_direction = camera.getDirection();
                f32 camera_fov = camera.getFovMax();
@@ -3034,7 +3035,9 @@ void the_game(
                                <<", "<<(player_position.Y/BS)
                                <<", "<<(player_position.Z/BS)
                                <<") (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->setVisible(true);
index 11f5d6483092002c4f2c2a6cb50f625a412eb361..fa52a2f52abbaf0475b6f63084d0520eb190d747 100644 (file)
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "emerge.h"
 #include "mapgen_v6.h"
 #include "mapgen_indev.h"
+#include "biome.h"
 
 #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
                liquid (=water node was added) to transform queue.
+               note: todo: for liquid_finite enough to add only self node
        */
        v3s16 dirs[7] = {
                v3s16(0,0,0), // self
@@ -1278,6 +1280,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
        /*
                Add neighboring liquid nodes and this node to transform queue.
                (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(0,0,1), // back
@@ -2364,6 +2367,26 @@ void Map::removeNodeTimer(v3s16 p)
        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
 */
@@ -3863,7 +3886,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
                                <<" (SerializationError). "
                                <<"what()="<<e.what()
                                <<std::endl;
-                               //" Ignoring. A new one will be generated.
+                               // Ignoring. A new one will be generated.
                assert(0);
 
                // TODO: Backup file; name is in fullpath.
@@ -4039,6 +4062,63 @@ void ServerMap::PrintInfo(std::ostream &out)
        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
 */
index bccadcec52a8e0847dc3165c4f5ec8a41180aa98..c1fd361a77dc9886119df1c4c523cc9a3016f6bc 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "modifiedstate.h"
 #include "util/container.h"
 #include "nodetimer.h"
+#include "environment.h"
 
 extern "C" {
        #include "sqlite3.h"
@@ -336,6 +337,9 @@ public:
        void transforming_liquid_add(v3s16 p);
        s32 transforming_liquid_size();
 
+       virtual s16 getHeat(v3s16 p);
+       virtual s16 getHumidity(v3s16 p);
+
 protected:
        friend class LuaVoxelManip;
 
@@ -483,6 +487,10 @@ public:
 
        // Parameters fed to the Mapgen
        MapgenParams *m_mgparams;
+
+       virtual s16 getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block = NULL);
+       virtual s16 getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block = NULL);
+
 private:
        // Seed used for all kinds of randomness in generation
        u64 m_seed;
index dd95ab77f8c3f60c93542adee12e3e2f056ddcb9..56d4416a4168767deef026a1f6a4188be24f95bf 100644 (file)
@@ -58,7 +58,11 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
                m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
                m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
                m_usage_timer(0),
-               m_refcount(0)
+               m_refcount(0),
+               heat_time(0),
+               heat(0),
+               humidity_time(0),
+               humidity(0)
 {
        data = NULL;
        if(dummy == false)
@@ -632,6 +636,11 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
                        // Node timers
                        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);
                        m_node_timers.deSerialize(is, version);
                }
+       } else {
+               if(version >= 26){
+                       heat = readF1000(is);
+                       humidity = readF1000(is);
+               }
        }
                
        TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
index 05bb944a68727b7dcafbbb051d63575168cc5adc..0168411157b30ee691c7094fe3916e8f2ddff4f8 100644 (file)
@@ -518,6 +518,11 @@ public:
        NodeTimerList m_node_timers;
        StaticObjectList m_static_objects;
        
+       s16 heat;
+       u32 heat_time;
+       s16 humidity;
+       u32 humidity_time;
+
 private:
        /*
                Private member variables
index 33644cf5cc84f671d4df1f9385e5c5ceb47b9a22..4707978cbc2a4803fa651060404277648f296511 100644 (file)
@@ -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 "serialization.h" // For ser_ver_supported
 #include "util/serialize.h"
+#include "log.h"
 #include <string>
 #include <sstream>
 
@@ -359,9 +360,23 @@ std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
        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
 {
        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)
                return LIQUID_LEVEL_SOURCE;
        if (f.param_type_2 == CPT2_FLOWINGLIQUID)
@@ -377,6 +392,37 @@ u8 MapNode::getLevel(INodeDefManager *nodemgr) const
        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)
 {
        if(!ser_ver_supported(version))
index 74b079c6d5e46557df8e180f5224707f788599b2..fcff1707a898516fcd5ea86676945ef3219828a1 100644 (file)
@@ -227,7 +227,9 @@ struct MapNode
        std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
 
        /* Liquid helpers */
+       u8 getMaxLevel(INodeDefManager *nodemgr) const;
        u8 getLevel(INodeDefManager *nodemgr) const;
+       u8 addLevel(INodeDefManager *nodemgr, s8 add = 1);
 
        /*
                Serialization functions
index a4d0368830007d92765cadc8f0ee5f95f25fdb4e..96dca730bf7785e1463ef43bbb627fbc76aa7b5f 100644 (file)
@@ -213,6 +213,7 @@ void ContentFeatures::reset()
        liquid_alternative_source = "";
        liquid_viscosity = 0;
        liquid_renewable = true;
+       freezemelt = "";
        liquid_range = LIQUID_LEVEL_MAX+1;
        drowning = true;
        light_source = 0;
index 3a8210304ec4a92d79bc65a47665da95377ce50a..067861e62314de54db23eeb791e8080db2056926 100644 (file)
@@ -224,6 +224,8 @@ struct ContentFeatures
        u8 liquid_viscosity;
        // Is liquid renewable (new liquid source will be created between 2 existing)
        bool liquid_renewable;
+       // Ice for water, water for ice
+       std::string freezemelt;
        // Number of flowing liquids surrounding source
        u8 liquid_range;
        bool drowning;
index dcffabb8b7b132ee49002b2356a8a2fbddbae516..d97264009ccfcde28496577a9756511d0ea16301 100644 (file)
@@ -397,6 +397,7 @@ ContentFeatures read_content_features(lua_State *L, int index)
        f.leveled = getintfield_default(L, index, "leveled", f.leveled);
 
        getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
+       getstringfield(L, index, "freezemelt", f.freezemelt);
        getboolfield(L, index, "drowning", f.drowning);
        // Amount of light the node emits
        f.light_source = getintfield_default(L, index,
index 49a825cacb690b40c583ef7905800e57500ea0ba..d0b0583c03eb71d85f7c2c072e45e885824e8fd7 100644 (file)
@@ -233,3 +233,20 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
                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));
+}
index a8c9b3a79ae607fb29dc96cf7b8eee5fbbbf179d..517b4b04e651e479ad9148681dddb15c16c7cf81 100644 (file)
@@ -49,6 +49,8 @@ public:
                        const std::string &formname,
                        const std::map<std::string, std::string> &fields,
                        ServerActiveObject *sender);
+       void node_falling_update(v3s16 p);
+       void node_falling_update_single(v3s16 p);
 public:
        static struct EnumString es_DrawType[];
        static struct EnumString es_ContentParamType[];
index 1cbf34ab95f54b70a0c25f3f224b0bd98048b926..52ea557175d6135b221dcd2a55b43cbf938ee241 100644 (file)
@@ -263,6 +263,48 @@ int ModApiEnvMod::l_punch_node(lua_State *L)
        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)
 int ModApiEnvMod::l_get_meta(lua_State *L)
 {
@@ -820,6 +862,40 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
        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)
 {
 
@@ -835,6 +911,9 @@ bool ModApiEnvMod::Initialize(lua_State *L,int top)
        retval &= API_FCT(place_node);
        retval &= API_FCT(dig_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(get_meta);
        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(find_path);
        retval &= API_FCT(line_of_sight);
+       retval &= API_FCT(transforming_liquid_add);
+       retval &= API_FCT(get_heat);
+       retval &= API_FCT(get_humidity);
 
        return retval;
 }
index 713cfa69f1095446fa6b69112aca126d30080795..eaef16180a78bb527e5ed736079674efdca840e0 100644 (file)
@@ -67,6 +67,19 @@ private:
        // pos = {x=num, y=num, z=num}
        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)
        static int l_get_meta(lua_State *L);
 
@@ -135,6 +148,12 @@ private:
        // minetest.find_path(pos1, pos2, searchdistance,
        //     max_jump, max_drop, algorithm) -> table containing path
        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[];
        
index defead31e3309c3052ebf212df003a5c347d4d79..807b68e9d5693476f016b227cbd468408ab940a8 100644 (file)
@@ -61,11 +61,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        23: new node metadata format
        24: 16-bit node ids and node timers (never released as stable)
        25: Improved node timer format
+       26: MapBlocks contain heat and humidity
 */
 // This represents an uninitialized or invalid format
 #define SER_FMT_VER_INVALID 255
 // Highest supported serialization version
-#define SER_FMT_VER_HIGHEST 25
+#define SER_FMT_VER_HIGHEST 26
 // Lowest supported serialization version
 #define SER_FMT_VER_LOWEST 0
 
index 3e82997bd98dca58bbafa715b4ddf4cb83a32159..076a08efc9d1bde6a52968af394744876e80b76e 100644 (file)
@@ -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