Scripting WIP (#70)

* Scripting introduced

* AppImage workflow fixes

* AppImage workflow simplified

* README.md update

* README.md update

* small fix
This commit is contained in:
MihailRis 2023-12-25 05:26:03 +03:00 committed by GitHub
parent ce26f0c227
commit acce49f188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 649 additions and 138 deletions

View File

@ -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

View File

@ -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.

View File

@ -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})

View File

@ -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

View File

@ -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]
}
}

View File

@ -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]
}
}

View File

@ -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

0
res/scripts/world.lua Normal file
View File

View File

@ -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);
}
}
}
}

View File

@ -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<fs::path> 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> screen) {
this->screen = screen;
}
const Content* Engine::getContent() const {
return content.get();
}
std::vector<ContentPack>& 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> 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<ContentPack>& Engine::getContentPacks() {
return contentPacks;
}
EnginePaths* Engine::getPaths() {
return paths;
}

View File

@ -20,9 +20,9 @@
#include <cassert>
#include <iostream>
#include <sstream>
#include <cstdint>
#include <fstream>
#include <iostream>
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<json::JObject> 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);

View File

@ -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;

View File

@ -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));

View File

@ -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> 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);
}
}
}
}
}
}

View File

@ -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_

View File

@ -2,13 +2,13 @@
#include <limits.h>
#include <memory>
#include <iostream>
#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> chunk = chunks->chunks[index];
std::shared_ptr<Chunk> 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> chunk = chunks->chunks[index];
std::shared_ptr<Chunk> chunk = chunks->chunks[index];
if (chunk != nullptr) {
return false;
}

View File

@ -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 @@ private:
/* 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 */

View File

@ -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);
}
blocks->update(delta);
}

View File

@ -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:

View File

@ -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;
}
}
}

View File

@ -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 @@ public:
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);
};

View File

@ -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");
}

View File

@ -0,0 +1,14 @@
#ifndef LOGIC_SCRIPTING_API_LUA_H_
#define LOGIC_SCRIPTING_API_LUA_H_
#include <lua.hpp>
#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_

View File

@ -0,0 +1,140 @@
#include "scripting.h"
#include <iostream>
#include <stdexcept>
#include <lua.hpp>
#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, "<script>");
}
void scripting::on_world_quit() {
scripting::level = nullptr;
scripting::content = nullptr;
}
void scripting::update_block(const Block* block, int x, int y, int z) {
std::string name = block->name+".update";
lua_getglobal(L, name.c_str());
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
call_func(L, 3, name);
}
void scripting::random_update_block(const Block* block, int x, int y, int z) {
std::string name = block->name+".randupdate";
lua_getglobal(L, name.c_str());
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
call_func(L, 3, name);
}
void scripting::on_block_placed(Player* player, const Block* block, int x, int y, int z) {
std::string name = block->name+".placed";
lua_getglobal(L, name.c_str());
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
call_func(L, 3, name);
}
void scripting::on_block_broken(Player* player, const Block* block, int x, int y, int z) {
std::string name = block->name+".broken";
lua_getglobal(L, name.c_str());
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
call_func(L, 3, name);
}
void scripting::load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset) {
std::string src = files::read_string(file);
std::cout << "loading script " << file.u8string() << std::endl;
if (luaL_loadbuffer(L, src.c_str(), src.size(), file.string().c_str())) {
std::cerr << "Lua error:" << lua_tostring(L,-1) << std::endl;
return;
}
call_func(L, 0, "<script>");
funcsset->init=rename_global(L, "init", (prefix+".init").c_str());
funcsset->update=rename_global(L, "on_update", (prefix+".update").c_str());
funcsset->randupdate=rename_global(L, "on_random_update", (prefix+".randupdate").c_str());
funcsset->onbroken=rename_global(L, "on_broken", (prefix+".broken").c_str());
funcsset->onplaced=rename_global(L, "on_placed", (prefix+".placed").c_str());
}
void scripting::close() {
lua_close(L);
L = nullptr;
content = nullptr;
level = nullptr;
}

View File

@ -0,0 +1,26 @@
#include <string>
#include <filesystem>
namespace fs = std::filesystem;
class EnginePaths;
class Content;
class Level;
class Block;
class Player;
struct block_funcs_set;
namespace scripting {
extern const Content* content;
extern Level* level;
void initialize(EnginePaths* paths);
void on_world_load(Level* level);
void on_world_quit();
void update_block(const Block* block, int x, int y, int z);
void random_update_block(const Block* block, int x, int y, int z);
void on_block_placed(Player* player, const Block* block, int x, int y, int z);
void on_block_broken(Player* player, const Block* block, int x, int y, int z);
void load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset);
void close();
}

View File

