From 3aedfac9685c2d9ae8bac5a5b7e72e527f22c08d Mon Sep 17 00:00:00 2001 From: proller Date: Sat, 27 Jul 2013 22:34:30 +0400 Subject: Weather support --- 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 + 20 files changed, 510 insertions(+), 73 deletions(-) (limited to 'src') 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 -- cgit v1.2.3