From 3aedfac9685c2d9ae8bac5a5b7e72e527f22c08d Mon Sep 17 00:00:00 2001 From: proller Date: Sat, 27 Jul 2013 22:34:30 +0400 Subject: [PATCH] Weather support --- builtin/falling.lua | 34 ++-- doc/lua_api.txt | 20 ++- minetest.conf.example | 6 +- src/content_abm.cpp | 274 ++++++++++++++++++++++++-------- src/defaultsettings.cpp | 2 + src/emerge.cpp | 8 + src/environment.h | 1 + src/game.cpp | 5 +- src/map.cpp | 82 +++++++++- src/map.h | 8 + src/mapblock.cpp | 16 +- src/mapblock.h | 5 + src/mapnode.cpp | 46 ++++++ src/mapnode.h | 2 + src/nodedef.cpp | 1 + src/nodedef.h | 2 + src/script/common/c_content.cpp | 1 + src/script/cpp_api/s_node.cpp | 17 ++ src/script/cpp_api/s_node.h | 2 + src/script/lua_api/l_env.cpp | 82 ++++++++++ src/script/lua_api/l_env.h | 19 +++ src/serialization.h | 3 +- src/util/numeric.h | 7 + 23 files changed, 552 insertions(+), 91 deletions(-) diff --git a/builtin/falling.lua b/builtin/falling.lua index 73087803f..91e732a4b 100644 --- a/builtin/falling.lua +++ b/builtin/falling.lua @@ -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 diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9973a790b..cf1ab7f05 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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? diff --git a/minetest.conf.example b/minetest.conf.example index 5fad31be9..4c049f87d 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -107,6 +107,8 @@ #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; @@ -268,7 +270,9 @@ # 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 diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 6adcbf708..110ac1eea 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -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 contents; +class LiquidFlowABM : public ActiveBlockModifier { + private: + std::set contents; -public: - LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) - { - std::set liquids; - nodemgr->getIds("group:liquid", liquids); - for(std::set::const_iterator k = liquids.begin(); k != liquids.end(); k++) - contents.insert(nodemgr->get(*k).liquid_alternative_flowing); - - } - virtual std::set 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 liquids; + nodemgr->getIds("group:liquid", liquids); + for(std::set::const_iterator k = liquids.begin(); k != liquids.end(); k++) + contents.insert(nodemgr->get(*k).liquid_alternative_flowing); + } + virtual std::set 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 contents; +class LiquidDropABM : public ActiveBlockModifier { + private: + std::set contents; -public: - LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) - { - std::set liquids; - nodemgr->getIds("group:liquid", liquids); - for(std::set::const_iterator k = liquids.begin(); k != liquids.end(); k++) - contents.insert(nodemgr->get(*k).liquid_alternative_source); - } - virtual std::set getTriggerContents() - { return contents; } - virtual std::set getRequiredNeighbors() - { - std::set 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 liquids; + nodemgr->getIds("group:liquid", liquids); + for(std::set::const_iterator k = liquids.begin(); k != liquids.end(); k++) + contents.insert(nodemgr->get(*k).liquid_alternative_source); + } + virtual std::set getTriggerContents() + { return contents; } + virtual std::set getRequiredNeighbors() { + std::set 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 getTriggerContents() { + std::set s; + s.insert("group:freezes"); + return s; + } + virtual std::set getRequiredNeighbors() { + std::set 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 getTriggerContents() { + std::set s; + s.insert("group:melts"); + return s; + } + virtual std::set getRequiredNeighbors() { + std::set 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 getTriggerContents() { + std::set s; + s.insert("group:melts"); + return s; + } + virtual std::set getRequiredNeighbors() { + std::set 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 getRequiredNeighbors() { + std::set 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)); + } } } diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index a537732db..326e11b8f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -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"); diff --git a/src/emerge.cpp b/src/emerge.cpp index c0560ba3b..f97763718 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -488,6 +488,14 @@ void *EmergeThread::Thread() { if (block) modified_blocks[p] = block; + // Update weather data in mapblock + for(std::map::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::iterator i = m_server->m_clients.begin(); diff --git a/src/environment.h b/src/environment.h index e175d70d9..8fc5611e4 100644 --- a/src/environment.h +++ b/src/environment.h @@ -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: /* diff --git a/src/game.cpp b/src/game.cpp index 3f14f09d4..0c7d15d0c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -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="<setText(narrow_to_wide(os.str()).c_str()); guitext2->setVisible(true); diff --git a/src/map.cpp b/src/map.cpp index 11f5d6483..fa52a2f52 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -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()="<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 */ diff --git a/src/map.h b/src/map.h index bccadcec5..c1fd361a7 100644 --- 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; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index dd95ab77f..56d4416a4 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -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)"<= 26){ + heat = readF1000(is); + humidity = readF1000(is); + } } TRACESTREAM(<<"MapBlock::deSerialize "< #include @@ -359,9 +360,23 @@ std::vector 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)) diff --git a/src/mapnode.h b/src/mapnode.h index 74b079c6d..fcff1707a 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -227,7 +227,9 @@ struct MapNode std::vector 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 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index a4d036883..96dca730b 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -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; diff --git a/src/nodedef.h b/src/nodedef.h index 3a8210304..067861e62 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -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; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index dcffabb8b..d97264009 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -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, diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 49a825cac..d0b0583c0 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -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)); +} diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h index a8c9b3a79..517b4b04e 100644 --- a/src/script/cpp_api/s_node.h +++ b/src/script/cpp_api/s_node.h @@ -49,6 +49,8 @@ public: const std::string &formname, const std::map &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[]; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 1cbf34ab9..52ea55717 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -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; } diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 713cfa69f..eaef16180 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -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[]; diff --git a/src/serialization.h b/src/serialization.h index defead31e..807b68e9d 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -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 diff --git a/src/util/numeric.h b/src/util/numeric.h index 3e82997bd..076a08efc 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -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