- // if this node is not (yet) of a liquid type,
- // choose the first liquid type we encounter
- if (liquid_kind_flowing == CONTENT_IGNORE)
- liquid_kind_flowing = nb.n.getContent();
- if (liquid_kind == CONTENT_IGNORE)
- liquid_kind = nodemgr->getId(
- nodemgr->get(nb.n).liquid_alternative_source);
- if (nb.n.getContent() == liquid_kind_flowing) {
- liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
- nb.l = 1;
- }
- break;
- }
-
- if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
- ++can_liquid_same_level;
- if (liquid_levels[i] > 0)
- total_level += liquid_levels[i];
-
- /*
- infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="
- << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="
- << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
- << nodemgr->get(nb.n.getContent()).liquid_type
- //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
- << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
- << " tlevel=" << (int)total_level << " cansame="
- << (int)can_liquid_same_level << std::endl;
- */
- }
-
- if (liquid_kind == CONTENT_IGNORE ||
- !neighbors[D_SELF].l ||
- total_level <= 0)
- continue;
-
- // fill bottom block
- if (neighbors[D_BOTTOM].l) {
- liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ?
- LIQUID_LEVEL_SOURCE : total_level;
- total_level -= liquid_levels_want[D_BOTTOM];
- }
-
- //relax up
- if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 &&
- liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE &&
- total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level-
- (can_liquid_same_level - relax) &&
- can_liquid_same_level >= relax + 1) {
- total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
- }
-
- // prevent lakes in air above unloaded blocks
- if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level || !fast_flood) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE) {
- --total_level;
- }
-
- // calculate self level 5 blocks
- u8 want_level =
- total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
- ? LIQUID_LEVEL_SOURCE
- : total_level / can_liquid_same_level;
- total_level -= want_level * can_liquid_same_level;
-
- //relax down
- if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 &&
- liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 &&
- total_level <= (can_liquid_same_level - relax) &&
- can_liquid_same_level >= relax + 1) {
- total_level = 0;
- }
-
- for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
- if (!neighbors[ii].l)
- continue;
- liquid_levels_want[ii] = want_level;
- if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
- && liquid_levels[ii] > liquid_levels_want[ii]
- ) {
- ++liquid_levels_want[ii];
- --total_level;
- }
- }
-
- for (u16 ii = 0; ii < 7; ++ii) {
- if (total_level < 1) break;
- if (liquid_levels_want[ii] >= 0 &&
- liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
- ++liquid_levels_want[ii];
- --total_level;
- }
- }
-
- // fill top block if can
- if (neighbors[D_TOP].l) {
- liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ?
- LIQUID_LEVEL_SOURCE : total_level;
- total_level -= liquid_levels_want[D_TOP];
- }
-
- for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
- if ( neighbors[ii].i ||
- (liquid_levels_want[ii] >= 0 &&
- (fast_flood && p0.Y < water_level &&
- (initial_size >= 1000
- && ii != D_TOP
- && want_level >= LIQUID_LEVEL_SOURCE/4
- && can_liquid_same_level >= 5
- && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
- liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
-
- /*
- if (total_level > 0) //|| flowed != volume)
- infostream <<" AFTER level=" << (int)total_level
- //<< " flowed="<<flowed<< " volume=" << volume
- << " wantsame="<<(int)want_level<< " top="
- << (int)liquid_levels_want[D_TOP]<< " topwas="
- << (int)liquid_levels[D_TOP]<< " bot="
- << (int)liquid_levels_want[D_BOTTOM]<<std::endl;
- */
-
- u8 changed = 0;
- for (u16 i = 0; i < 7; i++) {
- if (liquid_levels_want[i] < 0 || !neighbors[i].l)
- continue;
- MapNode & n0 = neighbors[i].n;
- p0 = neighbors[i].p;
- /*
- decide on the type (and possibly level) of the current node
- */
- content_t new_node_content;
- s8 new_node_level = -1;
- u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
- if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
- // amount to gain, limited by viscosity
- // must be at least 1 in absolute value
- s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
- if (level_inc < -viscosity || level_inc > viscosity)
- new_node_level = liquid_levels[i] + level_inc/viscosity;
- else if (level_inc < 0)
- new_node_level = liquid_levels[i] - 1;
- else if (level_inc > 0)
- new_node_level = liquid_levels[i] + 1;
- } else {
- new_node_level = liquid_levels_want[i];
- }
-
- if (new_node_level >= LIQUID_LEVEL_SOURCE)
- new_node_content = liquid_kind;
- else if (new_node_level > 0)
- new_node_content = liquid_kind_flowing;
- else
- new_node_content = CONTENT_AIR;
-
- // last level must flow down on stairs
- if (liquid_levels_want[i] != liquid_levels[i] &&
- liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l &&
- new_node_level >= 1 && new_node_level <= 2) {
- for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
- if (neighbors[ii].l)
- must_reflow_second.push_back(p0 + dirs[ii]);
- }
- }
-
- /*
- check if anything has changed.
- if not, just continue with the next node.
- */
- if (
- new_node_content == n0.getContent()
- && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
- ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
- //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) ==
- //LIQUID_FLOW_DOWN_MASK) == flowing_down
- ))
- &&
- (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
- (((n0.param2 & LIQUID_INFINITY_MASK) ==
- LIQUID_INFINITY_MASK) == neighbors[i].i
- ))
- ) {
- continue;
- }
- ++changed;
-
- /*
- update the current node
- */
- if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
- // set level to last 3 bits, flowing down bit to 4th bit
- n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
- } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
- //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
- n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
- }
- /*
- infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="
- <<new_node_content<< " p2="<<(int)n0.param2<< " nl="
- <<(int)new_node_level<<std::endl;
- */
-
- n0.setContent(new_node_content);
- // Find out whether there is a suspect for this action
- std::string suspect;
- if(m_gamedef->rollback()){
- suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
- }
-
- if(!suspect.empty()){
- // Blame suspect
- RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
- // Get old node for rollback
- RollbackNode rollback_oldnode(this, p0, m_gamedef);
- // Set node
- setNode(p0, n0);
- // Report
- RollbackNode rollback_newnode(this, p0, m_gamedef);
- RollbackAction action;
- action.setSetNode(p0, rollback_oldnode, rollback_newnode);
- m_gamedef->rollback()->reportAction(action);
- } else {
- // Set node
- setNode(p0, n0);
- }
-
- v3s16 blockpos = getNodeBlockPos(p0);
- MapBlock *block = getBlockNoCreateNoEx(blockpos);
- if(block != NULL) {
- modified_blocks[blockpos] = block;
- // If node emits light, MapBlock requires lighting update
- if(nodemgr->get(n0).light_source != 0)
- lighting_modified_blocks[block->getPos()] = block;
- }
- must_reflow.push_back(neighbors[i].p);
- }
- /* //for better relax only same level
- if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
- if (!neighbors[ii].l) continue;
- must_reflow.push_back(p0 + dirs[ii]);
- }*/
- }
- /*
- if (loopcount)
- infostream<<"Map::transformLiquids(): loopcount="<<loopcount
- <<" reflow="<<must_reflow.size()
- <<" queue="<< m_transforming_liquid.size()<<std::endl;
- */
- while (must_reflow.size() > 0)
- m_transforming_liquid.push_back(must_reflow.pop_front());
- while (must_reflow_second.size() > 0)
- m_transforming_liquid.push_back(must_reflow_second.pop_front());
- updateLighting(lighting_modified_blocks, modified_blocks);
-}
-
-void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
-{
-
- if (g_settings->getBool("liquid_finite"))
- return Map::transformLiquidsFinite(modified_blocks);
-
- INodeDefManager *nodemgr = m_gamedef->ndef();
-
- DSTACK(__FUNCTION_NAME);
- //TimeTaker timer("transformLiquids()");
-
- u32 loopcount = 0;
- u32 initial_size = m_transforming_liquid.size();
-
- /*if(initial_size != 0)
- infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
-
- // list of nodes that due to viscosity have not reached their max level height
- UniqueQueue<v3s16> must_reflow;
-
- // List of MapBlocks that will require a lighting update (due to lava)
- std::map<v3s16, MapBlock*> lighting_modified_blocks;
-
- while(m_transforming_liquid.size() != 0)
- {
- // This should be done here so that it is done when continue is used
- if(loopcount >= initial_size || loopcount >= 10000)
- break;
- loopcount++;
-
- /*
- Get a queued transforming liquid node
- */
- v3s16 p0 = m_transforming_liquid.pop_front();
-
- MapNode n0 = getNodeNoEx(p0);
-
- /*
- Collect information about current node
- */
- s8 liquid_level = -1;
- content_t liquid_kind = CONTENT_IGNORE;
- LiquidType liquid_type = nodemgr->get(n0).liquid_type;
- switch (liquid_type) {
- case LIQUID_SOURCE:
- liquid_level = LIQUID_LEVEL_SOURCE;
- liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
- break;
- case LIQUID_FLOWING:
- liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
- liquid_kind = n0.getContent();
- break;
- case LIQUID_NONE:
- // if this is an air node, it *could* be transformed into a liquid. otherwise,
- // continue with the next node.
- if (n0.getContent() != CONTENT_AIR)
- continue;
- liquid_kind = CONTENT_AIR;
- break;
- }
-
- /*
- Collect information about the environment
- */
- const v3s16 *dirs = g_6dirs;
- NodeNeighbor sources[6]; // surrounding sources
- int num_sources = 0;
- NodeNeighbor flows[6]; // surrounding flowing liquid nodes
- int num_flows = 0;
- NodeNeighbor airs[6]; // surrounding air
- int num_airs = 0;
- NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
- int num_neutrals = 0;
- bool flowing_down = false;
- for (u16 i = 0; i < 6; i++) {
- NeighborType nt = NEIGHBOR_SAME_LEVEL;
- switch (i) {
- case 1:
- nt = NEIGHBOR_UPPER;
- break;
- case 4:
- nt = NEIGHBOR_LOWER;
- break;
- }
- v3s16 npos = p0 + dirs[i];
- NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
- switch (nodemgr->get(nb.n.getContent()).liquid_type) {
- case LIQUID_NONE:
- if (nb.n.getContent() == CONTENT_AIR) {
- airs[num_airs++] = nb;
- // if the current node is a water source the neighbor
- // should be enqueded for transformation regardless of whether the
- // current node changes or not.
- if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
- m_transforming_liquid.push_back(npos);
- // if the current node happens to be a flowing node, it will start to flow down here.
- if (nb.t == NEIGHBOR_LOWER) {
- flowing_down = true;
- }
- } else {
- neutrals[num_neutrals++] = nb;
- }
- break;
- case LIQUID_SOURCE:
- // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
- if (liquid_kind == CONTENT_AIR)
- liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
- if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
- neutrals[num_neutrals++] = nb;
- } else {
- // Do not count bottom source, it will screw things up
- if(dirs[i].Y != -1)
- sources[num_sources++] = nb;
- }
- break;
- case LIQUID_FLOWING:
- // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
- if (liquid_kind == CONTENT_AIR)
- liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
- if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
- neutrals[num_neutrals++] = nb;
- } else {
- flows[num_flows++] = nb;
- if (nb.t == NEIGHBOR_LOWER)
- flowing_down = true;