diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua index 4b87f041..9f6f768b 100644 --- a/res/content/base/generators/demo.files/script.lua +++ b/res/content/base/generators/demo.files/script.lua @@ -26,16 +26,26 @@ end function place_structures(x, z, w, d, seed, hmap, chunk_height) local placements = {} place_ores(placements, x, z, w, d, seed, hmap, chunk_height) + return placements +end +function place_structures_wide(x, z, w, d, seed, chunk_height) + local placements = {} if math.random() < 0.1 then -- generate caves - local sy = math.random() * (chunk_height / 2) - local ey = math.random() * (chunk_height / 2) - local sx = x + math.random() * 20 - 10 - local ex = x + math.random() * 20 - 10 - local sz = z + math.random() * 20 - 10 - local ez = z + math.random() * 20 - 10 + local sy = math.random() * (chunk_height / 6) + 80 + local ey = math.max(1, sy - chunk_height / 4) + local my = (sy + ey) / 2 + math.random() * 24 - 12 + local sx = x + math.random() * 60 - 30 + local ex = x + math.random() * 60 - 30 + local mx = (sx + ex) / 2 + math.random() * 32 - 16 + local sz = z + math.random() * 60 - 30 + local ez = z + math.random() * 60 - 30 + local mz = (sz + ez) / 2 + math.random() * 32 - 16 + local width = math.random()*3+2 table.insert(placements, - {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, math.random()*2+2}) + {":line", 0, {sx, sy, sz}, {mx, my, mz}, width}) + table.insert(placements, + {":line", 0, {mx, my, mz}, {ex, ey, ez}, width}) end return placements end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 43e041c1..7a339adf 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -135,6 +135,36 @@ public: placements.structs.emplace_back(structIndex, pos, rotation); } + PrototypePlacements placeStructuresWide( + const glm::ivec2& offset, + const glm::ivec2& size, + uint64_t seed, + uint chunkHeight + ) override { + PrototypePlacements placements {}; + + stackguard _(L); + pushenv(L, *env); + if (getfield(L, "place_structures_wide")) { + pushivec_stack(L, offset); + pushivec_stack(L, size); + pushinteger(L, seed); + pushinteger(L, chunkHeight); + if (call_nothrow(L, 6, 1)) { + int len = objlen(L, -1); + for (int i = 1; i <= len; i++) { + rawgeti(L, i); + + perform_placement(L, placements); + + pop(L); + } + pop(L); + } + } + return placements; + } + PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 8dec4fbb..e8d7adf0 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -154,6 +154,13 @@ public: uint bpd ) = 0; + virtual PrototypePlacements placeStructuresWide( + const glm::ivec2& offset, + const glm::ivec2& size, + uint64_t seed, + uint chunkHeight + ) = 0; + /// @brief Generate a list of structures placements. Structures may be /// placed to nearest chunks also (position of out area). /// @param offset position of the area diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 46009ec5..d4a6ee26 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -17,7 +17,7 @@ static debug::Logger logger("world-generator"); static inline constexpr uint MAX_PARAMETERS = 4; -static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 5; +static inline constexpr uint MAX_CHUNK_PROTOTYPE_LEVELS = 10; WorldGenerator::WorldGenerator( const GeneratorDef& def, const Content* content, uint64_t seed @@ -41,13 +41,16 @@ WorldGenerator::WorldGenerator( } prototypes[{x, z}] = generatePrototype(x, z); }); - surroundMap.setLevelCallback(2, [this](int const x, int const z) { + surroundMap.setLevelCallback(4, [this](int const x, int const z) { + generateStructuresWide(requirePrototype(x, z), x, z); + }); + surroundMap.setLevelCallback(7, [this](int const x, int const z) { generateBiomes(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(3, [this](int const x, int const z) { + surroundMap.setLevelCallback(8, [this](int const x, int const z) { generateHeightmap(requirePrototype(x, z), x, z); }); - surroundMap.setLevelCallback(4, [this](int const x, int const z) { + surroundMap.setLevelCallback(9, [this](int const x, int const z) { generateStructures(requirePrototype(x, z), x, z); }); for (int i = 0; i < def.structures.size(); i++) { @@ -183,6 +186,41 @@ void WorldGenerator::placeLine(const LinePlacement& line) { } } +void WorldGenerator::placeStructures( + const PrototypePlacements& placements, + ChunkPrototype& prototype, + int chunkX, + int chunkZ +) { + util::concat(prototype.structures, placements.structs); + for (const auto& placement : prototype.structures) { + const auto& offset = placement.position; + if (placement.structure < 0 || placement.structure >= def.structures.size()) { + logger.error() << "invalid structure index " << placement.structure; + continue; + } + placeStructure( + offset, placement.structure, placement.rotation, chunkX, chunkZ); + } + for (const auto& line : placements.lines) { + placeLine(line); + } +} + +void WorldGenerator::generateStructuresWide( + ChunkPrototype& prototype, int chunkX, int chunkZ +) { + if (prototype.level >= ChunkPrototypeLevel::WIDE_STRUCTS) { + return; + } + auto placements = def.script->placeStructuresWide( + {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, CHUNK_H + ); + placeStructures(placements, prototype, chunkX, chunkZ); + + prototype.level = ChunkPrototypeLevel::WIDE_STRUCTS; +} + void WorldGenerator::generateStructures( ChunkPrototype& prototype, int chunkX, int chunkZ ) { @@ -196,20 +234,7 @@ void WorldGenerator::generateStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, heightmap, CHUNK_H ); - util::concat(prototype.structures, placements.structs); - - for (const auto& placement : prototype.structures) { - const auto& offset = placement.position; - if (placement.structure < 0 || placement.structure >= def.structures.size()) { - logger.error() << "invalid structure index " << placement.structure; - continue; - } - placeStructure( - offset, placement.structure, placement.rotation, chunkX, chunkZ); - } - for (const auto& line : placements.lines) { - placeLine(line); - } + placeStructures(placements, prototype, chunkX, chunkZ); util::PseudoRandom structsRand; structsRand.setSeed(chunkX, chunkZ); diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 0e3f580b..59a43e1c 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -17,9 +17,10 @@ struct GeneratorDef; class Heightmap; struct Biome; class VoxelFragment; +struct PrototypePlacements; enum class ChunkPrototypeLevel { - VOID=0, BIOMES, HEIGHTMAP, STRUCTURES + VOID=0, WIDE_STRUCTS, BIOMES, HEIGHTMAP, STRUCTURES }; struct ChunkPrototype { @@ -64,6 +65,8 @@ class WorldGenerator { ChunkPrototype& requirePrototype(int x, int z); + void generateStructuresWide(ChunkPrototype& prototype, int x, int z); + void generateStructures(ChunkPrototype& prototype, int x, int z); void generateBiomes(ChunkPrototype& prototype, int x, int z); @@ -99,6 +102,11 @@ class WorldGenerator { int z, const Biome** biomes ); + + void placeStructures( + const PrototypePlacements& placements, + ChunkPrototype& prototype, + int x, int z); public: WorldGenerator( const GeneratorDef& def,