diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index d1a94118..e8db021b 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -3,6 +3,7 @@ #include #include +#include "scripting_commons.hpp" #include "content/Content.hpp" #include "content/ContentPack.hpp" #include "debug/Logger.hpp" @@ -24,7 +25,6 @@ #include "util/timeutil.hpp" #include "voxels/Block.hpp" #include "world/Level.hpp" -#include "world/generator/GeneratorDef.hpp" using namespace scripting; @@ -39,7 +39,7 @@ const ContentIndices* scripting::indices = nullptr; BlocksController* scripting::blocks = nullptr; LevelController* scripting::controller = nullptr; -static void load_script(const fs::path& name, bool throwable) { +void scripting::load_script(const fs::path& name, bool throwable) { auto paths = scripting::engine->getPaths(); fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name; std::string src = files::read_string(file); @@ -52,6 +52,14 @@ static void load_script(const fs::path& name, bool throwable) { } } +int scripting::load_script( + int env, const std::string& type, const fs::path& file +) { + std::string src = files::read_string(file); + logger.info() << "script (" << type << ") " << file.u8string(); + return lua::execute(lua::get_main_thread(), env, src, file.u8string()); +} + void scripting::initialize(Engine* engine) { scripting::engine = engine; lua::initialize(); @@ -630,15 +638,6 @@ int scripting::get_values_on_stack() { return lua::gettop(lua::get_main_thread()); } -[[nodiscard]] -static int load_script( - int env, const std::string& type, const fs::path& file -) { - std::string src = files::read_string(file); - logger.info() << "script (" << type << ") " << file.u8string(); - return lua::execute(lua::get_main_thread(), env, src, file.u8string()); -} - void scripting::load_block_script( const scriptenv& senv, const std::string& prefix, @@ -685,155 +684,6 @@ void scripting::load_entity_component( lua::store_in(L, lua::CHUNKS_TABLE, name); } -class LuaGeneratorScript : public GeneratorScript { - scriptenv env; - BlocksLayers groundLayers; - BlocksLayers seaLayers; - uint seaLevel; -public: - LuaGeneratorScript( - scriptenv env, - BlocksLayers groundLayers, - BlocksLayers seaLayers, - uint seaLevel) - : env(std::move(env)), - groundLayers(std::move(groundLayers)), - seaLayers(std::move(seaLayers)), - seaLevel(seaLevel) - {} - - std::shared_ptr generateHeightmap( - const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed - ) override { - auto L = lua::get_main_thread(); - lua::pushenv(L, *env); - if (lua::getfield(L, "generate_heightmap")) { - lua::pushivec_stack(L, offset); - lua::pushivec_stack(L, size); - lua::pushinteger(L, seed); - if (lua::call_nothrow(L, 5)) { - auto map = lua::touserdata(L, -1)->getHeightmap(); - lua::pop(L, 2); - return map; - } - } - lua::pop(L); - return std::make_shared(size.x, size.y); - } - - void prepare(const Content* content) override { - for (auto& layer : groundLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - for (auto& layer : seaLayers.layers) { - layer.rt.id = content->blocks.require(layer.block).rt.id; - } - } - - const BlocksLayers& getGroundLayers() const override { - return groundLayers; - } - - const BlocksLayers& getSeaLayers() const override { - return seaLayers; - } - - uint getSeaLevel() const override { - return seaLevel; - } -}; - -static BlocksLayer load_layer( - lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer -) { - lua::requirefield(L, "block"); - auto name = lua::require_string(L, -1); - lua::pop(L); - lua::requirefield(L, "height"); - int height = lua::tointeger(L, -1); - lua::pop(L); - bool belowSeaLevel = true; - if (lua::getfield(L, "below_sea_level")) { - belowSeaLevel = lua::toboolean(L, -1); - lua::pop(L); - } - - if (hasResizeableLayer) { - lastLayersHeight += height; - } - if (height == -1) { - if (hasResizeableLayer) { - throw std::runtime_error("only one resizeable layer allowed"); - } - hasResizeableLayer = true; - } - return BlocksLayer {name, height, belowSeaLevel, {}}; -} - -static inline BlocksLayers load_layers( - lua::State* L, const std::string& fieldname -) { - uint lastLayersHeight = 0; - bool hasResizeableLayer = false; - std::vector layers; - - if (lua::getfield(L, fieldname)) { - int len = lua::objlen(L, -1); - for (int i = 1; i <= len; i++) { - lua::rawgeti(L, i); - try { - layers.push_back( - load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); - } catch (const std::runtime_error& err) { - lua::pop(L, 2); - throw std::runtime_error( - fieldname+" #"+std::to_string(i)+": "+err.what()); - } - lua::pop(L); - } - lua::pop(L); - } - return BlocksLayers {std::move(layers), lastLayersHeight}; -} - -std::unique_ptr scripting::load_generator( - const fs::path& file -) { - auto env = create_environment(); - auto L = lua::get_main_thread(); - - lua::pop(L, load_script(*env, "generator", file)); - - lua::pushenv(L, *env); - - uint seaLevel = 0; - if (lua::getfield(L, "sea_level")) { - seaLevel = lua::tointeger(L, -1); - lua::pop(L); - } - - uint lastGroundLayersHeight = 0; - uint lastSeaLayersHeight = 0; - bool hasResizeableGroundLayer = false; - bool hasResizeableSeaLayer = false; - - BlocksLayers groundLayers; - BlocksLayers seaLayers; - try { - groundLayers = load_layers(L, "layers"); - seaLayers = load_layers(L, "sea_layers"); - } catch (const std::runtime_error& err) { - lua::pop(L); - throw std::runtime_error(file.u8string()+": "+err.what()); - } - lua::pop(L); - return std::make_unique( - std::move(env), - std::move(groundLayers), - std::move(seaLayers), - seaLevel); -} - void scripting::load_world_script( const scriptenv& senv, const std::string& prefix, diff --git a/src/logic/scripting/scripting_commons.hpp b/src/logic/scripting/scripting_commons.hpp new file mode 100644 index 00000000..93e05ca7 --- /dev/null +++ b/src/logic/scripting/scripting_commons.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace scripting { + void load_script(const std::filesystem::path& name, bool throwable); + + [[nodiscard]] + int load_script(int env, const std::string& type, const std::filesystem::path& file); +} diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp new file mode 100644 index 00000000..712aaa13 --- /dev/null +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -0,0 +1,150 @@ +#include "scripting.hpp" + +#include "scripting_commons.hpp" +#include "typedefs.hpp" +#include "lua/lua_engine.hpp" +#include "lua/lua_custom_types.hpp" +#include "content/Content.hpp" +#include "voxels/Block.hpp" +#include "world/generator/GeneratorDef.hpp" + +class LuaGeneratorScript : public GeneratorScript { + scriptenv env; + Biome biome; + uint seaLevel; +public: + LuaGeneratorScript( + scriptenv env, + Biome biome, + uint seaLevel) + : env(std::move(env)), + biome(std::move(biome)), + seaLevel(seaLevel) + {} + + std::shared_ptr generateHeightmap( + const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed + ) override { + auto L = lua::get_main_thread(); + lua::pushenv(L, *env); + if (lua::getfield(L, "generate_heightmap")) { + lua::pushivec_stack(L, offset); + lua::pushivec_stack(L, size); + lua::pushinteger(L, seed); + if (lua::call_nothrow(L, 5)) { + auto map = lua::touserdata(L, -1)->getHeightmap(); + lua::pop(L, 2); + return map; + } + } + lua::pop(L); + return std::make_shared(size.x, size.y); + } + + void prepare(const Content* content) override { + for (auto& layer : biome.groundLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + for (auto& layer : biome.seaLayers.layers) { + layer.rt.id = content->blocks.require(layer.block).rt.id; + } + } + + const Biome& getBiome() const override { + return biome; + } + + uint getSeaLevel() const override { + return seaLevel; + } +}; + +static BlocksLayer load_layer( + lua::State* L, int idx, uint& lastLayersHeight, bool& hasResizeableLayer +) { + lua::requirefield(L, "block"); + auto name = lua::require_string(L, -1); + lua::pop(L); + lua::requirefield(L, "height"); + int height = lua::tointeger(L, -1); + lua::pop(L); + bool belowSeaLevel = true; + if (lua::getfield(L, "below_sea_level")) { + belowSeaLevel = lua::toboolean(L, -1); + lua::pop(L); + } + + if (hasResizeableLayer) { + lastLayersHeight += height; + } + if (height == -1) { + if (hasResizeableLayer) { + throw std::runtime_error("only one resizeable layer allowed"); + } + hasResizeableLayer = true; + } + return BlocksLayer {name, height, belowSeaLevel, {}}; +} + +static inline BlocksLayers load_layers( + lua::State* L, const std::string& fieldname +) { + uint lastLayersHeight = 0; + bool hasResizeableLayer = false; + std::vector layers; + + if (lua::getfield(L, fieldname)) { + int len = lua::objlen(L, -1); + for (int i = 1; i <= len; i++) { + lua::rawgeti(L, i); + try { + layers.push_back( + load_layer(L, -1, lastLayersHeight, hasResizeableLayer)); + } catch (const std::runtime_error& err) { + lua::pop(L, 2); + throw std::runtime_error( + fieldname+" #"+std::to_string(i)+": "+err.what()); + } + lua::pop(L); + } + lua::pop(L); + } + return BlocksLayers {std::move(layers), lastLayersHeight}; +} + +std::unique_ptr scripting::load_generator( + const fs::path& file +) { + auto env = create_environment(); + auto L = lua::get_main_thread(); + + lua::pop(L, load_script(*env, "generator", file)); + + lua::pushenv(L, *env); + + uint seaLevel = 0; + if (lua::getfield(L, "sea_level")) { + seaLevel = lua::tointeger(L, -1); + lua::pop(L); + } + + uint lastGroundLayersHeight = 0; + uint lastSeaLayersHeight = 0; + bool hasResizeableGroundLayer = false; + bool hasResizeableSeaLayer = false; + + BlocksLayers groundLayers; + BlocksLayers seaLayers; + try { + groundLayers = load_layers(L, "layers"); + seaLayers = load_layers(L, "sea_layers"); + } catch (const std::runtime_error& err) { + lua::pop(L); + throw std::runtime_error(file.u8string()+": "+err.what()); + } + lua::pop(L); + return std::make_unique( + std::move(env), + Biome {"default", std::move(groundLayers), std::move(seaLayers)}, + seaLevel); +} diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 649d3548..356d876d 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -33,6 +33,12 @@ struct BlocksLayers { uint lastLayersHeight; }; +struct Biome { + std::string name; + BlocksLayers groundLayers; + BlocksLayers seaLayers; +}; + /// @brief Generator behaviour and settings interface class GeneratorScript { public: @@ -46,8 +52,7 @@ public: virtual std::shared_ptr generateHeightmap( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed) = 0; - virtual const BlocksLayers& getGroundLayers() const = 0; - virtual const BlocksLayers& getSeaLayers() const = 0; + virtual const Biome& getBiome() const = 0; virtual uint getSeaLevel() const = 0; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index a8cd1763..c506dc9a 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -56,8 +56,9 @@ void WorldGenerator::generate( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed ); auto values = heightmap->getValues(); - const auto& groundLayers = def.script->getGroundLayers(); - const auto& seaLayers = def.script->getSeaLayers(); + const auto& biome = def.script->getBiome(); + const auto& groundLayers = biome.groundLayers; + const auto& seaLayers = biome.seaLayers; uint seaLevel = def.script->getSeaLevel();