Weather support

pull/790/merge
proller 2013-07-27 22:34:30 +04:00
parent e65d8ad655
commit 3aedfac968
23 changed files with 552 additions and 91 deletions

View File

@ -14,11 +14,11 @@ minetest.register_entity("__builtin:falling_node", {
visual_size = {x=0.667, y=0.667},
},
nodename = "",
node = {},
set_node = function(self, nodename)
self.nodename = nodename
local stack = ItemStack(nodename)
set_node = function(self, node)
self.node = node
local stack = ItemStack(node.name)
local itemtable = stack:to_table()
local itemname = nil
if itemtable then
@ -32,20 +32,19 @@ minetest.register_entity("__builtin:falling_node", {
end
prop = {
is_visible = true,
textures = {nodename},
textures = {node.name},
}
self.object:set_properties(prop)
end,
get_staticdata = function(self)
return self.nodename
return self.node.name
end,
on_activate = function(self, staticdata)
self.nodename = staticdata
self.object:set_armor_groups({immortal=1})
--self.object:setacceleration({x=0, y=-10, z=0})
self:set_node(self.nodename)
self:set_node({name=staticdata})
end,
on_step = function(self, dtime)
@ -57,8 +56,10 @@ minetest.register_entity("__builtin:falling_node", {
local bcn = minetest.get_node(bcp)
-- Note: walkable is in the node definition, not in item groups
if minetest.registered_nodes[bcn.name] and
minetest.registered_nodes[bcn.name].walkable then
if minetest.registered_nodes[bcn.name].buildable_to then
minetest.registered_nodes[bcn.name].walkable or
(minetest.get_node_group(self.node.name, "float") ~= 0 and minetest.registered_nodes[bcn.name].liquidtype ~= "none")
then
if minetest.registered_nodes[bcn.name].buildable_to and (minetest.get_node_group(self.node.name, "float") == 0 or minetest.registered_nodes[bcn.name].liquidtype == "none") then
minetest.remove_node(bcp)
return
end
@ -83,7 +84,7 @@ minetest.register_entity("__builtin:falling_node", {
end
end
-- Create node and remove entity
minetest.add_node(np, {name=self.nodename})
minetest.add_node(np, self.node)
self.object:remove()
nodeupdate(np)
else
@ -92,9 +93,9 @@ minetest.register_entity("__builtin:falling_node", {
end
})
function spawn_falling_node(p, nodename)
function spawn_falling_node(p, node)
obj = minetest.add_entity(p, "__builtin:falling_node")
obj:get_luaentity():set_node(nodename)
obj:get_luaentity():set_node(node)
end
function drop_attached_node(p)
@ -150,13 +151,14 @@ function nodeupdate_single(p, delay)
n_bottom = minetest.get_node(p_bottom)
-- Note: walkable is in the node definition, not in item groups
if minetest.registered_nodes[n_bottom.name] and
(minetest.get_node_group(n.name, "float") == 0 or minetest.registered_nodes[n_bottom.name].liquidtype == "none") and
(not minetest.registered_nodes[n_bottom.name].walkable or
minetest.registered_nodes[n_bottom.name].buildable_to) then
if delay then
minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
else
minetest.remove_node(p)
spawn_falling_node(p, n.name)
spawn_falling_node(p, n)
nodeupdate(p)
end
end
@ -170,7 +172,7 @@ function nodeupdate_single(p, delay)
end
end
function nodeupdate(p)
function nodeupdate(p, delay)
-- Round p to prevent falling entities to get stuck
p.x = math.floor(p.x+0.5)
p.y = math.floor(p.y+0.5)
@ -179,7 +181,7 @@ function nodeupdate(p)
for x = -1,1 do
for y = -1,1 do
for z = -1,1 do
nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, not (x==0 and y==0 and z==0))
nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0))
end
end
end

View File

@ -322,6 +322,8 @@ param2 is reserved for the engine when any of these are used:
facedir modulo 4 = axisdir
0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y-
facedir's two less significant bits are rotation around the axis
paramtype2 == "leveled"
^ The drawn node level is read from param2, like flowingliquid
Nodes can also contain extra data. See "Node Metadata".
@ -353,7 +355,7 @@ Node selection boxes are defined using "node boxes"
The "nodebox" node drawtype allows defining visual of nodes consisting of
arbitrary number of boxes. It allows defining stuff like stairs. Only the
"fixed" box type is supported for these.
"fixed" and "leveled" box type is supported for these.
^ Please note that this is still experimental, and may be incompatibly
changed in the future.
@ -381,6 +383,8 @@ A box is defined as:
A box of a regular node would look like:
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
type = "leveled" is same as "fixed", but y2 will be automaticaly setted to level from param2
Ore types
---------------
These tell in what manner the ore is generated.
@ -1258,6 +1262,18 @@ minetest.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm)
^ algorithm: A*_noprefetch(default), A*, Dijkstra
minetest.spawn_tree (pos, {treedef})
^ spawns L-System tree at given pos with definition in treedef table
minetest.transforming_liquid_add(pos)
^ add node to liquid update queue
minetest.get_node_max_level(pos)
^ get max available level for leveled node
minetest.get_node_level(pos)
^ get level of leveled node (water, snow)
minetest.add_node_level(pos, level)
^ increase level of leveled node by level, default level = 1, if totallevel > maxlevel returns rest (total-max). can be negative for decreasing
minetest.get_heat(pos)
^ heat at pos
minetest.get_humidity(pos)
^ humidity at pos
Inventory:
minetest.get_inventory(location) -> InvRef
@ -1965,6 +1981,8 @@ Node definition (register_node)
liquid_alternative_source = "", -- Source version of flowing liquid
liquid_viscosity = 0, -- Higher viscosity = slower flow (max. 7)
liquid_renewable = true, -- Can new liquid source be created by placing
freezemelt = "", -- water for snow/ice, ice/snow for water
leveled = 0, -- Block contain level in param2. value - default level, used for snow. Dont forget use "leveled" type nodebox
liquid_range = 8, -- number of flowing nodes arround source (max. 8)
drowning = true, -- Player will drown in these
two or more sources nearly?

View File

@ -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

View File

@ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "treegen.h" // For treegen::make_tree
#include "main.h" // for g_settings
#include "map.h"
#include "cpp_api/scriptapi.h"
#include "log.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -166,46 +168,38 @@ public:
}
};
class LiquidFlowABM : public ActiveBlockModifier
{
private:
class LiquidFlowABM : public ActiveBlockModifier {
private:
std::set<std::string> contents;
public:
LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr)
{
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()
{
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)
{
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);
}
};
class LiquidDropABM : public ActiveBlockModifier
{
private:
class LiquidDropABM : public ActiveBlockModifier {
private:
std::set<std::string> contents;
public:
LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr)
{
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++)
@ -213,8 +207,7 @@ public:
}
virtual std::set<std::string> getTriggerContents()
{ return contents; }
virtual std::set<std::string> getRequiredNeighbors()
{
virtual std::set<std::string> getRequiredNeighbors() {
std::set<std::string> neighbors;
neighbors.insert("mapgen_air");
return neighbors;
@ -223,8 +216,7 @@ public:
{ return 20.0; }
virtual u32 getTriggerChance()
{ return 10; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
{
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) {
ServerMap *map = &env->getServerMap();
if (map->transforming_liquid_size() > 500)
return;
@ -239,13 +231,157 @@ public:
}
};
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));
}
}
}

