diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 7174a8a4b..f45853c4a 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -26,6 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define WATER_ALPHA 160 +#define WATER_VISC 1 +#define LAVA_VISC 7 + // TODO: Get rid of these and set up some attributes like toughness, // fluffyness, and a funciton to calculate time and durability loss // (and sound? and whatever else) from them @@ -374,6 +377,7 @@ void content_mapnode_init() f->liquid_type = LIQUID_FLOWING; f->liquid_alternative_flowing = CONTENT_WATER; f->liquid_alternative_source = CONTENT_WATERSOURCE; + f->liquid_viscosity = WATER_VISC; f->vertex_alpha = WATER_ALPHA; if(f->special_material == NULL && g_texturesource) { @@ -421,6 +425,7 @@ void content_mapnode_init() f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; f->liquid_alternative_flowing = CONTENT_WATER; f->liquid_alternative_source = CONTENT_WATERSOURCE; + f->liquid_viscosity = WATER_VISC; f->vertex_alpha = WATER_ALPHA; if(f->special_material == NULL && g_texturesource) { @@ -451,6 +456,7 @@ void content_mapnode_init() f->liquid_type = LIQUID_FLOWING; f->liquid_alternative_flowing = CONTENT_LAVA; f->liquid_alternative_source = CONTENT_LAVASOURCE; + f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; if(f->special_material == NULL && g_texturesource) { @@ -499,6 +505,7 @@ void content_mapnode_init() f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; f->liquid_alternative_flowing = CONTENT_LAVA; f->liquid_alternative_source = CONTENT_LAVASOURCE; + f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; if(f->special_material == NULL && g_texturesource) { diff --git a/src/map.cpp b/src/map.cpp index 9d67c8282..f5c4a5e02 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1562,6 +1562,9 @@ void Map::transformLiquids(core::map & modified_blocks) /*if(initial_size != 0) dstream<<"transformLiquids(): initial_size="< must_reflow; + while(m_transforming_liquid.size() != 0) { // This should be done here so that it is done when continue is used @@ -1575,7 +1578,7 @@ void Map::transformLiquids(core::map & modified_blocks) v3s16 p0 = m_transforming_liquid.pop_front(); MapNode n0 = getNodeNoEx(p0); - + /* Collect information about current node */ @@ -1584,7 +1587,7 @@ void Map::transformLiquids(core::map & modified_blocks) LiquidType liquid_type = content_features(n0.getContent()).liquid_type; switch (liquid_type) { case LIQUID_SOURCE: - liquid_level = 8; + liquid_level = LIQUID_LEVEL_SOURCE; liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing; break; case LIQUID_FLOWING: @@ -1599,18 +1602,11 @@ void Map::transformLiquids(core::map & modified_blocks) 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 - }; + const v3s16 *dirs = g_6dirs; NodeNeighbor sources[6]; // surrounding sources int num_sources = 0; NodeNeighbor flows[6]; // surrounding flowing liquid nodes @@ -1623,10 +1619,10 @@ void Map::transformLiquids(core::map & modified_blocks) for (u16 i = 0; i < 6; i++) { NeighborType nt = NEIGHBOR_SAME_LEVEL; switch (i) { - case 0: + case 1: nt = NEIGHBOR_UPPER; break; - case 1: + case 4: nt = NEIGHBOR_LOWER; break; } @@ -1636,9 +1632,15 @@ void Map::transformLiquids(core::map & modified_blocks) 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) + if (nb.t == NEIGHBOR_LOWER) { flowing_down = true; + } } else { neutrals[num_neutrals++] = nb; } @@ -1667,12 +1669,13 @@ void Map::transformLiquids(core::map & modified_blocks) break; } } - + /* decide on the type (and possibly level) of the current node */ content_t new_node_content; s8 new_node_level = -1; + s8 max_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 @@ -1681,49 +1684,53 @@ void Map::transformLiquids(core::map & modified_blocks) } 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; + max_node_level = new_node_level = LIQUID_LEVEL_MAX; } 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; + if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) { + max_node_level = LIQUID_LEVEL_MAX; + if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX) + max_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; + nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) { + max_node_level = nb_liquid_level - 1; } break; } } - // 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; - } - + + u8 viscosity = content_features(liquid_kind).liquid_viscosity; + if (viscosity > 1 && max_node_level != liquid_level) { + // amount to gain, limited by viscosity + // must be at least 1 in absolute value + s8 level_inc = max_node_level - liquid_level; + if (level_inc < -viscosity || level_inc > viscosity) + new_node_level = liquid_level + level_inc/viscosity; + else if (level_inc < 0) + new_node_level = liquid_level - 1; + else if (level_inc > 0) + new_node_level = liquid_level + 1; + if (new_node_level != max_node_level) + must_reflow.push_back(p0); + } else + new_node_level = max_node_level; + if (new_node_level >= 0) new_node_content = liquid_kind; else new_node_content = CONTENT_AIR; + } - + /* check if anything has changed. if not, just continue with the next node. */ @@ -1732,31 +1739,32 @@ void Map::transformLiquids(core::map & modified_blocks) ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK) == flowing_down))) continue; - - + + /* update the current node */ bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK)); - n0.setContent(new_node_content); - if (content_features(n0.getContent()).liquid_type == LIQUID_FLOWING) { + if (content_features(new_node_content).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); + n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK); } else { // set the liquid level and flow bit to 0 - n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK); + n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK); } + n0.setContent(new_node_content); 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.getContent()).liquid_type) { case LIQUID_SOURCE: + case LIQUID_FLOWING: // make sure source flows into all neighboring nodes for (u16 i = 0; i < num_flows; i++) if (flows[i].t != NEIGHBOR_UPPER) @@ -1770,22 +1778,11 @@ void Map::transformLiquids(core::map & modified_blocks) 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); - } - 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); - } - break; } } //dstream<<"Map::transformLiquids(): loopcount="< 0) + m_transforming_liquid.push_back(must_reflow.pop_front()); } NodeMetadata* Map::getNodeMetadata(v3s16 p) diff --git a/src/mapnode.h b/src/mapnode.h index 3101a9fc1..4c2b92853 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -153,6 +153,10 @@ struct ContentFeatures content_t liquid_alternative_flowing; // If the content is liquid, this is the source version of the liquid. content_t liquid_alternative_source; + // Viscosity for fluid flow, ranging from 1 to 7, with + // 1 giving almost instantaneous propagation and 7 being + // the slowest possible + u8 liquid_viscosity; // Used currently for flowing liquids u8 vertex_alpha; // Special irrlicht material, used sometimes @@ -189,6 +193,7 @@ struct ContentFeatures initial_metadata = NULL; liquid_alternative_flowing = CONTENT_IGNORE; liquid_alternative_source = CONTENT_IGNORE; + liquid_viscosity = 0; vertex_alpha = 255; special_material = NULL; special_atlas = NULL; @@ -424,6 +429,10 @@ enum LightBank #define LIQUID_LEVEL_MASK 0x07 #define LIQUID_FLOW_DOWN_MASK 0x08 +/* maximum amount of liquid in a block */ +#define LIQUID_LEVEL_MAX LIQUID_LEVEL_MASK +#define LIQUID_LEVEL_SOURCE (LIQUID_LEVEL_MAX+1) + /* This is the stuff what the whole world consists of. */