diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 7ef89090e..62f205638 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -18,7 +18,9 @@ jobs: - name: install dependencies run: | sudo apt-get update - sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev cmake squashfs-tools + sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev cmake squashfs-tools + sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a + sudo ln -s /usr/include/luajit-2.1 /usr/include/lua - name: configure run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1 - name: build diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 0d8237ff3..502dd4869 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -21,9 +21,15 @@ jobs: - uses: actions/checkout@v3 - name: Install packages + # If libluajit-5.1-dev is not available, use this: + # git clone https://luajit.org/git/luajit.git + # cd luajit + # make && make install INSTALL_INC=/usr/include/lua run: | sudo apt-get update - sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev + sudo apt-get install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev + sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua-5.1.a + sudo ln -s /usr/include/luajit-2.1 /usr/include/lua - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/CMakeLists.txt b/CMakeLists.txt index b7ed2f69e..2702c2183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,8 @@ endif() find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenAL REQUIRED) +# luajit has no CMakeLists.txt to use it as subdirectory, so install it +find_package(Lua REQUIRED) if (WIN32) set(PNGLIB spng) @@ -67,8 +69,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie") endif() - -target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ${PNGLIB}) +include_directories(${LUA_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ${PNGLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/README.md b/README.md index baeb197d8..4240d6a67 100644 --- a/README.md +++ b/README.md @@ -27,21 +27,49 @@ cmake --build . ``` ## Install libs: + #### Debian-based distro: -`$ sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev` +```sh +sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev +``` + +CMake missing LUA_INCLUDE_DIR and LUA_LIBRARIES fix: +```sh +sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a +sudo ln -s /usr/include/luajit-2.1 /usr/include/lua +``` #### RHEL-based distro: -`$ sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel openal-devel` +```sh +sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel openal-devel +``` + +\+ install LuaJIT #### Arch-based distro: If you use X11 -`$ sudo pacman -S glfw-x11 glew glm libpng openal` +```sh +sudo pacman -S glfw-x11 glew glm libpng openal +``` If you use Wayland -`$ sudo pacman -S glfw-wayland glew glm libpng openal` +```sh +sudo pacman -S glfw-wayland glew glm libpng openal +``` + +\+ install LuaJIT + +#### LuaJIT installation: +```sh +git clone https://luajit.org/git/luajit.git +cd luajit +make && sudo make install INSTALL_INC=/usr/include/lua +``` #### macOS: -`$ brew install glfw3 glew glm libpng` +``` +brew install glfw3 glew glm libpng +``` -Download, compile and install OpenAL +Download, compile and install OpenAL and LuaJIT diff --git a/res/content/base/blocks/flower.json b/res/content/base/blocks/flower.json index 054bede9b..7d2f9c73f 100644 --- a/res/content/base/blocks/flower.json +++ b/res/content/base/blocks/flower.json @@ -4,6 +4,7 @@ "light-passing": true, "obstacle": false, "replaceable": true, + "grounded": true, "model": "X", "hitbox": [0.15, 0.0, 0.15, 0.7, 0.7, 0.7] -} \ No newline at end of file +} diff --git a/res/content/base/blocks/grass.json b/res/content/base/blocks/grass.json index 944a2a41d..844d7baac 100644 --- a/res/content/base/blocks/grass.json +++ b/res/content/base/blocks/grass.json @@ -4,6 +4,7 @@ "light-passing": true, "obstacle": false, "replaceable": true, + "grounded": true, "model": "X", "hitbox": [0.15, 0.0, 0.15, 0.7, 0.7, 0.7] -} \ No newline at end of file +} diff --git a/res/content/base/scripts/grass_block.lua b/res/content/base/scripts/grass_block.lua new file mode 100644 index 000000000..f1072ab11 --- /dev/null +++ b/res/content/base/scripts/grass_block.lua @@ -0,0 +1,20 @@ +function on_random_update(x, y, z) + local dirtid = block_index('base:dirt'); + if is_solid_at(x, y+1, z) then + set_block(x, y, z, dirtid) + else + local grassblockid = block_index('base:grass_block') + for lx=-1,1 do + for ly=-1,1 do + for lz=-1,1 do + if get_block(x + lx, y + ly, z + lz) == dirtid then + if not is_solid_at(x + lx, y + ly + 1, z + lz) then + set_block(x + lx, y + ly, z + lz, grassblockid) + return + end + end + end + end + end + end +end diff --git a/res/scripts/world.lua b/res/scripts/world.lua new file mode 100644 index 000000000..e69de29bb diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index d7f0f8df6..5a10b4c40 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -12,6 +12,7 @@ #include "../typedefs.h" #include "ContentPack.h" +#include "../logic/scripting/scripting.h" namespace fs = std::filesystem; @@ -84,6 +85,7 @@ Block* ContentLoader::loadBlock(std::string name, fs::path file) { root->flag("light-passing", def->lightPassing); root->flag("breakable", def->breakable); root->flag("selectable", def->selectable); + root->flag("grounded", def->grounded); root->flag("sky-light-passing", def->skyLightPassing); root->num("draw-group", def->drawGroup); @@ -100,8 +102,14 @@ void ContentLoader::load(ContentBuilder* builder) { if (blocksarr) { for (uint i = 0; i < blocksarr->size(); i++) { std::string name = blocksarr->str(i); + std::string prefix = pack->id+":"+name; fs::path blockfile = folder/fs::path("blocks/"+name+".json"); - builder->add(loadBlock(pack->id+":"+name, blockfile)); + Block* block = loadBlock(prefix, blockfile); + builder->add(block); + fs::path scriptfile = folder/fs::path("scripts/"+name+".lua"); + if (fs::is_regular_file(scriptfile)) { + scripting::load_block_script(prefix, scriptfile, &block->rt.funcsset); + } } } } diff --git a/src/engine.cpp b/src/engine.cpp index 72fb14f56..63762f09d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -33,6 +33,7 @@ #include "content/ContentPack.h" #include "content/ContentLoader.h" #include "frontend/locale/langs.h" +#include "logic/scripting/scripting.h" #include "definitions.h" @@ -45,6 +46,7 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths) } auto resdir = paths->getResources(); + scripting::initialize(paths); std::cout << "-- loading assets" << std::endl; std::vector roots {resdir}; @@ -117,6 +119,7 @@ void Engine::mainloop() { } Engine::~Engine() { + scripting::close(); screen = nullptr; delete gui; @@ -128,40 +131,6 @@ Engine::~Engine() { std::cout << "-- engine finished" << std::endl; } -gui::GUI* Engine::getGUI() { - return gui; -} - -EngineSettings& Engine::getSettings() { - return settings; -} - -Assets* Engine::getAssets() { - return assets.get(); -} - -void Engine::setScreen(std::shared_ptr screen) { - this->screen = screen; -} - -const Content* Engine::getContent() const { - return content.get(); -} - -std::vector& Engine::getContentPacks() { - return contentPacks; -} - -EnginePaths* Engine::getPaths() { - return paths; -} - -void Engine::setLanguage(std::string locale) { - settings.ui.language = locale; - langs::setup(paths->getResources(), locale, contentPacks); - menus::create_menus(this, gui->getMenu()); -} - void Engine::loadContent() { auto resdir = paths->getResources(); ContentBuilder contentBuilder; @@ -197,3 +166,37 @@ void Engine::loadAllPacks() { contentPacks.clear(); ContentPack::scan(resdir/fs::path("content"), contentPacks); } + +void Engine::setScreen(std::shared_ptr screen) { + this->screen = screen; +} + +void Engine::setLanguage(std::string locale) { + settings.ui.language = locale; + langs::setup(paths->getResources(), locale, contentPacks); + menus::create_menus(this, gui->getMenu()); +} + +gui::GUI* Engine::getGUI() { + return gui; +} + +EngineSettings& Engine::getSettings() { + return settings; +} + +Assets* Engine::getAssets() { + return assets.get(); +} + +const Content* Engine::getContent() const { + return content.get(); +} + +std::vector& Engine::getContentPacks() { + return contentPacks; +} + +EnginePaths* Engine::getPaths() { + return paths; +} diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index a96c6de10..12195187d 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -20,9 +20,9 @@ #include #include -#include #include #include +#include const int SECTION_POSITION = 1; const int SECTION_ROTATION = 2; @@ -94,7 +94,7 @@ WorldFiles::~WorldFiles(){ } WorldRegion* WorldFiles::getRegion(regionsmap& regions, int x, int z) { - auto found = regions.find(ivec2(x, z)); + auto found = regions.find(glm::ivec2(x, z)); if (found == regions.end()) return nullptr; return found->second; @@ -104,7 +104,7 @@ WorldRegion* WorldFiles::getOrCreateRegion(regionsmap& regions, int x, int z) { WorldRegion* region = getRegion(regions, x, z); if (region == nullptr) { region = new WorldRegion(); - regions[ivec2(x, z)] = region; + regions[glm::ivec2(x, z)] = region; } return region; } @@ -367,7 +367,7 @@ void WorldFiles::writeRegions(regionsmap& regions, const fs::path& folder, int l WorldRegion* region = it.second; if (region->getChunks() == nullptr || !region->isUnsaved()) continue; - ivec2 key = it.first; + glm::ivec2 key = it.first; writeRegion(key.x, key.y, region, folder, layer); } } @@ -458,7 +458,7 @@ bool WorldFiles::readWorldInfo(World* world) { } void WorldFiles::writePlayer(Player* player){ - vec3 position = player->hitbox->position; + glm::vec3 position = player->hitbox->position; json::JObject root; json::JArray& posarr = root.putArray("position"); posarr.put(position.x); @@ -484,7 +484,7 @@ bool WorldFiles::readPlayer(Player* player) { std::unique_ptr root(files::read_json(file)); json::JArray* posarr = root->arr("position"); - vec3& position = player->hitbox->position; + glm::vec3& position = player->hitbox->position; position.x = posarr->num(0); position.y = posarr->num(1); position.z = posarr->num(2); diff --git a/src/frontend/WorldRenderer.cpp b/src/frontend/WorldRenderer.cpp index 6893ffda4..5a6fc8129 100644 --- a/src/frontend/WorldRenderer.cpp +++ b/src/frontend/WorldRenderer.cpp @@ -168,7 +168,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera){ shader->uniform3f("u_cameraPos", camera->position); shader->uniform1i("u_cubemap", 1); { - blockid_t id = level->player->choosenBlock; + blockid_t id = level->player->chosenBlock; Block* block = contentIds->getBlockDef(id); assert(block != nullptr); float multiplier = 0.5f; diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index ace6972ce..2bc92468b 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -256,7 +256,7 @@ void HudRenderer::drawContentAccess(const GfxContext& ctx, Player* player) { tint.g *= 1.2f; tint.b *= 1.2f; if (Events::jclicked(mousecode::BUTTON_1)) { - player->choosenBlock = i+1; + player->chosenBlock = i+1; } } else { tint = vec4(1.0f); @@ -335,7 +335,7 @@ void HudRenderer::draw(const GfxContext& ctx){ subctx.depthTest(true); subctx.cullFace(true); - Block* cblock = contentIds->getBlockDef(player->choosenBlock); + Block* cblock = contentIds->getBlockDef(player->chosenBlock); assert(cblock != nullptr); blocksPreview->draw(cblock, width - 56, uicamera->getFov() - 56, 48, vec4(1.0f)); //drawBlockPreview(cblock, width - 56, uicamera->fov - 56, 48, 48, vec4(1.0f)); diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp new file mode 100644 index 000000000..fb9e82e70 --- /dev/null +++ b/src/logic/BlocksController.cpp @@ -0,0 +1,120 @@ +#include "BlocksController.h" + +#include "../voxels/voxel.h" +#include "../voxels/Block.h" +#include "../voxels/Chunk.h" +#include "../voxels/Chunks.h" +#include "../world/Level.h" +#include "../content/Content.h" +#include "../lighting/Lighting.h" +#include "../util/timeutil.h" + +#include "scripting/scripting.h" + +Clock::Clock(int tickRate, int tickParts) + : tickRate(tickRate), + tickParts(tickParts) { +} + +bool Clock::update(float delta) { + tickTimer += delta; + float delay = 1.0f / float(tickRate); + if (tickTimer > delay || tickPartsUndone) { + if (tickPartsUndone) { + tickPartsUndone--; + } else { + tickTimer = fmod(tickTimer, delay); + tickPartsUndone = tickParts-1; + } + return true; + } + return false; +} + +int Clock::getParts() const { + return tickParts; +} + +int Clock::getPart() const { + return tickParts-tickPartsUndone-1; +} + +BlocksController::BlocksController(Level* level, uint padding) + : level(level), + chunks(level->chunks), + lighting(level->lighting), + randTickClock(20, 3), + padding(padding) { +} + +void BlocksController::updateSides(int x, int y, int z) { + updateBlock(x-1, y, z); + updateBlock(x+1, y, z); + updateBlock(x, y-1, z); + updateBlock(x, y+1, z); + updateBlock(x, y, z-1); + updateBlock(x, y, z+1); +} + +void BlocksController::breakBlock(Player* player, const Block* def, int x, int y, int z) { + chunks->set(x,y,z, 0, 0); + lighting->onBlockSet(x,y,z, 0); + if (def->rt.funcsset.onbroken) { + scripting::on_block_broken(player, def, x, y, z); + } + updateSides(x, y, z); +} + +void BlocksController::updateBlock(int x, int y, int z) { + voxel* vox = chunks->get(x, y, z); + if (vox == nullptr) + return; + const Block* def = level->content->indices->getBlockDef(vox->id); + if (def->grounded && !chunks->isSolid(x, y-1, z)) { + breakBlock(nullptr, def, x, y, z); + return; + } + if (def->rt.funcsset.update) { + scripting::update_block(def, x, y, z); + } +} + +void BlocksController::update(float delta) { + if (randTickClock.update(delta)) { + randomTick(randTickClock.getPart(), randTickClock.getParts()); + } +} + +void BlocksController::randomTick(int tickid, int parts) { + // timeutil::ScopeLogTimer timer(5000+tickid); + const int w = chunks->w; + const int d = chunks->d; + auto indices = level->content->indices; + for (uint z = padding; z < d-padding; z++){ + for (uint x = padding; x < w-padding; x++){ + int index = z * w + x; + if ((index + tickid) % parts != 0) + continue; + std::shared_ptr chunk = chunks->chunks[index]; + if (chunk == nullptr || !chunk->isLighted()) + continue; + int segments = 4; + int segheight = CHUNK_H / segments; + for (int s = 0; s < segments; s++) { + for (int i = 0; i < 3; i++) { + int bx = rand() % CHUNK_W; + int by = rand() % segheight + s * segheight; + int bz = rand() % CHUNK_D; + const voxel& vox = chunk->voxels[(by * CHUNK_D + bz) * CHUNK_W + bx]; + Block* block = indices->getBlockDef(vox.id); + if (block->rt.funcsset.randupdate) { + scripting::random_update_block( + block, + chunk->x * CHUNK_W + bx, by, + chunk->z * CHUNK_D + bz); + } + } + } + } + } +} diff --git a/src/logic/BlocksController.h b/src/logic/BlocksController.h new file mode 100644 index 000000000..454bd16bb --- /dev/null +++ b/src/logic/BlocksController.h @@ -0,0 +1,46 @@ +#ifndef LOGIC_BLOCKS_CONTROLLER_H_ +#define LOGIC_BLOCKS_CONTROLLER_H_ + +#include "../typedefs.h" + +class Player; +class Block; +class Level; +class Chunks; +class Lighting; + +class Clock { + int tickRate; + int tickParts; + + float tickTimer = 0.0f; + int tickId = 0; + int tickPartsUndone = 0; +public: + Clock(int tickRate, int tickParts); + + bool update(float delta); + + int getParts() const; + int getPart() const; +}; + +class BlocksController { + Level* level; + Chunks* chunks; + Lighting* lighting; + Clock randTickClock; + uint padding; +public: + BlocksController(Level* level, uint padding); + + void updateSides(int x, int y, int z); + void updateBlock(int x, int y, int z); + + void breakBlock(Player* player, const Block* def, int x, int y, int z); + + void update(float delta); + void randomTick(int tickid, int parts); +}; + +#endif // LOGIC_BLOCKS_CONTROLLER_H_ diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 1d3546085..c3d28ead5 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -2,13 +2,13 @@ #include #include +#include #include "../voxels/Block.h" #include "../voxels/Chunk.h" #include "../voxels/Chunks.h" #include "../voxels/ChunksStorage.h" #include "../voxels/WorldGenerator.h" -#include "../content/Content.h" #include "../graphics/Mesh.h" #include "../lighting/Lighting.h" #include "../files/WorldFiles.h" @@ -17,19 +17,14 @@ #include "../maths/voxmaths.h" #include "../util/timeutil.h" + const uint MAX_WORK_PER_FRAME = 64; const uint MIN_SURROUNDING = 9; -using std::unique_ptr; -using std::shared_ptr; - -ChunksController::ChunksController(Level* level, - Chunks* chunks, - Lighting* lighting, - uint padding) +ChunksController::ChunksController(Level* level, uint padding) : level(level), - chunks(chunks), - lighting(lighting), + chunks(level->chunks), + lighting(level->lighting), padding(padding), generator(new WorldGenerator(level->content)) { } @@ -66,7 +61,7 @@ bool ChunksController::loadVisible(){ for (uint z = padding; z < d-padding; z++){ for (uint x = padding; x < w-padding; x++){ int index = z * w + x; - shared_ptr chunk = chunks->chunks[index]; + std::shared_ptr chunk = chunks->chunks[index]; if (chunk != nullptr){ int surrounding = 0; for (int oz = -1; oz <= 1; oz++){ @@ -98,7 +93,7 @@ bool ChunksController::loadVisible(){ } int index = nearZ * w + nearX; - shared_ptr chunk = chunks->chunks[index]; + std::shared_ptr chunk = chunks->chunks[index]; if (chunk != nullptr) { return false; } diff --git a/src/logic/ChunksController.h b/src/logic/ChunksController.h index af6461630..a9fa0fa63 100644 --- a/src/logic/ChunksController.h +++ b/src/logic/ChunksController.h @@ -6,9 +6,6 @@ class Level; class Chunks; class Lighting; -class WorldFiles; -class VoxelRenderer; -class ChunksLoader; class WorldGenerator; /* ChunksController manages chunks dynamic loading/unloading */ @@ -26,7 +23,7 @@ class ChunksController { /* Process one chunk: load it or calculate lights for it */ bool loadVisible(); public: - ChunksController(Level* level, Chunks* chunks, Lighting* lighting, uint padding); + ChunksController(Level* level, uint padding); ~ChunksController(); /* @param maxDuration milliseconds reserved for chunks loading */ diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 05c134b60..2ab74f702 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -2,28 +2,29 @@ #include "../world/Level.h" #include "PlayerController.h" +#include "BlocksController.h" #include "ChunksController.h" +#include "scripting/scripting.h" + LevelController::LevelController(EngineSettings& settings, Level* level) : settings(settings), level(level) { - chunks = new ChunksController( - level, - level->chunks, - level->lighting, - settings.chunks.padding); - player = new PlayerController(level, settings); + blocks = new BlocksController(level, settings.chunks.padding); + chunks = new ChunksController(level, settings.chunks.padding); + player = new PlayerController(level, settings, blocks); + + scripting::on_world_load(level); } LevelController::~LevelController() { + scripting::on_world_quit(); delete player; delete chunks; } -void LevelController::update( - float delta, - bool input, - bool pause) { +void LevelController::update(float delta, bool input, bool pause) { player->update(delta, input, pause); level->update(); chunks->update(settings.chunks.loadSpeed); -} \ No newline at end of file + blocks->update(delta); +} diff --git a/src/logic/LevelController.h b/src/logic/LevelController.h index a889cd036..f319d5541 100644 --- a/src/logic/LevelController.h +++ b/src/logic/LevelController.h @@ -4,6 +4,7 @@ #include "../settings.h" class Level; +class BlocksController; class ChunksController; class PlayerController; @@ -12,6 +13,7 @@ class LevelController { EngineSettings& settings; Level* level; // Sub-controllers + BlocksController* blocks; ChunksController* chunks; PlayerController* player; public: diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index d0caf2ef5..ba243237d 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -12,6 +12,8 @@ #include "../window/Camera.h" #include "../window/Events.h" #include "../window/input.h" +#include "scripting/scripting.h" +#include "BlocksController.h" #include "../core_defs.h" @@ -25,9 +27,6 @@ const float RUN_ZOOM = 1.1f; const float C_ZOOM = 0.1f; const float CROUCH_SHIFT_Y = -0.2f; -using glm::vec2; -using glm::vec3; -using std::string; CameraControl::CameraControl(Player* player, const CameraSettings& settings) : player(player), @@ -55,27 +54,27 @@ void CameraControl::updateMouse(PlayerInput& input) { float& camY = player->camY; camX += rotX; camY += rotY; - if (camY < -radians(89.9f)){ - camY = -radians(89.9f); + if (camY < -glm::radians(89.9f)){ + camY = -glm::radians(89.9f); } - if (camY > radians(89.9f)){ - camY = radians(89.9f); + if (camY > glm::radians(89.9f)){ + camY = glm::radians(89.9f); } - camera->rotation = mat4(1.0f); + camera->rotation = glm::mat4(1.0f); camera->rotate(camY, camX, 0); } void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { Hitbox* hitbox = player->hitbox; - offset = vec3(0.0f, 0.7f, 0.0f); + offset = glm::vec3(0.0f, 0.7f, 0.0f); if (settings.shaking && !input.cheat) { const float k = CAM_SHAKE_DELTA_K; const float oh = CAM_SHAKE_OFFSET; const float ov = CAM_SHAKE_OFFSET_Y; - const vec3& vel = hitbox->velocity; + const glm::vec3& vel = hitbox->velocity; interpVel = interpVel * (1.0f - delta * 5) + vel * delta * 0.1f; if (hitbox->grounded && interpVel.y < 0.0f){ @@ -83,12 +82,12 @@ void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { } shake = shake * (1.0f - delta * k); if (hitbox->grounded) { - float f = length(vec2(vel.x, vel.z)); + float f = glm::length(glm::vec2(vel.x, vel.z)); shakeTimer += delta * f * CAM_SHAKE_SPEED; shake += f * delta * k; } - offset += camera->right * sin(shakeTimer) * oh * shake; - offset += camera->up * abs(cos(shakeTimer)) * ov * shake; + offset += camera->right * glm::sin(shakeTimer) * oh * shake; + offset += camera->up * glm::abs(glm::cos(shakeTimer)) * ov * shake; offset -= glm::min(interpVel * 0.05f, 1.0f); } @@ -98,7 +97,7 @@ void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { float dt = fmin(1.0f, delta * ZOOM_SPEED); float zoomValue = 1.0f; if (crouch){ - offset += vec3(0.f, CROUCH_SHIFT_Y, 0.f); + offset += glm::vec3(0.f, CROUCH_SHIFT_Y, 0.f); zoomValue = CROUCH_ZOOM; } else if (input.sprint){ zoomValue = RUN_ZOOM; @@ -128,16 +127,20 @@ void CameraControl::update(PlayerInput& input, float delta, Chunks* chunks) { } } -vec3 PlayerController::selectedBlockPosition; -vec3 PlayerController::selectedPointPosition; -ivec3 PlayerController::selectedBlockNormal; +glm::vec3 PlayerController::selectedBlockPosition; +glm::vec3 PlayerController::selectedPointPosition; +glm::ivec3 PlayerController::selectedBlockNormal; int PlayerController::selectedBlockId = -1; int PlayerController::selectedBlockStates = 0; -PlayerController::PlayerController(Level* level, const EngineSettings& settings) +PlayerController::PlayerController( + Level* level, + const EngineSettings& settings, + BlocksController* blocksController) : level(level), player(level->player), - camControl(level->player, settings.camera) { + camControl(level->player, settings.camera), + blocksController(blocksController) { } void PlayerController::update(float delta, bool input, bool pause) { @@ -177,7 +180,7 @@ void PlayerController::updateKeyboard() { // block choice for (int i = 1; i < 10; i++){ if (Events::jpressed(keycode::NUM_0+i)){ - player->choosenBlock = i; + player->chosenBlock = i; } } } @@ -211,9 +214,9 @@ void PlayerController::updateInteraction(){ Player* player = level->player; Lighting* lighting = level->lighting; Camera* camera = player->camera; - vec3 end; - ivec3 iend; - ivec3 norm; + glm::vec3 end; + glm::ivec3 iend; + glm::ivec3 norm; bool xkey = Events::pressed(keycode::X); bool lclick = Events::jactive(BIND_PLAYER_ATTACK) || @@ -241,9 +244,9 @@ void PlayerController::updateInteraction(){ int z = iend.z; uint8_t states = 0; - Block* def = contentIds->getBlockDef(player->choosenBlock); + Block* def = contentIds->getBlockDef(player->chosenBlock); if (def->rotatable){ - const string& name = def->rotations.name; + const std::string& name = def->rotations.name; if (name == "pipe") { if (norm.x < 0.0f) states = BLOCK_DIR_WEST; else if (norm.x > 0.0f) states = BLOCK_DIR_EAST; @@ -252,7 +255,7 @@ void PlayerController::updateInteraction(){ else if (norm.z > 0.0f) states = BLOCK_DIR_NORTH; else if (norm.z < 0.0f) states = BLOCK_DIR_SOUTH; } else if (name == "pane") { - vec3 vec = camera->dir; + glm::vec3 vec = camera->dir; if (abs(vec.x) > abs(vec.z)){ if (vec.x > 0.0f) states = BLOCK_DIR_EAST; if (vec.x < 0.0f) states = BLOCK_DIR_WEST; @@ -266,8 +269,7 @@ void PlayerController::updateInteraction(){ Block* block = contentIds->getBlockDef(vox->id); if (lclick && block->breakable){ - chunks->set(x,y,z, 0, 0); - lighting->onBlockSet(x,y,z, 0); + blocksController->breakBlock(player, block, x, y, z); } if (rclick){ if (block->model != BlockModel::xsprite){ @@ -276,19 +278,30 @@ void PlayerController::updateInteraction(){ z = (iend.z)+(norm.z); } vox = chunks->get(x, y, z); + int chosenBlock = player->chosenBlock; if (vox && (block = contentIds->getBlockDef(vox->id))->replaceable) { if (!level->physics->isBlockInside(x,y,z, player->hitbox) || !def->obstacle){ - chunks->set(x, y, z, player->choosenBlock, states); - lighting->onBlockSet(x,y,z, player->choosenBlock); + Block* def = contentIds->getBlockDef(chosenBlock); + if (def->grounded && !chunks->isSolid(x, y-1, z)) { + chosenBlock = 0; + } + if (chosenBlock != vox->id) { + chunks->set(x, y, z, chosenBlock, states); + lighting->onBlockSet(x,y,z, chosenBlock); + if (def->rt.funcsset.onplaced) { + scripting::on_block_placed(player, def, x, y, z); + } + blocksController->updateSides(x, y, z); + } } } } if (Events::jactive(BIND_PLAYER_PICK)){ - player->choosenBlock = chunks->get(x,y,z)->id; + player->chosenBlock = chunks->get(x,y,z)->id; } } else { selectedBlockId = -1; selectedBlockStates = 0; } -} \ No newline at end of file +} diff --git a/src/logic/PlayerController.h b/src/logic/PlayerController.h index 6893382b1..ffab1c56c 100644 --- a/src/logic/PlayerController.h +++ b/src/logic/PlayerController.h @@ -8,6 +8,7 @@ class Camera; class Level; +class BlocksController; class CameraControl { Player* player; @@ -29,6 +30,7 @@ class PlayerController { Player* player; PlayerInput input; CameraControl camControl; + BlocksController* blocksController; void updateKeyboard(); void updateCamera(float delta, bool movement); @@ -42,7 +44,9 @@ class PlayerController { static int selectedBlockId; static int selectedBlockStates; - PlayerController(Level* level, const EngineSettings& settings); + PlayerController(Level* level, + const EngineSettings& settings, + BlocksController* blocksController); void update(float delta, bool input, bool pause); }; diff --git a/src/logic/scripting/api_lua.cpp b/src/logic/scripting/api_lua.cpp new file mode 100644 index 000000000..766bc7d0b --- /dev/null +++ b/src/logic/scripting/api_lua.cpp @@ -0,0 +1,65 @@ +#include "api_lua.h" +#include "scripting.h" + +#include "../../world/Level.h" +#include "../../content/Content.h" +#include "../../voxels/Block.h" +#include "../../voxels/Chunks.h" +#include "../../voxels/voxel.h" + +int l_block_name(lua_State* L) { + int id = lua_tointeger(L, 1); + lua_pushstring(L, scripting::content->indices->getBlockDef(id)->name.c_str()); + return 1; +} + +int l_is_solid_at(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + + lua_pushboolean(L, scripting::level->chunks->isSolid(x, y, z)); + return 1; +} + +int l_blocks_count(lua_State* L) { + lua_pushinteger(L, scripting::content->indices->countBlockDefs()); + return 1; +} + +int l_block_index(lua_State* L) { + auto name = lua_tostring(L, 1); + lua_pushinteger(L, scripting::content->requireBlock(name)->rt.id); + return 1; +} + +int l_set_block(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + int id = lua_tointeger(L, 4); + scripting::level->chunks->set(x, y, z, id, 0); + return 0; +} + +int l_get_block(lua_State* L) { + int x = lua_tointeger(L, 1); + int y = lua_tointeger(L, 2); + int z = lua_tointeger(L, 3); + voxel* vox = scripting::level->chunks->get(x, y, z); + int id = vox == nullptr ? -1 : vox->id; + lua_pushinteger(L, id); + return 1; +} + +#define lua_addfunc(L, FUNC, NAME) (lua_pushcfunction(L, FUNC),\ + lua_setglobal(L, NAME)) + +void apilua::create_funcs(lua_State* L) { + lua_addfunc(L, l_block_index, "block_index"); + lua_addfunc(L, l_block_name, "block_name"); + lua_addfunc(L, l_blocks_count, "blocks_count"); + lua_addfunc(L, l_is_solid_at, "is_solid_at"); + lua_addfunc(L, l_set_block, "set_block"); + lua_addfunc(L, l_get_block, "get_block"); +} diff --git a/src/logic/scripting/api_lua.h b/src/logic/scripting/api_lua.h new file mode 100644 index 000000000..e48a6548e --- /dev/null +++ b/src/logic/scripting/api_lua.h @@ -0,0 +1,14 @@ +#ifndef LOGIC_SCRIPTING_API_LUA_H_ +#define LOGIC_SCRIPTING_API_LUA_H_ + +#include + +#ifndef LUAJIT_VERSION +#pragma message("better use LuaJIT instead of plain Lua") +#endif // LUAJIT_VERSION + +namespace apilua { + extern void create_funcs(lua_State* L); +} + +#endif // LOGIC_SCRIPTING_API_LUA_H_ diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp new file mode 100644 index 000000000..ed0b19e08 --- /dev/null +++ b/src/logic/scripting/scripting.cpp @@ -0,0 +1,140 @@ +#include "scripting.h" + +#include +#include +#include + +#include "../../files/engine_paths.h" +#include "../../files/files.h" +#include "../../util/timeutil.h" +#include "../../world/Level.h" +#include "../../voxels/Block.h" +#include "api_lua.h" + +using namespace scripting; + +namespace scripting { + extern lua_State* L; + extern EnginePaths* paths; +} + +lua_State* scripting::L = nullptr; +Level* scripting::level = nullptr; +const Content* scripting::content = nullptr; +EnginePaths* scripting::paths = nullptr; + +void delete_global(lua_State* L, const char* name) { + lua_pushnil(L); + lua_setglobal(L, name); +} + +bool rename_global(lua_State* L, const char* src, const char* dst) { + lua_getglobal(L, src); + if (lua_isnil(L, lua_gettop(L))) { + lua_pop(L, lua_gettop(L)); + return false; + } + lua_setglobal(L, dst); + delete_global(L, src); + return true; +} + +void call_func(lua_State* L, int argc, const std::string& name) { + if (lua_pcall(L, argc, LUA_MULTRET, 0)) { + std::cerr << "Lua error in " << name << ": "; + std::cerr << lua_tostring(L,-1) << std::endl; + } +} + +void scripting::initialize(EnginePaths* paths) { + scripting::paths = paths; + + L = luaL_newstate(); + if (L == nullptr) { + throw std::runtime_error("could not to initialize Lua"); + } + luaopen_base(L); + luaopen_math(L); + + std::cout << LUA_VERSION << std::endl; +# ifdef LUAJIT_VERSION + luaopen_jit(L); + std::cout << LUAJIT_VERSION << std::endl; +# endif // LUAJIT_VERSION + + apilua::create_funcs(L); +} + +void scripting::on_world_load(Level* level) { + scripting::level = level; + scripting::content = level->content; + + fs::path file = paths->getResources()/fs::path("scripts/world.lua"); + std::string src = files::read_string(file); + luaL_loadbuffer(L, src.c_str(), src.length(), file.string().c_str()); + call_func(L, 0, "