diff options
Diffstat (limited to 'src/map.cpp')
-rw-r--r-- | src/map.cpp | 435 |
1 files changed, 214 insertions, 221 deletions
diff --git a/src/map.cpp b/src/map.cpp index 9ff0fa6d3..e47b1b212 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1086,7 +1086,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, v3s16 p2 = p + dirs[i]; MapNode n2 = getNode(p2); - if(content_liquid(n2.d)) + if(content_liquid(n2.d) || n2.d == CONTENT_AIR) { m_transforming_liquid.push_back(p2); } @@ -1240,17 +1240,19 @@ void Map::removeNodeAndUpdate(v3s16 p, } /* - Add neighboring liquid nodes to transform queue. + Add neighboring liquid nodes and this node to transform queue. + (it's vital for the node itself to get updated last.) */ - v3s16 dirs[6] = { + v3s16 dirs[7] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left + v3s16(0,0,0), // self }; - for(u16 i=0; i<6; i++) + for(u16 i=0; i<7; i++) { try { @@ -1258,7 +1260,7 @@ void Map::removeNodeAndUpdate(v3s16 p, v3s16 p2 = p + dirs[i]; MapNode n2 = getNode(p2); - if(content_liquid(n2.d)) + if(content_liquid(n2.d) || n2.d == CONTENT_AIR) { m_transforming_liquid.push_back(p2); } @@ -1538,6 +1540,17 @@ void Map::PrintInfo(std::ostream &out) #define WATER_DROP_BOOST 4 +enum NeighborType { + NEIGHBOR_UPPER, + NEIGHBOR_SAME_LEVEL, + NEIGHBOR_LOWER +}; +struct NodeNeighbor { + MapNode n; + NeighborType t; + v3s16 p; +}; + void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) { DSTACK(__FUNCTION_NAME); @@ -1557,240 +1570,220 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) v3s16 p0 = m_transforming_liquid.pop_front(); MapNode n0 = getNodeNoEx(p0); - - // Don't deal with non-liquids - if(content_liquid(n0.d) == false) - continue; - - bool is_source = !content_flowing_liquid(n0.d); - - u8 liquid_level = 8; - if(is_source == false) - liquid_level = n0.param2 & 0x0f; - - // Turn possible source into non-source - u8 nonsource_c = make_liquid_flowing(n0.d); - + /* - If not source, check that some node flows into this one - and what is the level of liquid in this one - */ - if(is_source == false) - { - s8 new_liquid_level_max = -1; - - v3s16 dirs_from[5] = { - v3s16(0,1,0), // top - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - for(u16 i=0; i<5; i++) - { - bool from_top = (i==0); - - v3s16 p2 = p0 + dirs_from[i]; - MapNode n2 = getNodeNoEx(p2); - - if(content_liquid(n2.d)) - { - u8 n2_nonsource_c = make_liquid_flowing(n2.d); - // Check that the liquids are the same type - if(n2_nonsource_c != nonsource_c) - { - dstream<<"WARNING: Not handling: different liquids" - " collide"<<std::endl; - continue; + Collect information about current node + */ + s8 liquid_level = -1; + u8 liquid_kind = CONTENT_IGNORE; + LiquidType liquid_type = content_features(n0.d).liquid_type; + switch (liquid_type) { + case LIQUID_SOURCE: + liquid_level = 8; + liquid_kind = content_features(n0.d).liquid_alternative_flowing; + break; + case LIQUID_FLOWING: + liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); + liquid_kind = n0.d; + 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.d != CONTENT_AIR) + continue; + liquid_kind = CONTENT_AIR; + break; + } + + /* + Collect information about the environment + */ + v3s16 dirs[6] = { + v3s16( 0, 1, 0), // top + v3s16( 0,-1, 0), // bottom + v3s16( 1, 0, 0), // right + v3s16(-1, 0, 0), // left + v3s16( 0, 0, 1), // back + v3s16( 0, 0,-1), // front + }; + 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 0: + nt = NEIGHBOR_UPPER; + break; + case 1: + nt = NEIGHBOR_LOWER; + break; + } + v3s16 npos = p0 + dirs[i]; + NodeNeighbor nb = {getNodeNoEx(npos), nt, npos}; + switch (content_features(nb.n.d).liquid_type) { + case LIQUID_NONE: + if (nb.n.d == CONTENT_AIR) { + airs[num_airs++] = nb; + // 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; } - bool n2_is_source = !content_flowing_liquid(n2.d); - s8 n2_liquid_level = 8; - if(n2_is_source == false) - n2_liquid_level = n2.param2 & 0x07; - - s8 new_liquid_level = -1; - if(from_top) - { - //new_liquid_level = 7; - if(n2_liquid_level >= 7 - WATER_DROP_BOOST) - new_liquid_level = 7; - else - new_liquid_level = n2_liquid_level + WATER_DROP_BOOST; + 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 = content_features(nb.n.d).liquid_alternative_flowing; + if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) { + neutrals[num_neutrals++] = nb; + } else { + sources[num_sources++] = nb; } - else if(n2_liquid_level > 0) - { - new_liquid_level = n2_liquid_level - 1; + 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 = content_features(nb.n.d).liquid_alternative_flowing; + if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) { + neutrals[num_neutrals++] = nb; + } else { + flows[num_flows++] = nb; + if (nb.t == NEIGHBOR_LOWER) + flowing_down = true; } - - if(new_liquid_level > new_liquid_level_max) - new_liquid_level_max = new_liquid_level; - } - } //for - - /* - If liquid level should be something else, update it and - add all the neighboring water nodes to the transform queue. - */ - if(new_liquid_level_max != liquid_level) - { - if(new_liquid_level_max == -1) - { - // Remove water alltoghether - n0.d = CONTENT_AIR; - n0.param2 = 0; - setNode(p0, n0); - } - else - { - n0.param2 = new_liquid_level_max; - setNode(p0, n0); - } - - // Block has been modified - { - v3s16 blockpos = getNodeBlockPos(p0); - MapBlock *block = getBlockNoCreateNoEx(blockpos); - if(block != NULL) - modified_blocks.insert(blockpos, block); + break; + } + } + + /* + decide on the type (and possibly level) of the current node + */ + u8 new_node_content; + s8 new_node_level = -1; + if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) { + // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid) + // or the flowing alternative of the first of the surrounding sources (if it's air), so + // it's perfectly safe to use liquid_kind here to determine the new node content. + new_node_content = content_features(liquid_kind).liquid_alternative_source; + } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) { + // liquid_kind is set properly, see above + new_node_content = liquid_kind; + new_node_level = 7; + } else { + // no surrounding sources, so get the maximum level that can flow into this node + for (u16 i = 0; i < num_flows; i++) { + u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK); + switch (flows[i].t) { + case NEIGHBOR_UPPER: + if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) { + new_node_level = 7; + if (nb_liquid_level + WATER_DROP_BOOST < 7) + new_node_level = nb_liquid_level + WATER_DROP_BOOST; + } + break; + case NEIGHBOR_LOWER: + break; + case NEIGHBOR_SAME_LEVEL: + if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK && + nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) { + new_node_level = nb_liquid_level - 1; + } + break; } - - /* - Add neighboring non-source liquid nodes to transform queue. - */ - v3s16 dirs[6] = { - v3s16(0,0,1), // back - v3s16(0,1,0), // top - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - for(u16 i=0; i<6; i++) - { - v3s16 p2 = p0 + dirs[i]; - - MapNode n2 = getNodeNoEx(p2); - if(content_flowing_liquid(n2.d)) - { - m_transforming_liquid.push_back(p2); + } + // don't flow as far in open terrain - if there isn't at least one adjacent solid block, + // substract another unit from the resulting water level. + if (!flowing_down && new_node_level >= 1) { + bool at_wall = false; + for (u16 i = 0; i < num_neutrals; i++) { + if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) { + at_wall = true; + break; } } + if (!at_wall) + new_node_level -= 1; } + + if (new_node_level >= 0) + new_node_content = liquid_kind; + else + new_node_content = CONTENT_AIR; } - - // Get a new one from queue if the node has turned into non-water - if(content_liquid(n0.d) == false) + + /* + check if anything has changed. if not, just continue with the next node. + */ + if (new_node_content == n0.d && (content_features(n0.d).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))) continue; - + + /* - Flow water from this node - */ - v3s16 dirs_to[5] = { - v3s16(0,-1,0), // bottom - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - for(u16 i=0; i<5; i++) - { - bool to_bottom = (i == 0); - - // If liquid is at lowest possible height, it's not going - // anywhere except down - if(liquid_level == 0 && to_bottom == false) - continue; - - u8 liquid_next_level = 0; - // If going to bottom - if(to_bottom) - { - //liquid_next_level = 7; - if(liquid_level >= 7 - WATER_DROP_BOOST) - liquid_next_level = 7; - else - liquid_next_level = liquid_level + WATER_DROP_BOOST; - } - else - liquid_next_level = liquid_level - 1; - - bool n2_changed = false; - bool flowed = false; - - v3s16 p2 = p0 + dirs_to[i]; - - MapNode n2 = getNodeNoEx(p2); - //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl; - - if(content_liquid(n2.d)) - { - u8 n2_nonsource_c = make_liquid_flowing(n2.d); - // Check that the liquids are the same type - if(n2_nonsource_c != nonsource_c) - { - dstream<<"WARNING: Not handling: different liquids" - " collide"<<std::endl; - continue; - } - bool n2_is_source = !content_flowing_liquid(n2.d); - u8 n2_liquid_level = 8; - if(n2_is_source == false) - n2_liquid_level = n2.param2 & 0x07; - - if(to_bottom) - { - flowed = true; - } - - if(n2_is_source) - { - // Just flow into the source, nothing changes. - // n2_changed is not set because destination didn't change - flowed = true; + update the current node + */ + bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK)); + n0.d = new_node_content; + if (content_features(n0.d).liquid_type == LIQUID_FLOWING) { + // set level to last 3 bits, flowing down bit to 4th bit + n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK); + } else { + n0.param2 = 0; + } + setNode(p0, n0); + v3s16 blockpos = getNodeBlockPos(p0); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block != NULL) + modified_blocks.insert(blockpos, block); + + /* + enqueue neighbors for update if neccessary + */ + switch (content_features(n0.d).liquid_type) { + case LIQUID_SOURCE: + // make sure source flows into all neighboring nodes + for (u16 i = 0; i < num_flows; i++) + if (flows[i].t != NEIGHBOR_UPPER) + m_transforming_liquid.push_back(flows[i].p); + for (u16 i = 0; i < num_airs; i++) + if (airs[i].t != NEIGHBOR_UPPER) + m_transforming_liquid.push_back(airs[i].p); + break; + case LIQUID_NONE: + // this flow has turned to air; neighboring flows might need to do the same + for (u16 i = 0; i < num_flows; i++) + m_transforming_liquid.push_back(flows[i].p); + break; + case LIQUID_FLOWING: + for (u16 i = 0; i < num_flows; i++) { + u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK); + // liquid_level is still the ORIGINAL level of this node. + if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) || + flow_down_enabled)) + m_transforming_liquid.push_back(flows[i].p); } - else - { - if(liquid_next_level > liquid_level) - { - n2.param2 = liquid_next_level; - setNode(p2, n2); - - n2_changed = true; - flowed = true; - } + for (u16 i = 0; i < num_airs; i++) { + if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0)) + m_transforming_liquid.push_back(airs[i].p); } - } - else if(n2.d == CONTENT_AIR) - { - n2.d = nonsource_c; - n2.param2 = liquid_next_level; - setNode(p2, n2); - - n2_changed = true; - flowed = true; - } - - //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl; - - if(n2_changed) - { - m_transforming_liquid.push_back(p2); - - v3s16 blockpos = getNodeBlockPos(p2); - MapBlock *block = getBlockNoCreateNoEx(blockpos); - if(block != NULL) - modified_blocks.insert(blockpos, block); - } - - // If n2_changed to bottom, don't flow anywhere else - if(to_bottom && flowed && !is_source) break; } - + loopcount++; //if(loopcount >= 100000) - if(loopcount >= initial_size * 1) + if(loopcount >= initial_size * 10) { break; + } } //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl; } |