Allow multiple cave liquids in a biome definition (#8481)
authorParamat <paramat@users.noreply.github.com>
Sat, 18 May 2019 20:13:14 +0000 (21:13 +0100)
committerGitHub <noreply@github.com>
Sat, 18 May 2019 20:13:14 +0000 (21:13 +0100)
This allows games to specify biome cave liquids and avoid the old
hardcoded behaviour, but preserves the ability to have multiple
cave liquids in one biome, such as lava and water.

When multiple cave liquids are defined by the biome definition,
make each entire cave use a randomly chosen liquid, instead of
every small cave segment using a randomly chosen liquid.

Plus an optimisation:
Don't place nodes if cave liquid is defined as 'air'

doc/lua_api.txt
src/mapgen/cavegen.cpp
src/mapgen/cavegen.h
src/mapgen/mg_biome.cpp
src/mapgen/mg_biome.h
src/script/lua_api/l_mapgen.cpp

index f72b87625b9c90768645808776a03ae3691a5de1..f29b7d439798996d867447b610dac8661976a4ae 100644 (file)
@@ -6510,10 +6510,14 @@ Used by `minetest.register_biome`.
         depth_riverbed = 2,
         -- Node placed under river water and thickness of this layer
 
-        node_cave_liquid = "default:water_source",
-        -- Nodes placed as a blob of liquid in 50% of large caves.
-        -- If absent, cave liquids fall back to classic behaviour of lava or
-        -- water distributed according to a hardcoded 3D noise.
+        node_cave_liquid = "default:lava_source",
+        node_cave_liquid = {"default:water_source", "default:lava_source"},
+        -- Nodes placed inside 50% of the medium size caves.
+        -- Multiple nodes can be specified, each cave will use a randomly
+        -- chosen node from the list.
+        -- If this field is left out or 'nil', cave liquids fall back to
+        -- classic behaviour of lava and water distributed using 3D noise.
+        -- For no cave liquid, specify "air".
 
         node_dungeon = "default:cobble",
         -- Node used for primary dungeon structure.
index e54d76e08e1d74879651b18089d6496051256321..4fa3e009d73775c8af8959343d9917e132ce5d35 100644 (file)
@@ -321,9 +321,26 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
 
        this->ystride = nmax.X - nmin.X + 1;
 
+       flooded = ps->range(1, 2) == 2;
+
+       // If flooded:
+       // Get biome at mapchunk midpoint. If cave liquid defined for biome, use it.
+       // If defined liquid is "air", disable 'flooded' to avoid placing "air".
+       use_biome_liquid = false;
+       if (flooded && bmgn) {
+               v3s16 midp = node_min + (node_max - node_min) / v3s16(2, 2, 2);
+               Biome *biome = (Biome *)bmgn->getBiomeAtPoint(midp);
+               if (biome->c_cave_liquid[0] != CONTENT_IGNORE) {
+                       use_biome_liquid = true;
+                       c_biome_liquid =
+                               biome->c_cave_liquid[ps->range(0, biome->c_cave_liquid.size() - 1)];
+                       if (c_biome_liquid == CONTENT_AIR)
+                               flooded = false;
+               }
+       }
+
        // Set initial parameters from randomness
        int dswitchint = ps->range(1, 14);
-       flooded = ps->range(1, 2) == 2;
 
        if (large_cave) {
                part_max_length_rs = ps->range(2, 4);
@@ -502,31 +519,19 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
        fp.Z += 0.1f * ps->range(-10, 10);
        v3s16 cp(fp.X, fp.Y, fp.Z);
 
-       // Get biome at 'cp + of', the absolute centre point of this route
-       v3s16 cpabs = cp + of;
+       // Choose cave liquid
        MapNode liquidnode = CONTENT_IGNORE;
 
-       if (bmgn) {
-               Biome *biome = nullptr;
-               if (cpabs.X < node_min.X || cpabs.X > node_max.X ||
-                               cpabs.Z < node_min.Z || cpabs.Z > node_max.Z)
-                       // Point is outside heat and humidity noise maps so use point noise
-                       // calculations.
-                       biome = (Biome *)bmgn->calcBiomeAtPoint(cpabs);
-               else
-                       // Point is inside heat and humidity noise maps so use them
-                       biome = (Biome *)bmgn->getBiomeAtPoint(cpabs);
-
-               if (biome->c_cave_liquid != CONTENT_IGNORE)
-                       liquidnode = biome->c_cave_liquid;
-       }
-
-       if (liquidnode == CONTENT_IGNORE) {
-               // Fallback to classic behaviour using point 'startp'
-               float nval = NoisePerlin3D(np_caveliquids, startp.X,
-                       startp.Y, startp.Z, seed);
-               liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
-                       lavanode : waternode;
+       if (flooded) {
+               if (use_biome_liquid) {
+                       liquidnode = c_biome_liquid;
+               } else {
+                       // If cave liquid not defined by biome, fallback to old hardcoded behaviour
+                       float nval = NoisePerlin3D(np_caveliquids, startp.X,
+                               startp.Y, startp.Z, seed);
+                       liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
+                               lavanode : waternode;
+               }
        }
 
        s16 d0 = -rs / 2;
index 7b7be62194f8a0aaf54d824d57f77dc110157bbd..f5234a671379ff6c404e85b8a861c1b7faf1f833 100644 (file)
@@ -133,6 +133,7 @@ public:
        bool large_cave;
        bool large_cave_is_flat;
        bool flooded;
+       bool use_biome_liquid;
 
        v3s16 node_min;
        v3s16 node_max;
@@ -150,6 +151,7 @@ public:
 
        content_t c_water_source;
        content_t c_lava_source;
+       content_t c_biome_liquid;
 
        // ndef is a mandatory parameter.
        // If gennotify is NULL, generation events are not logged.
index 7f717011c307a2fdf8fc1f5694a797581ef611e8..345bc8c6a1606e50edc50cc24cec4f9560e66890 100644 (file)
@@ -63,6 +63,7 @@ BiomeManager::BiomeManager(Server *server) :
        b->m_nodenames.emplace_back("mapgen_stone");
        b->m_nodenames.emplace_back("ignore");
        b->m_nodenames.emplace_back("ignore");
+       b->m_nnlistsizes.push_back(1);
        b->m_nodenames.emplace_back("ignore");
        b->m_nodenames.emplace_back("ignore");
        b->m_nodenames.emplace_back("ignore");
@@ -330,7 +331,7 @@ void Biome::resolveNodeNames()
        getIdFromNrBacklog(&c_river_water,   "mapgen_river_water_source", CONTENT_AIR,    false);
        getIdFromNrBacklog(&c_riverbed,      "mapgen_stone",              CONTENT_AIR,    false);
        getIdFromNrBacklog(&c_dust,          "ignore",                    CONTENT_IGNORE, false);
-       getIdFromNrBacklog(&c_cave_liquid,   "ignore",                    CONTENT_IGNORE, false);
+       getIdsFromNrBacklog(&c_cave_liquid);
        getIdFromNrBacklog(&c_dungeon,       "ignore",                    CONTENT_IGNORE, false);
        getIdFromNrBacklog(&c_dungeon_alt,   "ignore",                    CONTENT_IGNORE, false);
        getIdFromNrBacklog(&c_dungeon_stair, "ignore",                    CONTENT_IGNORE, false);
index 1f60f7baca431919591ff1df96743d03f3cd5f1c..ee148adbc783720d076fe9e096ac0697487fd489 100644 (file)
@@ -52,7 +52,7 @@ public:
        content_t c_river_water;
        content_t c_riverbed;
        content_t c_dust;
-       content_t c_cave_liquid;
+       std::vector<content_t> c_cave_liquid;
        content_t c_dungeon;
        content_t c_dungeon_alt;
        content_t c_dungeon_stair;
index 92ed4377ea5dac20ff496afe31ab017909ef630c..cb1dc1fffbdf680de9960c31b80eb1fae561a61b 100644 (file)
@@ -405,7 +405,16 @@ Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef)
        nn.push_back(getstringfield_default(L, index, "node_river_water",   ""));
        nn.push_back(getstringfield_default(L, index, "node_riverbed",      ""));
        nn.push_back(getstringfield_default(L, index, "node_dust",          ""));
-       nn.push_back(getstringfield_default(L, index, "node_cave_liquid",   ""));
+
+       size_t nnames = getstringlistfield(L, index, "node_cave_liquid", &nn);
+       // If no cave liquids defined, set list to "ignore" to trigger old hardcoded
+       // cave liquid behaviour.
+       if (nnames == 0) {
+               nn.push_back("ignore");
+               nnames = 1;
+       }
+       b->m_nnlistsizes.push_back(nnames);
+
        nn.push_back(getstringfield_default(L, index, "node_dungeon",       ""));
        nn.push_back(getstringfield_default(L, index, "node_dungeon_alt",   ""));
        nn.push_back(getstringfield_default(L, index, "node_dungeon_stair", ""));