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
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)
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
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
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)
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
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)
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
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".
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.
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.
^ 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
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?
#liquid_fast_flood = 1
# Underground water and lava springs, its infnity sources if liquid_finite enabled
#underground_springs = 1
+# Enable weather (cold-hot, water freeze-melt). use only with liquid_finite=1
+#weather = false
# Enable nice leaves; disable for speed
#new_style_leaves = true
# Enable smooth lighting with simple ambient occlusion;
# Interval of sending time of day to clients
#time_send_interval = 5
# Length of day/night cycle. 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged
-#time_speed = 96
+#time_speed = 72
+# Length of year in days for seasons change. With default time_speed 365 days = 5 real days for year. 30 days = 10 real hours
+#year_days = 30
#server_unload_unused_data_timeout = 29
# Interval of saving important changes in the world
#server_map_save_interval = 5.3
#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<<")"
}
};
-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));
+ }
}
}
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");
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");
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();
//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:
/*
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();
<<", "<<(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);
#include "emerge.h"
#include "mapgen_v6.h"
#include "mapgen_indev.h"
+#include "biome.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
/*
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
/*
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
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
*/
<<" (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.
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
*/
#include "modifiedstate.h"
#include "util/container.h"
#include "nodetimer.h"
+#include "environment.h"
extern "C" {
#include "sqlite3.h"
void transforming_liquid_add(v3s16 p);
s32 transforming_liquid_size();
+ virtual s16 getHeat(v3s16 p);
+ virtual s16 getHumidity(v3s16 p);
+
protected:
friend class LuaVoxelManip;
// 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;
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)
// Node timers
m_node_timers.serialize(os, version);
}
+ } else {
+ if(version >= 26){
+ writeF1000(os, heat);
+ writeF1000(os, humidity);
+ }
}
}
<<": 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())
NodeTimerList m_node_timers;
StaticObjectList m_static_objects;
+ s16 heat;
+ u32 heat_time;
+ s16 humidity;
+ u32 humidity_time;
+
private:
/*
Private member variables
#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>
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)
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))
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
liquid_alternative_source = "";
liquid_viscosity = 0;
liquid_renewable = true;
+ freezemelt = "";
liquid_range = LIQUID_LEVEL_MAX+1;
drowning = true;
light_source = 0;
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;
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,
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));
+}
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[];
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)
{
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)
{
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);
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;
}
// 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);
// 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[];
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
}
}
+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