View File

@ -178,6 +178,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
settings->setDefault("time_send_interval", "5");
settings->setDefault("time_speed", "72");
settings->setDefault("year_days", "30");
settings->setDefault("server_unload_unused_data_timeout", "29");
settings->setDefault("server_map_save_interval", "5.3");
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
@ -214,6 +215,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("liquid_relax", "2");
settings->setDefault("liquid_fast_flood", "1");
settings->setDefault("underground_springs", "1");
settings->setDefault("weather", "false");
//mapgen stuff
settings->setDefault("mg_name", "v6");

View File

@ -488,6 +488,14 @@ void *EmergeThread::Thread() {
if (block)
modified_blocks[p] = block;
// Update weather data in mapblock
for(std::map<v3s16, MapBlock *>::iterator
i = modified_blocks.begin();
i != modified_blocks.end(); ++i) {
map->getHeat(m_server->m_env, MAP_BLOCKSIZE*i->first ,i->second);
map->getHumidity(m_server->m_env, MAP_BLOCKSIZE*i->first, i->second);
}
// Set the modified blocks unsent for all the clients
for (std::map<u16, RemoteClient*>::iterator
i = m_server->m_clients.begin();

View File

@ -303,6 +303,7 @@ public:
//check if there's a line of sight between two positions
bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0);
u32 getGameTime() { return m_game_time; }
private:
/*

View File

@ -2455,6 +2455,7 @@ void the_game(
camera.step(dtime);
v3f player_position = player->getPosition();
v3s16 pos_i = floatToInt(player_position, BS);
v3f camera_position = camera.getPosition();
v3f camera_direction = camera.getDirection();
f32 camera_fov = camera.getFovMax();
@ -3034,7 +3035,9 @@ void the_game(
<<", "<<(player_position.Y/BS)
<<", "<<(player_position.Z/BS)
<<") (yaw="<<(wrapDegrees_0_360(camera_yaw))
<<") (seed = "<<((unsigned long long)client.getMapSeed())
<<") (t="<<client.getEnv().getClientMap().getHeat(pos_i)
<<"C, h="<<client.getEnv().getClientMap().getHumidity(pos_i)
<<"%) (seed = "<<((unsigned long long)client.getMapSeed())
<<")";
guitext2->setText(narrow_to_wide(os.str()).c_str());
guitext2->setVisible(true);

View File

@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "emerge.h"
#include "mapgen_v6.h"
#include "mapgen_indev.h"
#include "biome.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -1087,6 +1088,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
/*
Add neighboring liquid nodes and the node itself if it is
liquid (=water node was added) to transform queue.
note: todo: for liquid_finite enough to add only self node
*/
v3s16 dirs[7] = {
v3s16(0,0,0), // self
@ -1278,6 +1280,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
/*
Add neighboring liquid nodes and this node to transform queue.
(it's vital for the node itself to get updated last.)
note: todo: for liquid_finite enough to add only self node
*/
v3s16 dirs[7] = {
v3s16(0,0,1), // back
@ -2364,6 +2367,26 @@ void Map::removeNodeTimer(v3s16 p)
block->m_node_timers.remove(p_rel);
}
s16 Map::getHeat(v3s16 p)
{
MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
return block->heat;
}
//errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
return 0;
}
s16 Map::getHumidity(v3s16 p)
{
MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
return block->humidity;
}
//errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
return 0;
}
/*
ServerMap
*/
@ -3863,7 +3886,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
<<" (SerializationError). "
<<"what()="<<e.what()
<<std::endl;
//" Ignoring. A new one will be generated.
// Ignoring. A new one will be generated.
assert(0);
// TODO: Backup file; name is in fullpath.
@ -4039,6 +4062,63 @@ void ServerMap::PrintInfo(std::ostream &out)
out<<"ServerMap: ";
}
s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
{
if(block == NULL)
block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
if (env->getGameTime() - block->heat_time < 10)
return block->heat;
}
//variant 1: full random
//f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
//variant 2: season change based on default heat map
f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z, m_emerge->params->seed);
heat += -30; // -30 - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
f32 base = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
base /= ( 86400 * g_settings->getS16("year_days") );
base += (f32)p.X / 3000;
heat += 30 * sin(base * M_PI); // season
heat += p.Y / -333; // upper=colder, lower=hotter
// daily change, hotter at sun +4, colder at night -4
heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5);
if(block != NULL) {
block->heat = heat;
block->heat_time = env->getGameTime();
}
return heat;
}
s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
{
if(block == NULL)
block = getBlockNoCreateNoEx(getNodeBlockPos(p));
if(block != NULL) {
if (env->getGameTime() - block->humidity_time < 10)
return block->humidity;
}
f32 humidity = NoisePerlin3D( m_emerge->biomedef->np_humidity,
p.X, env->getGameTime()/10, p.Z,
m_emerge->params->seed);
humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
//todo like heat//humidity += 20 * ( sin(((f32)p.Z / 300) * M_PI) - 0.5);
if (humidity > 100) humidity = 100;
if (humidity < 0) humidity = 0;
if(block != NULL) {
block->humidity = humidity;
block->humidity_time = env->getGameTime();
}
return humidity;
}
/*
MapVoxelManipulator
*/