@ -18,12 +18,12 @@ const float JUMP_FORCE = 8.0f;
Player::Player(glm::vec3 position, float speed) :
speed(speed),
choosenBlock(1) {
chosenBlock(1) {
camera = new Camera(position, glm::radians(90.0f));
currentViewCamera = camera;
SPCamera = new Camera(position, glm::radians(90.0f));
TPCamera = new Camera(position, glm::radians(90.0f));
hitbox = new Hitbox(position, vec3(0.3f,0.9f,0.3f));
hitbox = new Hitbox(position, glm::vec3(0.3f,0.9f,0.3f));
}
Player::~Player(){
@ -49,7 +49,7 @@ void Player::update(
speed *= RUN_SPEED_MUL;
}
vec3 dir(0,0,0);
glm::vec3 dir(0,0,0);
if (input.moveForward){
dir.x += camera->dir.x;
dir.z += camera->dir.z;
@ -66,8 +66,8 @@ void Player::update(
dir.x -= camera->right.x;
dir.z -= camera->right.z;
}
if (length(dir) > 0.0f){
dir = normalize(dir);
if (glm::length(dir) > 0.0f){
dir = glm::normalize(dir);
hitbox->velocity.x += dir.x * speed * delta * 9;
hitbox->velocity.z += dir.z * speed * delta * 9;
}

View File

@ -35,7 +35,7 @@ public:
bool flight = false;
bool noclip = false;
bool debug = false;
int choosenBlock;
int chosenBlock;
voxel selectedVoxel {0, 0};
float camX = 0.0f;

View File

@ -1,4 +1,8 @@
#include "Hitbox.h"
Hitbox::Hitbox(vec3 position, vec3 halfsize) : position(position), halfsize(halfsize), velocity(0.0f,0.0f,0.0f), linear_damping(0.1f) {
Hitbox::Hitbox(glm::vec3 position, glm::vec3 halfsize)
: position(position),
halfsize(halfsize),
velocity(0.0f,0.0f,0.0f),
linear_damping(0.1f) {
}

View File

@ -3,17 +3,15 @@
#include <glm/glm.hpp>
using namespace glm;
class Hitbox {
public:
vec3 position;
vec3 halfsize;
vec3 velocity;
glm::vec3 position;
glm::vec3 halfsize;
glm::vec3 velocity;
float linear_damping;
bool grounded = false;
Hitbox(vec3 position, vec3 halfsize);
Hitbox(glm::vec3 position, glm::vec3 halfsize);
};
#endif /* PHYSICS_HITBOX_H_ */

View File

@ -25,14 +25,14 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS;
platform::configure_encoding();
std::filesystem::path userfiles = paths.getUserfiles();
fs::path userfiles = paths.getUserfiles();
try {
EngineSettings settings;
std::unique_ptr<toml::Wrapper> wrapper (create_wrapper(settings));
fs::path settings_file = userfiles/fs::path(SETTINGS_FILE);
fs::path controls_file = userfiles/fs::path(CONTROLS_FILE);
if (std::filesystem::is_regular_file(settings_file)) {
if (fs::is_regular_file(settings_file)) {
std::cout << "-- loading settings" << std::endl;
std::string text = files::read_string(settings_file);
toml::Reader reader(wrapper.get(), settings_file.string(), text);
@ -40,7 +40,7 @@ int main(int argc, char** argv) {
}
setup_bindings();
Engine engine(settings, &paths);
if (std::filesystem::is_regular_file(controls_file)) {
if (fs::is_regular_file(controls_file)) {
std::cout << "-- loading controls" << std::endl;
std::string text = files::read_string(controls_file);
load_controls(controls_file.string(), text);

View File

@ -16,6 +16,14 @@ const uint FACE_PZ = 5;
const uint BLOCK_AABB_GRID = 16;
struct block_funcs_set {
bool init: 1;
bool update: 1;
bool onplaced: 1;
bool onbroken: 1;
bool randupdate: 1;
};
struct CoordSystem {
glm::ivec3 axisX;
glm::ivec3 axisY;
@ -70,6 +78,7 @@ public:
bool replaceable = false;
bool breakable = true;
bool rotatable = false;
bool grounded = false;
AABB hitbox;
BlockRotProfile rotations;
@ -78,6 +87,7 @@ public:
bool solid = true;
bool emissive = false;
AABB hitboxes[BlockRotProfile::MAX_COUNT];
block_funcs_set funcsset {};
} rt;
Block(std::string name);

View File

@ -90,6 +90,13 @@ const AABB* Chunks::isObstacle(float x, float y, float z){
return nullptr;
}
bool Chunks::isSolid(int x, int y, int z) {
voxel* v = get(x, y, z);
if (v == nullptr)
return false;
return contentIds->getBlockDef(v->id)->rt.solid;
}
ubyte Chunks::getLight(int x, int y, int z, int channel){
x -= ox * CHUNK_W;
z -= oz * CHUNK_D;
@ -167,7 +174,7 @@ void Chunks::set(int x, int y, int z, int id, uint8_t states){
else if (y + 1 > chunk->top) chunk->top = y + 1;
else if (id == 0) chunk->updateHeights();
if (lx == 0 && (chunk = getChunk(cx+ox-1, cz+oz)))
if (lx == 0 && (chunk = getChunk(cx+ox-1, cz+oz)))
chunk->setModified(true);
if (lz == 0 && (chunk = getChunk(cx+ox, cz+oz-1)))
chunk->setModified(true);

View File

@ -54,6 +54,7 @@ public:
glm::vec3 rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDist);
const AABB* isObstacle(float x, float y, float z);
bool isSolid(int x, int y, int z);
// does not move chunks inside
void _setOffset(int x, int z);

View File

@ -17,7 +17,7 @@ Level::Level(World* world, const Content* content, Player* player, EngineSetting
chunksStorage(new ChunksStorage(this)),
events(new LevelEvents()) ,
settings(settings) {
physics = new PhysicsSolver(vec3(0, -22.6f, 0));
physics = new PhysicsSolver(glm::vec3(0, -22.6f, 0));
uint matrixSize = (settings.chunks.loadDistance+
settings.chunks.padding) * 2;
@ -40,7 +40,7 @@ Level::~Level(){
}
void Level::update() {
vec3 position = player->hitbox->position;
glm::vec3 position = player->hitbox->position;
chunks->setCenter(position.x, position.z);
int matrixSize = (settings.chunks.loadDistance+

View File

@ -5,16 +5,13 @@
#include "../settings.h"
class Content;
class ContentIndices;
class World;
class Player;
class Chunks;
class LevelEvents;
class Lighting;
class PhysicsSolver;
class ChunksController;
class ChunksStorage;
class PlayerController;
class Level {
public: