aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/falling.lua34
-rw-r--r--doc/lua_api.txt20
-rw-r--r--minetest.conf.example6
-rw-r--r--src/content_abm.cpp274
-rw-r--r--src/defaultsettings.cpp2
-rw-r--r--src/emerge.cpp8
-rw-r--r--src/environment.h1
-rw-r--r--src/game.cpp5
-rw-r--r--src/map.cpp82
-rw-r--r--src/map.h8
-rw-r--r--src/mapblock.cpp16
-rw-r--r--src/mapblock.h5
-rw-r--r--src/mapnode.cpp46
-rw-r--r--src/mapnode.h2
-rw-r--r--src/nodedef.cpp1
-rw-r--r--src/nodedef.h2
-rw-r--r--src/script/common/c_content.cpp1
-rw-r--r--src/script/cpp_api/s_node.cpp17
-rw-r--r--src/script/cpp_api/s_node.h2
-rw-r--r--src/script/lua_api/l_env.cpp82
-rw-r--r--src/script/lua_api/l_env.h19
-rw-r--r--src/serialization.h3
-rw-r--r--src/util/numeric.h7
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<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));
+ }
}
}
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<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();
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="<<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);
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()="<<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
*/
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)"<<std::endl);
m_node_timers.deSerialize(is, version);
}
+ } else {
+ if(version >= 26){
+ heat = readF1000(is);
+ humidity = readF1000(is);
+ }
}
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
diff --git a/src/mapblock.h b/src/mapblock.h
index 05bb944a6..016841115 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -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
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index 33644cf5c..4707978cb 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -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))
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<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
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<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[];
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