View File

@ -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;

View File

@ -58,7 +58,11 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0),
m_refcount(0)
m_refcount(0),
heat_time(0),
heat(0),
humidity_time(0),
humidity(0)
{
data = NULL;
if(dummy == false)
@ -632,6 +636,11 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
// Node timers
m_node_timers.serialize(os, version);
}
} else {
if(version >= 26){
writeF1000(os, heat);
writeF1000(os, humidity);
}
}
}
@ -734,6 +743,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
<<": Node timers (ver>=25)"<<std::endl);
m_node_timers.deSerialize(is, version);
}
} else {
if(version >= 26){
heat = readF1000(is);
humidity = readF1000(is);
}
}
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())

View File

@ -518,6 +518,11 @@ public:
NodeTimerList m_node_timers;
StaticObjectList m_static_objects;
s16 heat;
u32 heat_time;
s16 humidity;
u32 humidity_time;
private:
/*
Private member variables

View File

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_mapnode.h" // For mapnode_translate_*_internal
#include "serialization.h" // For ser_ver_supported
#include "util/serialize.h"
#include "log.h"
#include <string>
#include <sstream>
@ -359,9 +360,23 @@ std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
return transformNodeBox(*this, f.selection_box, nodemgr);
}
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
// todo: after update in all games leave only if (f.param_type_2 ==
if( f.liquid_type == LIQUID_SOURCE
|| f.liquid_type == LIQUID_FLOWING
|| f.param_type_2 == CPT2_FLOWINGLIQUID)
return LIQUID_LEVEL_MAX;
if(f.leveled || f.param_type_2 == CPT2_LEVELED)
return LEVELED_MAX;
return 0;
}
u8 MapNode::getLevel(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
// todo: after update in all games leave only if (f.param_type_2 ==
if(f.liquid_type == LIQUID_SOURCE)
return LIQUID_LEVEL_SOURCE;
if (f.param_type_2 == CPT2_FLOWINGLIQUID)
@ -377,6 +392,37 @@ u8 MapNode::getLevel(INodeDefManager *nodemgr) const
return 0;
}
u8 MapNode::addLevel(INodeDefManager *nodemgr, s8 add)
{
s8 level = getLevel(nodemgr);
u8 rest = 0;
if (add == 0) level = 1;
level += add;
if (level < 1) {
setContent(CONTENT_AIR);
return 0;
}
const ContentFeatures &f = nodemgr->get(*this);
if ( f.param_type_2 == CPT2_FLOWINGLIQUID
|| f.liquid_type == LIQUID_FLOWING
|| f.liquid_type == LIQUID_SOURCE) {
if (level >= LIQUID_LEVEL_MAX) {
rest = level - LIQUID_LEVEL_MAX;
setContent(nodemgr->getId(f.liquid_alternative_source));
} else {
setContent(nodemgr->getId(f.liquid_alternative_flowing));
setParam2(level & LIQUID_LEVEL_MASK);
}
} else if (f.leveled || f.param_type_2 == CPT2_LEVELED) {
if (level > LEVELED_MAX) {
rest = level - LEVELED_MAX;
level = LEVELED_MAX;
}
setParam2(level & LEVELED_MASK);
}
return rest;
}
u32 MapNode::serializedLength(u8 version)
{
if(!ser_ver_supported(version))

View File

@ -227,7 +227,9 @@ struct MapNode
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
/* Liquid helpers */
u8 getMaxLevel(INodeDefManager *nodemgr) const;
u8 getLevel(INodeDefManager *nodemgr) const;
u8 addLevel(INodeDefManager *nodemgr, s8 add = 1);
/*
Serialization functions

View File

@ -213,6 +213,7 @@ void ContentFeatures::reset()
liquid_alternative_source = "";
liquid_viscosity = 0;
liquid_renewable = true;
freezemelt = "";
liquid_range = LIQUID_LEVEL_MAX+1;
drowning = true;
light_source = 0;

View File

@ -224,6 +224,8 @@ struct ContentFeatures
u8 liquid_viscosity;
// Is liquid renewable (new liquid source will be created between 2 existing)
bool liquid_renewable;
// Ice for water, water for ice
std::string freezemelt;
// Number of flowing liquids surrounding source
u8 liquid_range;
bool drowning;

View File

@ -397,6 +397,7 @@ ContentFeatures read_content_features(lua_State *L, int index)
f.leveled = getintfield_default(L, index, "leveled", f.leveled);
getboolfield(L, index, "liquid_renewable", f.liquid_renewable);
getstringfield(L, index, "freezemelt", f.freezemelt);
getboolfield(L, index, "drowning", f.drowning);
// Amount of light the node emits
f.light_source = getintfield_default(L, index,

View File

@ -233,3 +233,20 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
scriptError("error: %s", lua_tostring(L, -1));
}
void ScriptApiNode::node_falling_update(v3s16 p)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "nodeupdate");
push_v3s16(L, p);
if(lua_pcall(L, 1, 0, 0))
scriptError("error: %s", lua_tostring(L, -1));
}
void ScriptApiNode::node_falling_update_single(v3s16 p)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "nodeupdate_single");
push_v3s16(L, p);
if(lua_pcall(L, 1, 0, 0))
scriptError("error: %s", lua_tostring(L, -1));
}

View File

@ -49,6 +49,8 @@ public:
const std::string &formname,
const std::map<std::string, std::string> &fields,
ServerActiveObject *sender);
void node_falling_update(v3s16 p);
void node_falling_update_single(v3s16 p);
public:
static struct EnumString es_DrawType[];
static struct EnumString es_ContentParamType[];

View File

@ -263,6 +263,48 @@ int ModApiEnvMod::l_punch_node(lua_State *L)
return 1;
}
// minetest.get_node_max_level(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_max_level(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
MapNode n = env->getMap().getNodeNoEx(pos);
lua_pushnumber(L, n.getMaxLevel(env->getGameDef()->ndef()));
return 1;
}
// minetest.get_node_level(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_level(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
MapNode n = env->getMap().getNodeNoEx(pos);
lua_pushnumber(L, n.getLevel(env->getGameDef()->ndef()));
return 1;
}
// minetest.add_node_level(pos, level)
// pos = {x=num, y=num, z=num}
// level: 0..8
int ModApiEnvMod::l_add_node_level(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
u8 level = 1;
if(lua_isnumber(L, 2))
level = lua_tonumber(L, 2);
MapNode n = env->getMap().getNodeNoEx(pos);
lua_pushnumber(L, n.addLevel(env->getGameDef()->ndef(), level));
env->setNode(pos, n);
return 1;
}
// minetest.get_meta(pos)
int ModApiEnvMod::l_get_meta(lua_State *L)
{
@ -820,6 +862,40 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
return 1;
}
// minetest.transforming_liquid_add(pos)
int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
{
GET_ENV_PTR;
v3s16 p0 = read_v3s16(L, 1);
env->getMap().transforming_liquid_add(p0);
return 1;
}
// minetest.get_heat(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_heat(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
lua_pushnumber(L, env->getServerMap().getHeat(env, pos));
return 1;
}
// minetest.get_humidity(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_humidity(lua_State *L)
{
GET_ENV_PTR;
v3s16 pos = read_v3s16(L, 1);
lua_pushnumber(L, env->getServerMap().getHumidity(env, pos));
return 1;
}
bool ModApiEnvMod::Initialize(lua_State *L,int top)
{
@ -835,6 +911,9 @@ bool ModApiEnvMod::Initialize(lua_State *L,int top)
retval &= API_FCT(place_node);
retval &= API_FCT(dig_node);
retval &= API_FCT(punch_node);
retval &= API_FCT(get_node_max_level);
retval &= API_FCT(get_node_level);
retval &= API_FCT(add_node_level);
retval &= API_FCT(add_entity);
retval &= API_FCT(get_meta);
retval &= API_FCT(get_node_timer);
@ -853,6 +932,9 @@ bool ModApiEnvMod::Initialize(lua_State *L,int top)
retval &= API_FCT(spawn_tree);
retval &= API_FCT(find_path);
retval &= API_FCT(line_of_sight);
retval &= API_FCT(transforming_liquid_add);
retval &= API_FCT(get_heat);
retval &= API_FCT(get_humidity);
return retval;
}

View File

@ -67,6 +67,19 @@ private:
// pos = {x=num, y=num, z=num}
static int l_punch_node(lua_State *L);
// minetest.get_node_max_level(pos)
// pos = {x=num, y=num, z=num}
static int l_get_node_max_level(lua_State *L);
// minetest.get_node_level(pos)
// pos = {x=num, y=num, z=num}
static int l_get_node_level(lua_State *L);
// minetest.add_node_level(pos)
// pos = {x=num, y=num, z=num}
static int l_add_node_level(lua_State *L);
// minetest.get_meta(pos)
static int l_get_meta(lua_State *L);
@ -136,6 +149,12 @@ private:
// 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[];
public:

View File

@ -61,11 +61,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
23: new node metadata format
24: 16-bit node ids and node timers (never released as stable)
25: Improved node timer format
26: MapBlocks contain heat and humidity
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
#define SER_FMT_VER_HIGHEST 25
#define SER_FMT_VER_HIGHEST 26
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0

View File

@ -349,5 +349,12 @@ inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxi
}
}
inline float cycle_shift(float value, float by = 0, float max = 1)
{
if (value + by < 0) return max + by + value;
if (value + by > max) return value + by - max;
return value + by;
}
#endif