Merge branch 'main' of https://github.com/MihailRis/VoxelEngine-Cpp
This commit is contained in:
commit
652ea6c3e3
3
res/layouts/inventory.xml
Normal file
3
res/layouts/inventory.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<inventory color="#1F1F1FE0" size="400,0">
|
||||
<slots-grid rows="4" count="40" sharefunc="inventory_share_func"/>
|
||||
</inventory>
|
||||
11
res/layouts/inventory.xml.lua
Normal file
11
res/layouts/inventory.xml.lua
Normal file
@ -0,0 +1,11 @@
|
||||
function on_open(inv)
|
||||
print("OPEN", inv)
|
||||
end
|
||||
|
||||
function on_close(inv)
|
||||
print("CLOSE", inv)
|
||||
end
|
||||
|
||||
function inventory_share_func(invid, slotid)
|
||||
inventory.set(invid, slotid, 0, 0)
|
||||
end
|
||||
65
res/modules/toml.lua
Normal file
65
res/modules/toml.lua
Normal file
@ -0,0 +1,65 @@
|
||||
-- TOML serialization module
|
||||
local toml = {}
|
||||
|
||||
-- Convert table to TOML
|
||||
function toml.serialize(tb, isinner)
|
||||
local text = ""
|
||||
for k, v in pairs(tb) do
|
||||
local tp = type(v)
|
||||
if tp ~= "table" then
|
||||
text = text..k.." = "
|
||||
if tp == "string" then
|
||||
text = text..string.format("%q", v)
|
||||
else
|
||||
text = text..tostring(v)
|
||||
end
|
||||
text = text.."\n"
|
||||
end
|
||||
end
|
||||
for k, v in pairs(tb) do
|
||||
local tp = type(v)
|
||||
if tp == "table" then
|
||||
if isinner then
|
||||
error("only one level of subtables supported")
|
||||
end
|
||||
text = text.."["..k.."]\n"..toml.serialize(v).."\n"
|
||||
end
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
-- Parse TOML to new table
|
||||
function toml.deserialize(s)
|
||||
local output = {}
|
||||
local current = output
|
||||
local lines = {}
|
||||
for line in string.gmatch(s, "[^\r\n]+") do
|
||||
line = string.gsub(line, "%s+", "")
|
||||
table.insert(lines, line)
|
||||
end
|
||||
for i = 1,#lines do
|
||||
local s = lines[i]
|
||||
if string.sub(s, 1, 1) == "[" then
|
||||
local section = s.sub(s, 2, #s-1)
|
||||
current = {}
|
||||
output[section] = current
|
||||
else
|
||||
for k, v in string.gmatch(s, "(%w+)=(.+)" ) do
|
||||
v = string.gsub(v, "%s+", "")
|
||||
if v.sub(v, 1, 1) == "\"" then
|
||||
current[k] = v.sub(v, 2, #v-1)
|
||||
elseif v == "true" or v == "false" then
|
||||
current[k] = v == "true"
|
||||
end
|
||||
|
||||
local num = tonumber(v)
|
||||
if num ~= nil then
|
||||
current[k] = num
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
return toml
|
||||
@ -37,10 +37,14 @@ function load_script(path, nocache)
|
||||
if not nocache and __cached_scripts[fullpath] ~= nil then
|
||||
return __cached_results[fullpath]
|
||||
end
|
||||
local script = loadfile(fullpath)
|
||||
if script == nil then
|
||||
if not file.isfile(fullpath) then
|
||||
error("script '"..filename.."' not found in '"..packname.."'")
|
||||
end
|
||||
|
||||
local script, err = loadfile(fullpath)
|
||||
if script == nil then
|
||||
error(err)
|
||||
end
|
||||
local result = script()
|
||||
if not nocache then
|
||||
__cached_scripts[fullpath] = script
|
||||
@ -49,6 +53,16 @@ function load_script(path, nocache)
|
||||
return result
|
||||
end
|
||||
|
||||
function require(path)
|
||||
local prefix, file = parse_path(path)
|
||||
return load_script(prefix..":modules/"..file..".lua")
|
||||
end
|
||||
|
||||
function __reset_scripts_cache()
|
||||
__cached_scripts = {}
|
||||
__cached_results = {}
|
||||
end
|
||||
|
||||
function sleep(timesec)
|
||||
local start = time.uptime()
|
||||
while time.uptime() - start < timesec do
|
||||
@ -73,3 +87,7 @@ function dofile(path)
|
||||
end
|
||||
return _dofile(path)
|
||||
end
|
||||
|
||||
function pack.is_installed(packid)
|
||||
return file.isfile(packid..":package.json")
|
||||
end
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
-- use for engine development tests
|
||||
-- must be empty in release
|
||||
-- must not be modified by content-packs
|
||||
@ -26,6 +26,7 @@ uniform float u_torchlightDistance;
|
||||
void main(){
|
||||
vec3 pos3d = (u_model * vec4(v_position, 1.0)).xyz-u_cameraPos.xyz;
|
||||
vec4 modelpos = u_model * vec4(v_position, 1.0);
|
||||
modelpos.y -= pow(length(pos3d.xz)*0.002, 3.0);
|
||||
vec4 viewmodelpos = u_view * modelpos;
|
||||
vec4 decomp_light = decompress_light(v_light);
|
||||
vec3 light = decomp_light.rgb;
|
||||
|
||||
@ -13,8 +13,8 @@ menu.New World = Uusi Maailma
|
||||
menu.Quit=Poistu
|
||||
menu.Continue=Jatka
|
||||
menu.Save and Quit to Menu=Tallenna ja poistu valikkoon
|
||||
menu.missing-content=Puuttuu jotkut lisäosat!
|
||||
menu.Content Error=Sisältövirhe!
|
||||
menu.missing-content=Puuttuu lisäosia!
|
||||
menu.Content Error=Lisäosa virhe!
|
||||
menu.Controls=Ohjaus
|
||||
menu.Back to Main Menu=Takaisin Valikoon
|
||||
menu.Settings=Asetukset
|
||||
|
||||
BIN
res/textures/gui/error.png
Normal file
BIN
res/textures/gui/error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 702 B |
BIN
res/textures/gui/warning.png
Normal file
BIN
res/textures/gui/warning.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 658 B |
@ -4,6 +4,8 @@
|
||||
#include "../graphics/Shader.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Font.h"
|
||||
#include "../frontend/UiDocument.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
Assets::~Assets() {
|
||||
}
|
||||
@ -62,6 +64,17 @@ void Assets::store(const TextureAnimation& animation) {
|
||||
animations.emplace_back(animation);
|
||||
}
|
||||
|
||||
UiDocument* Assets::getLayout(std::string name) const {
|
||||
auto found = layouts.find(name);
|
||||
if (found == layouts.end())
|
||||
return nullptr;
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
void Assets::store(UiDocument* layout, std::string name) {
|
||||
layouts[name].reset(layout);
|
||||
}
|
||||
|
||||
void Assets::extend(const Assets& assets) {
|
||||
for (auto entry : assets.textures) {
|
||||
textures[entry.first] = entry.second;
|
||||
@ -75,6 +88,9 @@ void Assets::extend(const Assets& assets) {
|
||||
for (auto entry : assets.atlases) {
|
||||
atlases[entry.first] = entry.second;
|
||||
}
|
||||
for (auto entry : assets.layouts) {
|
||||
layouts[entry.first] = entry.second;
|
||||
}
|
||||
animations.clear();
|
||||
for (auto entry : assets.animations) {
|
||||
animations.emplace_back(entry);
|
||||
|
||||
@ -12,12 +12,20 @@ class Texture;
|
||||
class Shader;
|
||||
class Font;
|
||||
class Atlas;
|
||||
class UiDocument;
|
||||
|
||||
struct LayoutCfg {
|
||||
int env;
|
||||
|
||||
LayoutCfg(int env) : env(env) {}
|
||||
};
|
||||
|
||||
class Assets {
|
||||
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
|
||||
std::unordered_map<std::string, std::shared_ptr<Shader>> shaders;
|
||||
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
|
||||
std::unordered_map<std::string, std::shared_ptr<Atlas>> atlases;
|
||||
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
|
||||
std::vector<TextureAnimation> animations;
|
||||
public:
|
||||
~Assets();
|
||||
@ -36,6 +44,9 @@ public:
|
||||
const std::vector<TextureAnimation>& getAnimations();
|
||||
void store(const TextureAnimation& animation);
|
||||
|
||||
UiDocument* getLayout(std::string name) const;
|
||||
void store(UiDocument* layout, std::string name);
|
||||
|
||||
void extend(const Assets& assets);
|
||||
};
|
||||
|
||||
|
||||
@ -8,20 +8,24 @@
|
||||
|
||||
#include "../constants.h"
|
||||
#include "../files/engine_paths.h"
|
||||
|
||||
using std::filesystem::path;
|
||||
using std::unique_ptr;
|
||||
#include "../content/Content.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
|
||||
: assets(assets), paths(paths) {
|
||||
addLoader(ASSET_SHADER, assetload::shader);
|
||||
addLoader(ASSET_TEXTURE, assetload::texture);
|
||||
addLoader(ASSET_FONT, assetload::font);
|
||||
addLoader(ASSET_ATLAS, assetload::atlas);
|
||||
addLoader(ASSET_LAYOUT, assetload::layout);
|
||||
}
|
||||
|
||||
void AssetsLoader::addLoader(int tag, aloader_func func) {
|
||||
loaders[tag] = func;
|
||||
}
|
||||
|
||||
void AssetsLoader::add(int tag, const std::string filename, const std::string alias) {
|
||||
entries.push(aloader_entry{ tag, filename, alias });
|
||||
void AssetsLoader::add(int tag, const std::string filename, const std::string alias, std::shared_ptr<void> settings) {
|
||||
entries.push(aloader_entry{ tag, filename, alias, settings});
|
||||
}
|
||||
|
||||
bool AssetsLoader::hasNext() const {
|
||||
@ -38,19 +42,25 @@ bool AssetsLoader::loadNext() {
|
||||
return false;
|
||||
}
|
||||
aloader_func loader = found->second;
|
||||
bool status = loader(assets, paths, entry.filename, entry.alias);
|
||||
bool status = loader(*this, assets, paths, entry.filename, entry.alias, entry.config);
|
||||
entries.pop();
|
||||
return status;
|
||||
}
|
||||
|
||||
void AssetsLoader::createDefaults(AssetsLoader& loader) {
|
||||
loader.addLoader(ASSET_SHADER, assetload::shader);
|
||||
loader.addLoader(ASSET_TEXTURE, assetload::texture);
|
||||
loader.addLoader(ASSET_FONT, assetload::font);
|
||||
loader.addLoader(ASSET_ATLAS, assetload::atlas);
|
||||
void addLayouts(int env, const std::string& prefix, const fs::path& folder, AssetsLoader& loader) {
|
||||
if (!fs::is_directory(folder)) {
|
||||
return;
|
||||
}
|
||||
for (auto& entry : fs::directory_iterator(folder)) {
|
||||
const fs::path file = entry.path();
|
||||
if (file.extension().u8string() != ".xml")
|
||||
continue;
|
||||
std::string name = prefix+":"+file.stem().u8string();
|
||||
loader.add(ASSET_LAYOUT, file.u8string(), name, std::make_shared<LayoutCfg>(env));
|
||||
}
|
||||
}
|
||||
|
||||
void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) {
|
||||
void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||
loader.add(ASSET_FONT, FONTS_FOLDER"/font", "normal");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui", "ui");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/main", "main");
|
||||
@ -58,12 +68,21 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) {
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/menubg.png", "gui/menubg");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/delete_icon.png", "gui/delete_icon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/no_icon.png", "gui/no_icon");
|
||||
if (world) {
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/warning.png", "gui/warning");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/gui/error.png", "gui/error");
|
||||
if (content) {
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/ui3d", "ui3d");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/background", "background");
|
||||
loader.add(ASSET_SHADER, SHADERS_FOLDER"/skybox_gen", "skybox_gen");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/moon.png", "misc/moon");
|
||||
loader.add(ASSET_TEXTURE, TEXTURES_FOLDER"/misc/sun.png", "misc/sun");
|
||||
|
||||
addLayouts(0, "core", loader.getPaths()->getMainRoot()/fs::path("layouts"), loader);
|
||||
for (auto& pack : content->getPacks()) {
|
||||
auto& info = pack->getInfo();
|
||||
fs::path folder = info.folder / fs::path("layouts");
|
||||
addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader);
|
||||
}
|
||||
}
|
||||
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/blocks", "blocks");
|
||||
loader.add(ASSET_ATLAS, TEXTURES_FOLDER"/items", "items");
|
||||
@ -71,4 +90,4 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, bool world) {
|
||||
|
||||
const ResPaths* AssetsLoader::getPaths() const {
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define ASSETS_ASSETS_LOADER_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
@ -10,16 +11,20 @@ const short ASSET_TEXTURE = 1;
|
||||
const short ASSET_SHADER = 2;
|
||||
const short ASSET_FONT = 3;
|
||||
const short ASSET_ATLAS = 4;
|
||||
const short ASSET_LAYOUT = 5;
|
||||
|
||||
class ResPaths;
|
||||
class Assets;
|
||||
class AssetsLoader;
|
||||
class Content;
|
||||
|
||||
typedef std::function<bool(Assets*, const ResPaths*, const std::string&, const std::string&)> aloader_func;
|
||||
using aloader_func = std::function<bool(AssetsLoader&, Assets*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<void>)>;
|
||||
|
||||
struct aloader_entry {
|
||||
int tag;
|
||||
const std::string filename;
|
||||
const std::string alias;
|
||||
std::shared_ptr<void> config;
|
||||
};
|
||||
|
||||
class AssetsLoader {
|
||||
@ -30,13 +35,18 @@ class AssetsLoader {
|
||||
public:
|
||||
AssetsLoader(Assets* assets, const ResPaths* paths);
|
||||
void addLoader(int tag, aloader_func func);
|
||||
void add(int tag, const std::string filename, const std::string alias);
|
||||
void add(
|
||||
int tag,
|
||||
const std::string filename,
|
||||
const std::string alias,
|
||||
std::shared_ptr<void> settings=nullptr
|
||||
);
|
||||
|
||||
|
||||
bool hasNext() const;
|
||||
bool loadNext();
|
||||
|
||||
static void createDefaults(AssetsLoader& loader);
|
||||
static void addDefaults(AssetsLoader& loader, bool world);
|
||||
static void addDefaults(AssetsLoader& loader, const Content* content);
|
||||
|
||||
const ResPaths* getPaths() const;
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "Assets.h"
|
||||
#include "AssetsLoader.h"
|
||||
#include "../files/files.h"
|
||||
#include "../files/engine_paths.h"
|
||||
#include "../coders/png.h"
|
||||
@ -13,194 +14,233 @@
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../graphics/Font.h"
|
||||
#include "../graphics/TextureAnimation.h"
|
||||
#include "../frontend/UiDocument.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool assetload::texture(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name) {
|
||||
std::unique_ptr<Texture> texture(
|
||||
png::load_texture(paths->find(filename).string())
|
||||
bool assetload::texture(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
std::unique_ptr<Texture> texture(
|
||||
png::load_texture(paths->find(filename).u8string())
|
||||
);
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load texture '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(texture.release(), name);
|
||||
return true;
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load texture '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(texture.release(), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::shader(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name) {
|
||||
fs::path vertexFile = paths->find(filename+".glslv");
|
||||
fs::path fragmentFile = paths->find(filename+".glslf");
|
||||
bool assetload::shader(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
fs::path vertexFile = paths->find(filename+".glslv");
|
||||
fs::path fragmentFile = paths->find(filename+".glslf");
|
||||
|
||||
std::string vertexSource = files::read_string(vertexFile);
|
||||
std::string fragmentSource = files::read_string(fragmentFile);
|
||||
std::string vertexSource = files::read_string(vertexFile);
|
||||
std::string fragmentSource = files::read_string(fragmentFile);
|
||||
|
||||
Shader* shader = Shader::loadShader(
|
||||
vertexFile.string(),
|
||||
fragmentFile.string(),
|
||||
vertexSource, fragmentSource);
|
||||
Shader* shader = Shader::loadShader(
|
||||
vertexFile.string(),
|
||||
fragmentFile.string(),
|
||||
vertexSource, fragmentSource);
|
||||
|
||||
if (shader == nullptr) {
|
||||
std::cerr << "failed to load shader '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(shader, name);
|
||||
return true;
|
||||
if (shader == nullptr) {
|
||||
std::cerr << "failed to load shader '" << name << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
assets->store(shader, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) {
|
||||
// png is only supported format
|
||||
if (file.extension() != ".png")
|
||||
return false;
|
||||
std::string name = file.stem().string();
|
||||
// skip duplicates
|
||||
if (atlas.has(name)) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<ImageData> image(png::load_image(file.string()));
|
||||
if (image == nullptr) {
|
||||
std::cerr << "could not to load " << file.string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
image->fixAlphaColor();
|
||||
atlas.add(name, image.release());
|
||||
// png is only supported format
|
||||
if (file.extension() != ".png")
|
||||
return false;
|
||||
std::string name = file.stem().string();
|
||||
// skip duplicates
|
||||
if (atlas.has(name)) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<ImageData> image(png::load_image(file.string()));
|
||||
if (image == nullptr) {
|
||||
std::cerr << "could not to load " << file.string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
image->fixAlphaColor();
|
||||
atlas.add(name, image.release());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::atlas(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name) {
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
Atlas* atlas = builder.build(2);
|
||||
assets->store(atlas, name);
|
||||
for (const auto& file : builder.getNames()) {
|
||||
assetload::animation(assets, paths, "textures", file, atlas);
|
||||
}
|
||||
return true;
|
||||
bool assetload::atlas(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths->listdir(directory)) {
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
Atlas* atlas = builder.build(2);
|
||||
assets->store(atlas, name);
|
||||
for (const auto& file : builder.getNames()) {
|
||||
assetload::animation(assets, paths, "textures", file, atlas);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::font(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name) {
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
for (size_t i = 0; i <= 4; i++) {
|
||||
bool assetload::font(
|
||||
AssetsLoader&,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void>
|
||||
) {
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
for (size_t i = 0; i <= 4; i++) {
|
||||
std::string name = filename + "_" + std::to_string(i) + ".png";
|
||||
name = paths->find(name).string();
|
||||
std::unique_ptr<Texture> texture (png::load_texture(name));
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load bitmap font '" << name;
|
||||
std::unique_ptr<Texture> texture (png::load_texture(name));
|
||||
if (texture == nullptr) {
|
||||
std::cerr << "failed to load bitmap font '" << name;
|
||||
std::cerr << "' (missing page " << std::to_string(i) << ")";
|
||||
std::cerr << std::endl;
|
||||
return false;
|
||||
}
|
||||
pages.push_back(std::move(texture));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
pages.push_back(std::move(texture));
|
||||
}
|
||||
int res = pages[0]->height / 16;
|
||||
assets->store(new Font(std::move(pages), res, 4), name);
|
||||
return true;
|
||||
assets->store(new Font(std::move(pages), res, 4), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool assetload::layout(
|
||||
AssetsLoader& loader,
|
||||
Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string file,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> config
|
||||
) {
|
||||
try {
|
||||
LayoutCfg* cfg = reinterpret_cast<LayoutCfg*>(config.get());
|
||||
auto document = UiDocument::read(loader, cfg->env, name, file);
|
||||
assets->store(document.release(), name);
|
||||
return true;
|
||||
} catch (const parsing_error& err) {
|
||||
std::cerr << "failed to parse layout XML '" << file << "'" << std::endl;
|
||||
std::cerr << err.errorLog() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool assetload::animation(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas) {
|
||||
std::string animsDir = directory + "/animations";
|
||||
std::string blocksDir = directory + "/blocks";
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas) {
|
||||
std::string animsDir = directory + "/animations";
|
||||
std::string blocksDir = directory + "/blocks";
|
||||
|
||||
for (const auto& folder : paths->listdir(animsDir)) {
|
||||
if (!fs::is_directory(folder)) continue;
|
||||
if (folder.filename().string() != name) continue;
|
||||
if (fs::is_empty(folder)) continue;
|
||||
|
||||
AtlasBuilder builder;
|
||||
for (const auto& folder : paths->listdir(animsDir)) {
|
||||
if (!fs::is_directory(folder)) continue;
|
||||
if (folder.filename().string() != name) continue;
|
||||
if (fs::is_empty(folder)) continue;
|
||||
|
||||
AtlasBuilder builder;
|
||||
appendAtlas(builder, paths->find(blocksDir + "/" + name + ".png"));
|
||||
|
||||
std::string animFile = folder.string() + "/animation.json";
|
||||
std::string animFile = folder.string() + "/animation.json";
|
||||
|
||||
std::vector<std::pair<std::string, float>> frameList;
|
||||
std::vector<std::pair<std::string, float>> frameList;
|
||||
|
||||
if (fs::exists(animFile)) {
|
||||
auto root = files::read_json(animFile);
|
||||
if (fs::exists(animFile)) {
|
||||
auto root = files::read_json(animFile);
|
||||
|
||||
auto frameArr = root->list("frames");
|
||||
auto frameArr = root->list("frames");
|
||||
|
||||
float frameDuration = DEFAULT_FRAME_DURATION;
|
||||
std::string frameName;
|
||||
float frameDuration = DEFAULT_FRAME_DURATION;
|
||||
std::string frameName;
|
||||
|
||||
if (frameArr) {
|
||||
for (size_t i = 0; i < frameArr->size(); i++) {
|
||||
auto currentFrame = frameArr->list(i);
|
||||
if (frameArr) {
|
||||
for (size_t i = 0; i < frameArr->size(); i++) {
|
||||
auto currentFrame = frameArr->list(i);
|
||||
|
||||
frameName = currentFrame->str(0);
|
||||
if (currentFrame->size() > 1) frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
|
||||
frameName = currentFrame->str(0);
|
||||
if (currentFrame->size() > 1)
|
||||
frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
|
||||
|
||||
frameList.emplace_back(frameName, frameDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
|
||||
if (!frameList.empty()) {
|
||||
bool contains = false;
|
||||
for (const auto& elem : frameList) {
|
||||
if (file.stem() == elem.first) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) continue;
|
||||
}
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
frameList.emplace_back(frameName, frameDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
|
||||
if (!frameList.empty()) {
|
||||
bool contains = false;
|
||||
for (const auto& elem : frameList) {
|
||||
if (file.stem() == elem.first) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) continue;
|
||||
}
|
||||
if (!appendAtlas(builder, file)) continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<Atlas> srcAtlas (builder.build(2));
|
||||
std::unique_ptr<Atlas> srcAtlas (builder.build(2));
|
||||
|
||||
Texture* srcTex = srcAtlas->getTexture();
|
||||
Texture* dstTex = dstAtlas->getTexture();
|
||||
Texture* srcTex = srcAtlas->getTexture();
|
||||
Texture* dstTex = dstAtlas->getTexture();
|
||||
|
||||
TextureAnimation animation(srcTex, dstTex);
|
||||
Frame frame;
|
||||
UVRegion region = dstAtlas->get(name);
|
||||
TextureAnimation animation(srcTex, dstTex);
|
||||
Frame frame;
|
||||
UVRegion region = dstAtlas->get(name);
|
||||
|
||||
frame.dstPos = glm::ivec2(region.u1 * dstTex->width, region.v1 * dstTex->height);
|
||||
frame.size = glm::ivec2(region.u2 * dstTex->width, region.v2 * dstTex->height) - frame.dstPos;
|
||||
frame.dstPos = glm::ivec2(region.u1 * dstTex->width, region.v1 * dstTex->height);
|
||||
frame.size = glm::ivec2(region.u2 * dstTex->width, region.v2 * dstTex->height) - frame.dstPos;
|
||||
|
||||
if (frameList.empty()) {
|
||||
for (const auto& elem : builder.getNames()) {
|
||||
region = srcAtlas->get(elem);
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& elem : frameList) {
|
||||
if (!srcAtlas->has(elem.first)) {
|
||||
std::cerr << "Unknown frame name: " << elem.first << std::endl;
|
||||
continue;
|
||||
}
|
||||
region = srcAtlas->get(elem.first);
|
||||
frame.duration = elem.second;
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
if (frameList.empty()) {
|
||||
for (const auto& elem : builder.getNames()) {
|
||||
region = srcAtlas->get(elem);
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& elem : frameList) {
|
||||
if (!srcAtlas->has(elem.first)) {
|
||||
std::cerr << "Unknown frame name: " << elem.first << std::endl;
|
||||
continue;
|
||||
}
|
||||
region = srcAtlas->get(elem.first);
|
||||
frame.duration = elem.second;
|
||||
frame.srcPos = glm::ivec2(region.u1 * srcTex->width, srcTex->height - region.v2 * srcTex->height);
|
||||
animation.addFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
assets->store(srcAtlas.release(), name + "_animation");
|
||||
assets->store(animation);
|
||||
assets->store(srcAtlas.release(), name + "_animation");
|
||||
assets->store(animation);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2,33 +2,62 @@
|
||||
#define ASSETS_ASSET_LOADERS_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class ResPaths;
|
||||
class Assets;
|
||||
class AssetsLoader;
|
||||
class Atlas;
|
||||
|
||||
namespace assetload {
|
||||
bool texture(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name);
|
||||
bool shader(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name);
|
||||
bool atlas(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name);
|
||||
bool font(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name);
|
||||
bool animation(Assets* assets,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas);
|
||||
bool texture(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool shader(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool atlas(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool font(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string filename,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
bool layout(
|
||||
AssetsLoader&,
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string file,
|
||||
const std::string name,
|
||||
std::shared_ptr<void> settings
|
||||
);
|
||||
|
||||
bool animation(
|
||||
Assets*,
|
||||
const ResPaths* paths,
|
||||
const std::string directory,
|
||||
const std::string name,
|
||||
Atlas* dstAtlas
|
||||
);
|
||||
}
|
||||
|
||||
#endif // ASSETS_ASSET_LOADERS_H_
|
||||
@ -200,3 +200,11 @@ std::string ByteReader::getString() {
|
||||
bool ByteReader::hasNext() const {
|
||||
return pos < size;
|
||||
}
|
||||
|
||||
const ubyte* ByteReader::pointer() const {
|
||||
return data + pos;
|
||||
}
|
||||
|
||||
void ByteReader::skip(size_t n) {
|
||||
pos += n;
|
||||
}
|
||||
|
||||
@ -71,6 +71,9 @@ public:
|
||||
const char* getCString();
|
||||
std::string getString();
|
||||
bool hasNext() const;
|
||||
|
||||
const ubyte* pointer() const;
|
||||
void skip(size_t n);
|
||||
};
|
||||
|
||||
#endif // CODERS_BYTE_UTILS_H_
|
||||
|
||||
@ -32,6 +32,77 @@ bool Attribute::asBool() const {
|
||||
return text == "true" || text == "1";
|
||||
}
|
||||
|
||||
/* Read 2d vector formatted `x,y`*/
|
||||
glm::vec2 Attribute::asVec2() const {
|
||||
size_t pos = text.find(',');
|
||||
if (pos == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec2 value "+escape_string(text));
|
||||
}
|
||||
return glm::vec2(
|
||||
util::parse_double(text, 0, pos),
|
||||
util::parse_double(text, pos+1, text.length()-pos-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read 3d vector formatted `x,y,z`*/
|
||||
glm::vec3 Attribute::asVec3() const {
|
||||
size_t pos1 = text.find(',');
|
||||
if (pos1 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec3 value "+escape_string(text));
|
||||
}
|
||||
size_t pos2 = text.find(',', pos1+1);
|
||||
if (pos2 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec3 value "+escape_string(text));
|
||||
}
|
||||
return glm::vec3(
|
||||
util::parse_double(text, 0, pos1),
|
||||
util::parse_double(text, pos1+1, pos2),
|
||||
util::parse_double(text, pos2+1, text.length()-pos2-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read 4d vector formatted `x,y,z,w`*/
|
||||
glm::vec4 Attribute::asVec4() const {
|
||||
size_t pos1 = text.find(',');
|
||||
if (pos1 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(text));
|
||||
}
|
||||
size_t pos2 = text.find(',', pos1+1);
|
||||
if (pos2 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(text));
|
||||
}
|
||||
size_t pos3 = text.find(',', pos2+1);
|
||||
if (pos3 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(text));
|
||||
}
|
||||
return glm::vec4(
|
||||
util::parse_double(text, 0, pos1),
|
||||
util::parse_double(text, pos1+1, pos2-pos1-1),
|
||||
util::parse_double(text, pos2+1, pos3-pos2-1),
|
||||
util::parse_double(text, pos3+1, text.length()-pos3-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read RGBA color. Supported formats:
|
||||
- "#RRGGBB" or "#RRGGBBAA" hex color */
|
||||
glm::vec4 Attribute::asColor() const {
|
||||
if (text[0] == '#') {
|
||||
if (text.length() != 7 && text.length() != 9) {
|
||||
throw std::runtime_error("#RRGGBB or #RRGGBBAA required");
|
||||
}
|
||||
int a = 255;
|
||||
int r = (hexchar2int(text[1]) << 4) | hexchar2int(text[2]);
|
||||
int g = (hexchar2int(text[3]) << 4) | hexchar2int(text[4]);
|
||||
int b = (hexchar2int(text[5]) << 4) | hexchar2int(text[6]);
|
||||
if (text.length() == 9) {
|
||||
a = (hexchar2int(text[7]) << 4) | hexchar2int(text[8]);
|
||||
}
|
||||
return glm::vec4(r / 255.f, g / 255.f, b / 255.f, a / 255.f);
|
||||
} else {
|
||||
throw std::runtime_error("hex colors are only supported");
|
||||
}
|
||||
}
|
||||
|
||||
Node::Node(std::string tag) : tag(tag) {
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "commons.h"
|
||||
@ -30,6 +31,10 @@ namespace xml {
|
||||
int64_t asInt() const;
|
||||
double asFloat() const;
|
||||
bool asBool() const;
|
||||
glm::vec2 asVec2() const;
|
||||
glm::vec3 asVec3() const;
|
||||
glm::vec4 asVec4() const;
|
||||
glm::vec4 asColor() const;
|
||||
};
|
||||
|
||||
/* XML element class. Text element has tag 'text' and attribute 'text' */
|
||||
|
||||
@ -36,5 +36,6 @@ constexpr uint vox_index(uint x, uint y, uint z, uint w=CHUNK_W, uint d=CHUNK_D)
|
||||
#define SHADERS_FOLDER "shaders"
|
||||
#define TEXTURES_FOLDER "textures"
|
||||
#define FONTS_FOLDER "fonts"
|
||||
#define LAYOUTS_FOLDER "layouts"
|
||||
|
||||
#endif // SRC_CONSTANTS_H_
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
#include "../voxels/Block.h"
|
||||
#include "../items/ItemDef.h"
|
||||
|
||||
#include "ContentPack.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
ContentBuilder::~ContentBuilder() {
|
||||
}
|
||||
|
||||
@ -22,7 +25,11 @@ void ContentBuilder::add(ItemDef* def) {
|
||||
itemIds.push_back(def->name);
|
||||
}
|
||||
|
||||
Block* ContentBuilder::createBlock(std::string id) {
|
||||
void ContentBuilder::add(ContentPackRuntime* pack) {
|
||||
packs.push_back(std::unique_ptr<ContentPackRuntime>(pack));
|
||||
}
|
||||
|
||||
Block& ContentBuilder::createBlock(std::string id) {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found != blockDefs.end()) {
|
||||
//return found->second;
|
||||
@ -30,20 +37,20 @@ Block* ContentBuilder::createBlock(std::string id) {
|
||||
}
|
||||
Block* block = new Block(id);
|
||||
add(block);
|
||||
return block;
|
||||
return *block;
|
||||
}
|
||||
|
||||
ItemDef* ContentBuilder::createItem(std::string id) {
|
||||
ItemDef& ContentBuilder::createItem(std::string id) {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found != itemDefs.end()) {
|
||||
if (found->second->generated) {
|
||||
return found->second;
|
||||
return *found->second;
|
||||
}
|
||||
throw namereuse_error("name "+id+" is already used", contenttype::item);
|
||||
}
|
||||
ItemDef* item = new ItemDef(id);
|
||||
add(item);
|
||||
return item;
|
||||
return *item;
|
||||
}
|
||||
|
||||
void ContentBuilder::checkIdentifier(std::string id) {
|
||||
@ -65,7 +72,7 @@ contenttype ContentBuilder::checkContentType(std::string id) {
|
||||
|
||||
Content* ContentBuilder::build() {
|
||||
std::vector<Block*> blockDefsIndices;
|
||||
DrawGroups* groups = new DrawGroups;
|
||||
auto groups = std::make_unique<DrawGroups>();
|
||||
for (const std::string& name : blockIds) {
|
||||
Block* def = blockDefs[name];
|
||||
|
||||
@ -100,15 +107,18 @@ Content* ContentBuilder::build() {
|
||||
}
|
||||
|
||||
auto indices = new ContentIndices(blockDefsIndices, itemDefsIndices);
|
||||
std::unique_ptr<Content> content (new Content(indices, groups, blockDefs, itemDefs));
|
||||
|
||||
// Now, it's time to solve foreign keys
|
||||
auto content = std::make_unique<Content>(
|
||||
indices, std::move(groups), blockDefs, itemDefs, std::move(packs)
|
||||
);
|
||||
|
||||
// Now, it's time to resolve foreign keys
|
||||
for (Block* def : blockDefsIndices) {
|
||||
def->rt.pickingItem = content->requireItem(def->pickingItem)->rt.id;
|
||||
def->rt.pickingItem = content->requireItem(def->pickingItem).rt.id;
|
||||
}
|
||||
|
||||
for (ItemDef* def : itemDefsIndices) {
|
||||
def->rt.placingBlock = content->requireBlock(def->placingBlock)->rt.id;
|
||||
def->rt.placingBlock = content->requireBlock(def->placingBlock).rt.id;
|
||||
}
|
||||
|
||||
return content.release();
|
||||
@ -121,17 +131,19 @@ ContentIndices::ContentIndices(
|
||||
itemDefs(itemDefs) {
|
||||
}
|
||||
|
||||
Content::Content(ContentIndices* indices, DrawGroups* drawGroups,
|
||||
Content::Content(ContentIndices* indices,
|
||||
std::unique_ptr<DrawGroups> drawGroups,
|
||||
std::unordered_map<std::string, Block*> blockDefs,
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs)
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs,
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs)
|
||||
: blockDefs(blockDefs),
|
||||
itemDefs(itemDefs),
|
||||
indices(indices),
|
||||
drawGroups(drawGroups) {
|
||||
packs(std::move(packs)),
|
||||
drawGroups(std::move(drawGroups)) {
|
||||
}
|
||||
|
||||
Content::~Content() {
|
||||
delete drawGroups;
|
||||
}
|
||||
|
||||
Block* Content::findBlock(std::string id) const {
|
||||
@ -142,12 +154,12 @@ Block* Content::findBlock(std::string id) const {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
Block* Content::requireBlock(std::string id) const {
|
||||
Block& Content::requireBlock(std::string id) const {
|
||||
auto found = blockDefs.find(id);
|
||||
if (found == blockDefs.end()) {
|
||||
throw std::runtime_error("missing block "+id);
|
||||
}
|
||||
return found->second;
|
||||
return *found->second;
|
||||
}
|
||||
|
||||
ItemDef* Content::findItem(std::string id) const {
|
||||
@ -158,10 +170,14 @@ ItemDef* Content::findItem(std::string id) const {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
ItemDef* Content::requireItem(std::string id) const {
|
||||
ItemDef& Content::requireItem(std::string id) const {
|
||||
auto found = itemDefs.find(id);
|
||||
if (found == itemDefs.end()) {
|
||||
throw std::runtime_error("missing item "+id);
|
||||
}
|
||||
return found->second;
|
||||
return *found->second;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<ContentPackRuntime>>& Content::getPacks() const {
|
||||
return packs;
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ using DrawGroups = std::set<unsigned char>;
|
||||
class Block;
|
||||
class ItemDef;
|
||||
class Content;
|
||||
class ContentPackRuntime;
|
||||
|
||||
enum class contenttype {
|
||||
none, block, item
|
||||
@ -46,14 +47,17 @@ class ContentBuilder {
|
||||
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs;
|
||||
std::vector<std::string> itemIds;
|
||||
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs;
|
||||
public:
|
||||
~ContentBuilder();
|
||||
|
||||
void add(Block* def);
|
||||
void add(ItemDef* def);
|
||||
void add(ContentPackRuntime* pack);
|
||||
|
||||
Block* createBlock(std::string id);
|
||||
ItemDef* createItem(std::string id);
|
||||
Block& createBlock(std::string id);
|
||||
ItemDef& createItem(std::string id);
|
||||
|
||||
void checkIdentifier(std::string id);
|
||||
contenttype checkContentType(std::string id);
|
||||
@ -104,12 +108,15 @@ class Content {
|
||||
std::unordered_map<std::string, Block*> blockDefs;
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs;
|
||||
std::unique_ptr<ContentIndices> indices;
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs;
|
||||
public:
|
||||
DrawGroups* const drawGroups;
|
||||
std::unique_ptr<DrawGroups> const drawGroups;
|
||||
|
||||
Content(ContentIndices* indices, DrawGroups* drawGroups,
|
||||
Content(ContentIndices* indices,
|
||||
std::unique_ptr<DrawGroups> drawGroups,
|
||||
std::unordered_map<std::string, Block*> blockDefs,
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs);
|
||||
std::unordered_map<std::string, ItemDef*> itemDefs,
|
||||
std::vector<std::unique_ptr<ContentPackRuntime>> packs);
|
||||
~Content();
|
||||
|
||||
inline ContentIndices* getIndices() const {
|
||||
@ -117,10 +124,12 @@ public:
|
||||
}
|
||||
|
||||
Block* findBlock(std::string id) const;
|
||||
Block* requireBlock(std::string id) const;
|
||||
Block& requireBlock(std::string id) const;
|
||||
|
||||
ItemDef* findItem(std::string id) const;
|
||||
ItemDef* requireItem(std::string id) const;
|
||||
ItemDef& requireItem(std::string id) const;
|
||||
|
||||
const std::vector<std::unique_ptr<ContentPackRuntime>>& getPacks() const;
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_H_
|
||||
@ -92,7 +92,7 @@ void ContentLoader::fixPackIndices() {
|
||||
}
|
||||
|
||||
// TODO: add basic validation and logging
|
||||
void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
void ContentLoader::loadBlock(Block& def, std::string name, fs::path file) {
|
||||
auto root = files::read_json(file);
|
||||
|
||||
// block texturing
|
||||
@ -100,22 +100,22 @@ void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
std::string texture;
|
||||
root->str("texture", texture);
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
def->textureFaces[i] = texture;
|
||||
def.textureFaces[i] = texture;
|
||||
}
|
||||
} else if (root->has("texture-faces")) {
|
||||
auto texarr = root->list("texture-faces");
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
def->textureFaces[i] = texarr->str(i);
|
||||
def.textureFaces[i] = texarr->str(i);
|
||||
}
|
||||
}
|
||||
|
||||
// block model
|
||||
std::string model = "block";
|
||||
root->str("model", model);
|
||||
if (model == "block") def->model = BlockModel::block;
|
||||
else if (model == "aabb") def->model = BlockModel::aabb;
|
||||
if (model == "block") def.model = BlockModel::block;
|
||||
else if (model == "aabb") def.model = BlockModel::aabb;
|
||||
else if (model == "custom") {
|
||||
def->model = BlockModel::custom;
|
||||
def.model = BlockModel::custom;
|
||||
if (root->has("model-primitives")) {
|
||||
loadCustomBlockModel(def, root->map("model-primitives"));
|
||||
}
|
||||
@ -124,30 +124,30 @@ void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
<< name << " parsed: no \"model-primitives\" found" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (model == "X") def->model = BlockModel::xsprite;
|
||||
else if (model == "none") def->model = BlockModel::none;
|
||||
else if (model == "X") def.model = BlockModel::xsprite;
|
||||
else if (model == "none") def.model = BlockModel::none;
|
||||
else {
|
||||
std::cerr << "unknown model " << model << std::endl;
|
||||
def->model = BlockModel::none;
|
||||
def.model = BlockModel::none;
|
||||
}
|
||||
|
||||
// rotation profile
|
||||
std::string profile = "none";
|
||||
root->str("rotation", profile);
|
||||
def->rotatable = profile != "none";
|
||||
def.rotatable = profile != "none";
|
||||
if (profile == "pipe") {
|
||||
def->rotations = BlockRotProfile::PIPE;
|
||||
def.rotations = BlockRotProfile::PIPE;
|
||||
} else if (profile == "pane") {
|
||||
def->rotations = BlockRotProfile::PANE;
|
||||
def.rotations = BlockRotProfile::PANE;
|
||||
} else if (profile != "none") {
|
||||
std::cerr << "unknown rotation profile " << profile << std::endl;
|
||||
def->rotatable = false;
|
||||
def.rotatable = false;
|
||||
}
|
||||
|
||||
// block hitbox AABB [x, y, z, width, height, depth]
|
||||
auto boxarr = root->list("hitbox");
|
||||
if (boxarr) {
|
||||
AABB& aabb = def->hitbox;
|
||||
AABB& aabb = def.hitbox;
|
||||
aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
aabb.b += aabb.a;
|
||||
@ -156,27 +156,27 @@ void ContentLoader::loadBlock(Block* def, std::string name, fs::path file) {
|
||||
// block light emission [r, g, b] where r,g,b in range [0..15]
|
||||
auto emissionarr = root->list("emission");
|
||||
if (emissionarr) {
|
||||
def->emission[0] = emissionarr->num(0);
|
||||
def->emission[1] = emissionarr->num(1);
|
||||
def->emission[2] = emissionarr->num(2);
|
||||
def.emission[0] = emissionarr->num(0);
|
||||
def.emission[1] = emissionarr->num(1);
|
||||
def.emission[2] = emissionarr->num(2);
|
||||
}
|
||||
|
||||
// primitive properties
|
||||
root->flag("obstacle", def->obstacle);
|
||||
root->flag("replaceable", def->replaceable);
|
||||
root->flag("light-passing", def->lightPassing);
|
||||
root->flag("breakable", def->breakable);
|
||||
root->flag("selectable", def->selectable);
|
||||
root->flag("grounded", def->grounded);
|
||||
root->flag("hidden", def->hidden);
|
||||
root->flag("sky-light-passing", def->skyLightPassing);
|
||||
root->num("draw-group", def->drawGroup);
|
||||
root->str("picking-item", def->pickingItem);
|
||||
root->str("script-name", def->scriptName);
|
||||
root->num("inventory-size", def->inventorySize);
|
||||
root->flag("obstacle", def.obstacle);
|
||||
root->flag("replaceable", def.replaceable);
|
||||
root->flag("light-passing", def.lightPassing);
|
||||
root->flag("breakable", def.breakable);
|
||||
root->flag("selectable", def.selectable);
|
||||
root->flag("grounded", def.grounded);
|
||||
root->flag("hidden", def.hidden);
|
||||
root->flag("sky-light-passing", def.skyLightPassing);
|
||||
root->num("draw-group", def.drawGroup);
|
||||
root->str("picking-item", def.pickingItem);
|
||||
root->str("script-name", def.scriptName);
|
||||
root->num("inventory-size", def.inventorySize);
|
||||
}
|
||||
|
||||
void ContentLoader::loadCustomBlockModel(Block* def, dynamic::Map* primitives) {
|
||||
void ContentLoader::loadCustomBlockModel(Block& def, dynamic::Map* primitives) {
|
||||
if (primitives->has("aabbs")) {
|
||||
auto modelboxes = primitives->list("aabbs");
|
||||
for (uint i = 0; i < modelboxes->size(); i++ ) {
|
||||
@ -186,19 +186,19 @@ void ContentLoader::loadCustomBlockModel(Block* def, dynamic::Map* primitives) {
|
||||
modelbox.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
modelbox.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
modelbox.b += modelbox.a;
|
||||
def->modelBoxes.push_back(modelbox);
|
||||
def.modelBoxes.push_back(modelbox);
|
||||
|
||||
if (boxarr->size() == 7)
|
||||
for (uint i = 6; i < 12; i++) {
|
||||
def->modelTextures.push_back(boxarr->str(6));
|
||||
def.modelTextures.push_back(boxarr->str(6));
|
||||
}
|
||||
else if (boxarr->size() == 12)
|
||||
for (uint i = 6; i < 12; i++) {
|
||||
def->modelTextures.push_back(boxarr->str(i));
|
||||
def.modelTextures.push_back(boxarr->str(i));
|
||||
}
|
||||
else
|
||||
for (uint i = 6; i < 12; i++) {
|
||||
def->modelTextures.push_back("notfound");
|
||||
def.modelTextures.push_back("notfound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -210,77 +210,81 @@ void ContentLoader::loadCustomBlockModel(Block* def, dynamic::Map* primitives) {
|
||||
glm::vec3 p1(tgonobj->num(0), tgonobj->num(1), tgonobj->num(2)),
|
||||
xw(tgonobj->num(3), tgonobj->num(4), tgonobj->num(5)),
|
||||
yh(tgonobj->num(6), tgonobj->num(7), tgonobj->num(8));
|
||||
def->modelExtraPoints.push_back(p1);
|
||||
def->modelExtraPoints.push_back(p1+xw);
|
||||
def->modelExtraPoints.push_back(p1+xw+yh);
|
||||
def->modelExtraPoints.push_back(p1+yh);
|
||||
def.modelExtraPoints.push_back(p1);
|
||||
def.modelExtraPoints.push_back(p1+xw);
|
||||
def.modelExtraPoints.push_back(p1+xw+yh);
|
||||
def.modelExtraPoints.push_back(p1+yh);
|
||||
|
||||
def->modelTextures.push_back(tgonobj->str(9));
|
||||
def.modelTextures.push_back(tgonobj->str(9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadItem(ItemDef* def, std::string name, fs::path file) {
|
||||
void ContentLoader::loadItem(ItemDef& def, std::string name, fs::path file) {
|
||||
auto root = files::read_json(file);
|
||||
std::string iconTypeStr = "";
|
||||
root->str("icon-type", iconTypeStr);
|
||||
if (iconTypeStr == "none") {
|
||||
def->iconType = item_icon_type::none;
|
||||
def.iconType = item_icon_type::none;
|
||||
} else if (iconTypeStr == "block") {
|
||||
def->iconType = item_icon_type::block;
|
||||
def.iconType = item_icon_type::block;
|
||||
} else if (iconTypeStr == "sprite") {
|
||||
def->iconType = item_icon_type::sprite;
|
||||
def.iconType = item_icon_type::sprite;
|
||||
} else if (iconTypeStr.length()){
|
||||
std::cerr << "unknown icon type" << iconTypeStr << std::endl;
|
||||
}
|
||||
root->str("icon", def->icon);
|
||||
root->str("placing-block", def->placingBlock);
|
||||
root->str("script-name", def->scriptName);
|
||||
root->num("stack-size", def->stackSize);
|
||||
root->str("icon", def.icon);
|
||||
root->str("placing-block", def.placingBlock);
|
||||
root->str("script-name", def.scriptName);
|
||||
root->num("stack-size", def.stackSize);
|
||||
|
||||
// item light emission [r, g, b] where r,g,b in range [0..15]
|
||||
auto emissionarr = root->list("emission");
|
||||
if (emissionarr) {
|
||||
def->emission[0] = emissionarr->num(0);
|
||||
def->emission[1] = emissionarr->num(1);
|
||||
def->emission[2] = emissionarr->num(2);
|
||||
def.emission[0] = emissionarr->num(0);
|
||||
def.emission[1] = emissionarr->num(1);
|
||||
def.emission[2] = emissionarr->num(2);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadBlock(Block* def, std::string full, std::string name) {
|
||||
void ContentLoader::loadBlock(Block& def, std::string full, std::string name) {
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path configFile = folder/fs::path("blocks/"+name+".json");
|
||||
loadBlock(def, full, configFile);
|
||||
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def->scriptName+".lua");
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_block_script(full, scriptfile, &def->rt.funcsset);
|
||||
scripting::load_block_script(env, full, scriptfile, def.rt.funcsset);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::loadItem(ItemDef* def, std::string full, std::string name) {
|
||||
void ContentLoader::loadItem(ItemDef& def, std::string full, std::string name) {
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path configFile = folder/fs::path("items/"+name+".json");
|
||||
loadItem(def, full, configFile);
|
||||
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def->scriptName+".lua");
|
||||
fs::path scriptfile = folder/fs::path("scripts/"+def.scriptName+".lua");
|
||||
if (fs::is_regular_file(scriptfile)) {
|
||||
scripting::load_item_script(full, scriptfile, &def->rt.funcsset);
|
||||
scripting::load_item_script(env, full, scriptfile, def.rt.funcsset);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentLoader::load(ContentBuilder* builder) {
|
||||
void ContentLoader::load(ContentBuilder& builder) {
|
||||
std::cout << "-- loading pack [" << pack->id << "]" << std::endl;
|
||||
|
||||
auto runtime = new ContentPackRuntime(*pack, scripting::create_pack_environment(*pack));
|
||||
builder.add(runtime);
|
||||
env = runtime->getEnvironment()->getId();
|
||||
|
||||
fixPackIndices();
|
||||
|
||||
auto folder = pack->folder;
|
||||
|
||||
fs::path scriptFile = folder/fs::path("scripts/world.lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_world_script(pack->id, scriptFile);
|
||||
scripting::load_world_script(env, pack->id, scriptFile);
|
||||
}
|
||||
|
||||
if (!fs::is_regular_file(pack->getContentFile()))
|
||||
@ -291,17 +295,17 @@ void ContentLoader::load(ContentBuilder* builder) {
|
||||
for (uint i = 0; i < blocksarr->size(); i++) {
|
||||
std::string name = blocksarr->str(i);
|
||||
std::string full = pack->id+":"+name;
|
||||
auto def = builder->createBlock(full);
|
||||
auto& def = builder.createBlock(full);
|
||||
loadBlock(def, full, name);
|
||||
if (!def->hidden) {
|
||||
auto item = builder->createItem(full+BLOCK_ITEM_SUFFIX);
|
||||
item->generated = true;
|
||||
item->iconType = item_icon_type::block;
|
||||
item->icon = full;
|
||||
item->placingBlock = full;
|
||||
if (!def.hidden) {
|
||||
auto& item = builder.createItem(full+BLOCK_ITEM_SUFFIX);
|
||||
item.generated = true;
|
||||
item.iconType = item_icon_type::block;
|
||||
item.icon = full;
|
||||
item.placingBlock = full;
|
||||
|
||||
for (uint j = 0; j < 4; j++) {
|
||||
item->emission[j] = def->emission[j];
|
||||
item.emission[j] = def.emission[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,7 +316,7 @@ void ContentLoader::load(ContentBuilder* builder) {
|
||||
for (uint i = 0; i < itemsarr->size(); i++) {
|
||||
std::string name = itemsarr->str(i);
|
||||
std::string full = pack->id+":"+name;
|
||||
loadItem(builder->createItem(full), full, name);
|
||||
loadItem(builder.createItem(full), full, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,10 +17,11 @@ namespace dynamic {
|
||||
|
||||
class ContentLoader {
|
||||
const ContentPack* pack;
|
||||
int env = 0;
|
||||
|
||||
void loadBlock(Block* def, std::string full, std::string name);
|
||||
void loadCustomBlockModel(Block* def, dynamic::Map* primitives);
|
||||
void loadItem(ItemDef* def, std::string full, std::string name);
|
||||
void loadBlock(Block& def, std::string full, std::string name);
|
||||
void loadCustomBlockModel(Block& def, dynamic::Map* primitives);
|
||||
void loadItem(ItemDef& def, std::string full, std::string name);
|
||||
public:
|
||||
ContentLoader(ContentPack* pack);
|
||||
|
||||
@ -28,9 +29,9 @@ public:
|
||||
dynamic::Map* indicesRoot,
|
||||
std::string contentSection);
|
||||
void fixPackIndices();
|
||||
void loadBlock(Block* def, std::string name, fs::path file);
|
||||
void loadItem(ItemDef* def, std::string name, fs::path file);
|
||||
void load(ContentBuilder* builder);
|
||||
void loadBlock(Block& def, std::string name, fs::path file);
|
||||
void loadItem(ItemDef& def, std::string name, fs::path file);
|
||||
void load(ContentBuilder& builder);
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_LOADER_H_
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "../files/files.h"
|
||||
#include "../files/engine_paths.h"
|
||||
#include "../data/dynamic.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -149,3 +150,10 @@ void ContentPack::readPacks(const EnginePaths* paths,
|
||||
packs.push_back(ContentPack::read(packfolder));
|
||||
}
|
||||
}
|
||||
|
||||
ContentPackRuntime::ContentPackRuntime(
|
||||
ContentPack info,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
) : info(info), env(std::move(env))
|
||||
{
|
||||
}
|
||||
|
||||
@ -8,16 +8,22 @@
|
||||
|
||||
class EnginePaths;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
class contentpack_error : public std::runtime_error {
|
||||
std::string packId;
|
||||
std::filesystem::path folder;
|
||||
fs::path folder;
|
||||
public:
|
||||
contentpack_error(std::string packId,
|
||||
std::filesystem::path folder,
|
||||
fs::path folder,
|
||||
std::string message);
|
||||
|
||||
std::string getPackId() const;
|
||||
std::filesystem::path getFolder() const;
|
||||
fs::path getFolder() const;
|
||||
};
|
||||
|
||||
struct ContentPack {
|
||||
@ -26,35 +32,61 @@ struct ContentPack {
|
||||
std::string version = "0.0";
|
||||
std::string creator = "";
|
||||
std::string description = "no description";
|
||||
std::filesystem::path folder;
|
||||
fs::path folder;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
std::filesystem::path getContentFile() const;
|
||||
fs::path getContentFile() const;
|
||||
|
||||
static const std::string PACKAGE_FILENAME;
|
||||
static const std::string CONTENT_FILENAME;
|
||||
static const std::filesystem::path BLOCKS_FOLDER;
|
||||
static const std::filesystem::path ITEMS_FOLDER;
|
||||
static const fs::path BLOCKS_FOLDER;
|
||||
static const fs::path ITEMS_FOLDER;
|
||||
static const std::vector<std::string> RESERVED_NAMES;
|
||||
|
||||
static bool is_pack(std::filesystem::path folder);
|
||||
static ContentPack read(std::filesystem::path folder);
|
||||
static bool is_pack(fs::path folder);
|
||||
static ContentPack read(fs::path folder);
|
||||
|
||||
static void scan(std::filesystem::path folder,
|
||||
static void scan(fs::path folder,
|
||||
std::vector<ContentPack>& packs);
|
||||
static void scan(EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs);
|
||||
|
||||
static std::vector<std::string> worldPacksList(std::filesystem::path folder);
|
||||
static std::vector<std::string> worldPacksList(fs::path folder);
|
||||
|
||||
static std::filesystem::path findPack(
|
||||
static fs::path findPack(
|
||||
const EnginePaths* paths,
|
||||
std::filesystem::path worldDir,
|
||||
std::string name);
|
||||
static void readPacks(const EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs,
|
||||
const std::vector<std::string>& names,
|
||||
std::filesystem::path worldDir);
|
||||
fs::path worldDir,
|
||||
std::string name
|
||||
);
|
||||
|
||||
static void readPacks(
|
||||
const EnginePaths* paths,
|
||||
std::vector<ContentPack>& packs,
|
||||
const std::vector<std::string>& names,
|
||||
fs::path worldDir
|
||||
);
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
ContentPack info;
|
||||
std::unique_ptr<scripting::Environment> env;
|
||||
public:
|
||||
ContentPackRuntime(
|
||||
ContentPack info,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
);
|
||||
|
||||
inline const std::string& getId() {
|
||||
return info.id;
|
||||
}
|
||||
|
||||
inline const ContentPack& getInfo() const {
|
||||
return info;
|
||||
}
|
||||
|
||||
inline scripting::Environment* getEnvironment() const {
|
||||
return env.get();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONTENT_CONTENT_PACK_H_
|
||||
|
||||
@ -9,18 +9,18 @@
|
||||
|
||||
// All in-game definitions (blocks, items, etc..)
|
||||
void corecontent::setup(ContentBuilder* builder) {
|
||||
Block* block = builder->createBlock("core:air");
|
||||
block->replaceable = true;
|
||||
block->drawGroup = 1;
|
||||
block->lightPassing = true;
|
||||
block->skyLightPassing = true;
|
||||
block->obstacle = false;
|
||||
block->selectable = false;
|
||||
block->model = BlockModel::none;
|
||||
block->pickingItem = "core:empty";
|
||||
Block& block = builder->createBlock("core:air");
|
||||
block.replaceable = true;
|
||||
block.drawGroup = 1;
|
||||
block.lightPassing = true;
|
||||
block.skyLightPassing = true;
|
||||
block.obstacle = false;
|
||||
block.selectable = false;
|
||||
block.model = BlockModel::none;
|
||||
block.pickingItem = "core:empty";
|
||||
|
||||
ItemDef* item = builder->createItem("core:empty");
|
||||
item->iconType = item_icon_type::none;
|
||||
ItemDef& item = builder->createItem("core:empty");
|
||||
item.iconType = item_icon_type::none;
|
||||
}
|
||||
|
||||
void corecontent::setup_bindings() {
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
typedef std::function<void()> runnable;
|
||||
typedef std::function<void(const std::string&)> stringconsumer;
|
||||
using runnable = std::function<void()>;
|
||||
using stringconsumer = std::function<void(const std::string&)>;
|
||||
using wstringsupplier = std::function<std::wstring()>;
|
||||
using wstringconsumer = std::function<void(const std::wstring&)>;
|
||||
|
||||
#endif // DELEGATES_H_
|
||||
|
||||
@ -42,7 +42,8 @@
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
Engine::Engine(EngineSettings& settings, EnginePaths* paths)
|
||||
: settings(settings), paths(paths) {
|
||||
: settings(settings), paths(paths)
|
||||
{
|
||||
if (Window::initialize(settings.display)){
|
||||
throw initialize_error("could not initialize window");
|
||||
}
|
||||
@ -52,18 +53,21 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths)
|
||||
|
||||
std::cout << "-- loading assets" << std::endl;
|
||||
std::vector<fs::path> roots {resdir};
|
||||
resPaths.reset(new ResPaths(resdir, roots));
|
||||
assets.reset(new Assets());
|
||||
|
||||
resPaths = std::make_unique<ResPaths>(resdir, roots);
|
||||
assets = std::make_unique<Assets>();
|
||||
|
||||
|
||||
AssetsLoader loader(assets.get(), resPaths.get());
|
||||
AssetsLoader::createDefaults(loader);
|
||||
AssetsLoader::addDefaults(loader, false);
|
||||
AssetsLoader::addDefaults(loader, nullptr);
|
||||
|
||||
Shader::preprocessor->setPaths(resPaths.get());
|
||||
while (loader.hasNext()) {
|
||||
if (!loader.loadNext()) {
|
||||
assets.reset();
|
||||
scripting::close();
|
||||
Window::terminate();
|
||||
throw initialize_error("could not to initialize assets");
|
||||
throw initialize_error("could not to load assets");
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +77,6 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths)
|
||||
settings.ui.language = langs::locale_by_envlocale(platform::detect_locale(), paths->getResources());
|
||||
}
|
||||
setLanguage(settings.ui.language);
|
||||
std::cout << "-- initializing finished" << std::endl;
|
||||
}
|
||||
|
||||
void Engine::updateTimers() {
|
||||
@ -98,12 +101,11 @@ void Engine::updateHotkeys() {
|
||||
|
||||
void Engine::mainloop() {
|
||||
setScreen(std::make_shared<MenuScreen>(this));
|
||||
|
||||
std::cout << "-- preparing systems" << std::endl;
|
||||
|
||||
Batch2D batch(1024);
|
||||
lastTime = Window::time();
|
||||
|
||||
std::cout << "-- initialized" << std::endl;
|
||||
while (!Window::isShouldClose()){
|
||||
assert(screen != nullptr);
|
||||
updateTimers();
|
||||
@ -129,13 +131,13 @@ void Engine::mainloop() {
|
||||
}
|
||||
|
||||
Engine::~Engine() {
|
||||
screen = nullptr;
|
||||
scripting::close();
|
||||
std::cout << "-- shutting down" << std::endl;
|
||||
screen.reset();
|
||||
content.reset();
|
||||
|
||||
Audio::finalize();
|
||||
|
||||
std::cout << "-- shutting down" << std::endl;
|
||||
assets.reset();
|
||||
scripting::close();
|
||||
Window::terminate();
|
||||
std::cout << "-- engine finished" << std::endl;
|
||||
}
|
||||
@ -163,13 +165,14 @@ void Engine::loadContent() {
|
||||
for (auto& pack : srcPacks) {
|
||||
if(loadedPacks.find(pack.id) != loadedPacks.end()) continue;
|
||||
missingDependency = checkPacks(existingPacks, pack.dependencies);
|
||||
if(!missingDependency.empty()) throw contentpack_error(pack.id, pack.folder, "missing dependency '"+missingDependency+"'");
|
||||
if(!missingDependency.empty())
|
||||
throw contentpack_error(pack.id, pack.folder, "missing dependency '"+missingDependency+"'");
|
||||
if(pack.dependencies.empty() || checkPacks(loadedPacks, pack.dependencies).empty()) {
|
||||
loadedPacks.insert(pack.id);
|
||||
resRoots.push_back(pack.folder);
|
||||
contentPacks.push_back(pack);
|
||||
ContentLoader loader(&pack);
|
||||
loader.load(&contentBuilder);
|
||||
loader.load(contentBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,8 +185,7 @@ void Engine::loadContent() {
|
||||
std::unique_ptr<Assets> new_assets(new Assets());
|
||||
std::cout << "-- loading assets" << std::endl;
|
||||
AssetsLoader loader(new_assets.get(), resPaths.get());
|
||||
AssetsLoader::createDefaults(loader);
|
||||
AssetsLoader::addDefaults(loader, true);
|
||||
AssetsLoader::addDefaults(loader, content.get());
|
||||
while (loader.hasNext()) {
|
||||
if (!loader.loadNext()) {
|
||||
new_assets.reset();
|
||||
@ -196,7 +198,6 @@ void Engine::loadContent() {
|
||||
void Engine::loadWorldContent(const fs::path& folder) {
|
||||
contentPacks.clear();
|
||||
auto packNames = ContentPack::worldPacksList(folder);
|
||||
std::cout << folder << " " << packNames.size() << std::endl;
|
||||
ContentPack::readPacks(paths, contentPacks, packNames, folder);
|
||||
loadContent();
|
||||
}
|
||||
@ -243,4 +244,4 @@ EnginePaths* Engine::getPaths() {
|
||||
|
||||
std::shared_ptr<Screen> Engine::getScreen() {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,4 +67,4 @@ public:
|
||||
std::shared_ptr<Screen> getScreen();
|
||||
};
|
||||
|
||||
#endif // SRC_ENGINE_H_
|
||||
#endif // SRC_ENGINE_H_
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
const size_t BUFFER_SIZE_UNKNOWN = -1;
|
||||
|
||||
regfile::regfile(fs::path filename) : file(filename) {
|
||||
if (file.length() < REGION_HEADER_SIZE)
|
||||
throw std::runtime_error("incomplete region file header");
|
||||
@ -217,7 +219,7 @@ void WorldFiles::put(Chunk* chunk){
|
||||
for (auto& entry : inventories) {
|
||||
builder.putInt32(entry.first);
|
||||
auto map = entry.second->serialize();
|
||||
auto bytes = json::to_binary(map.get(), true);
|
||||
auto bytes = json::to_binary(map.get(), false);
|
||||
builder.putInt32(bytes.size());
|
||||
builder.put(bytes.data(), bytes.size());
|
||||
}
|
||||
@ -226,8 +228,8 @@ void WorldFiles::put(Chunk* chunk){
|
||||
|
||||
auto datavec = builder.data();
|
||||
uint datasize = builder.size();
|
||||
auto data = std::make_unique<ubyte[]>(builder.size());
|
||||
for (uint i = 0; i < builder.size(); i++) {
|
||||
auto data = std::make_unique<ubyte[]>(datasize);
|
||||
for (uint i = 0; i < datasize; i++) {
|
||||
data[i] = datavec[i];
|
||||
}
|
||||
region->put(localX, localZ, data.release(), datasize);
|
||||
@ -289,20 +291,39 @@ fs::path WorldFiles::getPacksFile() const {
|
||||
}
|
||||
|
||||
ubyte* WorldFiles::getChunk(int x, int z){
|
||||
return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS);
|
||||
return getData(regions, getRegionsFolder(), x, z, REGION_LAYER_VOXELS, true);
|
||||
}
|
||||
|
||||
/* Get cached lights for chunk at x,z
|
||||
* @return lights data or nullptr */
|
||||
light_t* WorldFiles::getLights(int x, int z) {
|
||||
std::unique_ptr<ubyte> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS));
|
||||
std::unique_ptr<ubyte[]> data (getData(lights, getLightsFolder(), x, z, REGION_LAYER_LIGHTS, true));
|
||||
if (data == nullptr)
|
||||
return nullptr;
|
||||
return Lightmap::decode(data.get());
|
||||
}
|
||||
|
||||
chunk_inventories_map WorldFiles::fetchInventories(int x, int z) {
|
||||
chunk_inventories_map inventories;
|
||||
const ubyte* data = getData(storages, getInventoriesFolder(), x, z, REGION_LAYER_INVENTORIES, false);
|
||||
if (data == nullptr)
|
||||
return inventories;
|
||||
ByteReader reader(data, BUFFER_SIZE_UNKNOWN);
|
||||
int count = reader.getInt32();
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint index = reader.getInt32();
|
||||
uint size = reader.getInt32();
|
||||
auto map = json::from_binary(reader.pointer(), size);
|
||||
reader.skip(size);
|
||||
auto inv = std::make_shared<Inventory>(0, 0);
|
||||
inv->deserialize(map.get());
|
||||
inventories[index] = inv;
|
||||
}
|
||||
return inventories;
|
||||
}
|
||||
|
||||
ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
|
||||
int x, int z, int layer) {
|
||||
int x, int z, int layer, bool compression) {
|
||||
int regionX = floordiv(x, REGION_SIZE);
|
||||
int regionZ = floordiv(z, REGION_SIZE);
|
||||
|
||||
@ -320,7 +341,10 @@ ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
|
||||
}
|
||||
if (data != nullptr) {
|
||||
size_t size = region->getChunkDataSize(localX, localZ);
|
||||
return decompress(data, size, CHUNK_DATA_LEN);
|
||||
if (compression) {
|
||||
return decompress(data, size, CHUNK_DATA_LEN);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -372,17 +396,16 @@ ubyte* WorldFiles::readChunkData(int x,
|
||||
file.seekg(table_offset + chunkIndex * 4);
|
||||
file.read((char*)(&offset), 4);
|
||||
offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
|
||||
|
||||
if (offset == 0){
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.seekg(offset);
|
||||
file.read((char*)(&offset), 4);
|
||||
length = dataio::read_int32_big((const ubyte*)(&offset), 0);
|
||||
ubyte* data = new ubyte[length];
|
||||
ubyte* data = new ubyte[length]{};
|
||||
file.read((char*)data, length);
|
||||
if (data == nullptr) {
|
||||
std::cerr << "ERROR: failed to read data of chunk x("<< x <<"), z("<< z <<")" << std::endl;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -482,7 +505,7 @@ void WorldFiles::write(const World* world, const Content* content) {
|
||||
writeIndices(content->getIndices());
|
||||
writeRegions(regions, regionsFolder, REGION_LAYER_VOXELS);
|
||||
writeRegions(lights, lightsFolder, REGION_LAYER_LIGHTS);
|
||||
writeRegions(storages, inventoriesFolder, REGION_LAYER_STORAGES);
|
||||
writeRegions(storages, inventoriesFolder, REGION_LAYER_INVENTORIES);
|
||||
}
|
||||
|
||||
void WorldFiles::writePacks(const World* world) {
|
||||
|
||||
@ -15,11 +15,13 @@
|
||||
#include "../typedefs.h"
|
||||
#include "../settings.h"
|
||||
|
||||
#include "../voxels/Chunk.h"
|
||||
|
||||
const uint REGION_HEADER_SIZE = 10;
|
||||
|
||||
const uint REGION_LAYER_VOXELS = 0;
|
||||
const uint REGION_LAYER_LIGHTS = 1;
|
||||
const uint REGION_LAYER_STORAGES = 2;
|
||||
const uint REGION_LAYER_INVENTORIES = 2;
|
||||
|
||||
const uint REGION_SIZE_BIT = 5;
|
||||
const uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
|
||||
@ -32,7 +34,6 @@ const uint MAX_OPEN_REGION_FILES = 16;
|
||||
#define WORLD_FORMAT_MAGIC ".VOXWLD"
|
||||
|
||||
class Player;
|
||||
class Chunk;
|
||||
class Content;
|
||||
class ContentIndices;
|
||||
class World;
|
||||
@ -109,7 +110,7 @@ class WorldFiles {
|
||||
|
||||
ubyte* getData(regionsmap& regions,
|
||||
const fs::path& folder,
|
||||
int x, int z, int layer);
|
||||
int x, int z, int layer, bool compression);
|
||||
|
||||
regfile* getRegFile(glm::ivec3 coord, const fs::path& folder);
|
||||
|
||||
@ -139,6 +140,7 @@ public:
|
||||
|
||||
ubyte* getChunk(int x, int z);
|
||||
light_t* getLights(int x, int z);
|
||||
chunk_inventories_map fetchInventories(int x, int z);
|
||||
|
||||
bool readWorldInfo(World* world);
|
||||
bool readPlayer(Player* player);
|
||||
|
||||
@ -159,3 +159,7 @@ std::vector<fs::path> ResPaths::listdir(const std::string& folderName) const {
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
const fs::path& ResPaths::getMainRoot() const {
|
||||
return mainRoot;
|
||||
}
|
||||
|
||||
@ -42,6 +42,8 @@ public:
|
||||
|
||||
fs::path find(const std::string& filename) const;
|
||||
std::vector<fs::path> listdir(const std::string& folder) const;
|
||||
|
||||
const fs::path& getMainRoot() const;
|
||||
};
|
||||
|
||||
#endif // FILES_ENGINE_PATHS_H_
|
||||
#endif // FILES_ENGINE_PATHS_H_
|
||||
|
||||
@ -32,6 +32,7 @@ toml::Wrapper* create_wrapper(EngineSettings& settings) {
|
||||
camera.add("sensitivity", &settings.camera.sensitivity);
|
||||
|
||||
toml::Section& graphics = wrapper->add("graphics");
|
||||
graphics.add("gamma", &settings.graphics.gamma);
|
||||
graphics.add("fog-curve", &settings.graphics.fogCurve);
|
||||
graphics.add("backlight", &settings.graphics.backlight);
|
||||
graphics.add("frustum-culling", &settings.graphics.frustumCulling);
|
||||
|
||||
@ -4,38 +4,49 @@
|
||||
|
||||
#include "../assets/Assets.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../content/ContentPack.h"
|
||||
#include "../graphics/Atlas.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../core_defs.h"
|
||||
#include "UiDocument.h"
|
||||
|
||||
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) {
|
||||
ContentGfxCache::ContentGfxCache(const Content* content, Assets* assets) : content(content) {
|
||||
auto indices = content->getIndices();
|
||||
sideregions = new UVRegion[indices->countBlockDefs() * 6];
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
for (uint i = 0; i < indices->countBlockDefs(); i++) {
|
||||
Block* def = indices->getBlockDef(i);
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
std::string tex = def->textureFaces[side];
|
||||
if (atlas->has(tex)) {
|
||||
sideregions[i * 6 + side] = atlas->get(tex);
|
||||
} else {
|
||||
if (atlas->has("notfound"))
|
||||
sideregions[i * 6 + side] = atlas->get("notfound");
|
||||
}
|
||||
}
|
||||
for (uint side = 0; side < def->modelTextures.size(); side++)
|
||||
{
|
||||
std::string tex = def->modelTextures[side];
|
||||
if (atlas->has(tex)) {
|
||||
def->modelUVs.push_back(atlas->get(tex));
|
||||
} else {
|
||||
if (atlas->has("notfound"))
|
||||
def->modelUVs.push_back(atlas->get("notfound"));
|
||||
}
|
||||
}
|
||||
sideregions = std::make_unique<UVRegion[]>(indices->countBlockDefs() * 6);
|
||||
Atlas* atlas = assets->getAtlas("blocks");
|
||||
|
||||
for (uint i = 0; i < indices->countBlockDefs(); i++) {
|
||||
Block* def = indices->getBlockDef(i);
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
const std::string& tex = def->textureFaces[side];
|
||||
if (atlas->has(tex)) {
|
||||
sideregions[i * 6 + side] = atlas->get(tex);
|
||||
} else if (atlas->has(TEXTURE_NOTFOUND)) {
|
||||
sideregions[i * 6 + side] = atlas->get(TEXTURE_NOTFOUND);
|
||||
}
|
||||
}
|
||||
for (uint side = 0; side < def->modelTextures.size(); side++) {
|
||||
const std::string& tex = def->modelTextures[side];
|
||||
if (atlas->has(tex)) {
|
||||
def->modelUVs.push_back(atlas->get(tex));
|
||||
} else if (atlas->has(TEXTURE_NOTFOUND)) {
|
||||
def->modelUVs.push_back(atlas->get(TEXTURE_NOTFOUND));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentGfxCache::~ContentGfxCache() {
|
||||
delete[] sideregions;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiDocument> ContentGfxCache::getLayout(const std::string& id) {
|
||||
auto found = layouts.find(id);
|
||||
if (found == layouts.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
const Content* ContentGfxCache::getContent() const {
|
||||
return content;
|
||||
}
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
#ifndef FRONTEND_BLOCKS_GFX_CACHE_H_
|
||||
#define FRONTEND_BLOCKS_GFX_CACHE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "../graphics/UVRegion.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
class Content;
|
||||
class Assets;
|
||||
class UiDocument;
|
||||
|
||||
using uidocuments_map = std::unordered_map<std::string, std::shared_ptr<UiDocument>>;
|
||||
|
||||
class ContentGfxCache {
|
||||
const Content* content;
|
||||
// array of block sides uv regions (6 per block)
|
||||
UVRegion* sideregions;
|
||||
std::unique_ptr<UVRegion[]> sideregions;
|
||||
// all loaded layouts
|
||||
uidocuments_map layouts;
|
||||
public:
|
||||
ContentGfxCache(const Content* content, Assets* assets);
|
||||
~ContentGfxCache();
|
||||
@ -17,6 +26,10 @@ public:
|
||||
inline const UVRegion& getRegion(blockid_t id, int side) const {
|
||||
return sideregions[id * 6 + side];
|
||||
}
|
||||
|
||||
std::shared_ptr<UiDocument> getLayout(const std::string& id);
|
||||
|
||||
const Content* getContent() const;
|
||||
};
|
||||
|
||||
#endif // FRONTEND_BLOCKS_GFX_CACHE_H_
|
||||
|
||||
@ -19,65 +19,30 @@
|
||||
#include "../maths/voxmaths.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../voxels/Block.h"
|
||||
#include "../frontend/gui/panels.h"
|
||||
#include "../frontend/gui/controls.h"
|
||||
#include "../util/stringutil.h"
|
||||
|
||||
InventoryLayout::InventoryLayout(glm::vec2 size) : size(size) {}
|
||||
|
||||
void InventoryLayout::add(SlotLayout slot) {
|
||||
slots.push_back(slot);
|
||||
}
|
||||
|
||||
void InventoryLayout::add(InventoryPanel panel) {
|
||||
panels.push_back(panel);
|
||||
}
|
||||
|
||||
void InventoryLayout::setSize(glm::vec2 size) {
|
||||
this->size = size;
|
||||
}
|
||||
|
||||
void InventoryLayout::setOrigin(glm::vec2 origin) {
|
||||
this->origin = origin;
|
||||
}
|
||||
|
||||
glm::vec2 InventoryLayout::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
glm::vec2 InventoryLayout::getOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
std::vector<SlotLayout>& InventoryLayout::getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
std::vector<InventoryPanel>& InventoryLayout::getPanels() {
|
||||
return panels;
|
||||
}
|
||||
#include "../world/Level.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
SlotLayout::SlotLayout(
|
||||
int index,
|
||||
glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
itemsharefunc shareFunc,
|
||||
slotcallback rightClick
|
||||
)
|
||||
: position(position),
|
||||
: index(index),
|
||||
position(position),
|
||||
background(background),
|
||||
itemSource(itemSource),
|
||||
shareFunc(shareFunc),
|
||||
rightClick(rightClick) {}
|
||||
|
||||
InventoryPanel::InventoryPanel(
|
||||
glm::vec2 position,
|
||||
glm::vec2 size,
|
||||
glm::vec4 color)
|
||||
: position(position), size(size), color(color) {}
|
||||
|
||||
InventoryBuilder::InventoryBuilder()
|
||||
: layout(std::make_unique<InventoryLayout>(glm::vec2()))
|
||||
{}
|
||||
InventoryBuilder::InventoryBuilder() {
|
||||
view = std::make_shared<InventoryView>();
|
||||
}
|
||||
|
||||
void InventoryBuilder::addGrid(
|
||||
int cols, int count,
|
||||
@ -94,14 +59,20 @@ void InventoryBuilder::addGrid(
|
||||
uint width = cols * (slotSize + interval) - interval + padding*2;
|
||||
uint height = rows * (slotSize + interval) - interval + padding*2;
|
||||
|
||||
auto lsize = layout->getSize();
|
||||
if (coord.x + width > lsize.x) {
|
||||
lsize.x = coord.x + width;
|
||||
glm::vec2 vsize = view->getSize();
|
||||
if (coord.x + width > vsize.x) {
|
||||
vsize.x = coord.x + width;
|
||||
}
|
||||
if (coord.y + height > lsize.y) {
|
||||
lsize.y = coord.y + height;
|
||||
if (coord.y + height > vsize.y) {
|
||||
vsize.y = coord.y + height;
|
||||
}
|
||||
view->setSize(vsize);
|
||||
|
||||
if (addpanel) {
|
||||
auto panel = std::make_shared<gui::Container>(coord, glm::vec2(width, height));
|
||||
view->setColor(glm::vec4(0, 0, 0, 0.5f));
|
||||
view->add(panel);
|
||||
}
|
||||
layout->setSize(lsize);
|
||||
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
@ -112,59 +83,34 @@ void InventoryBuilder::addGrid(
|
||||
row * (slotSize + interval) + padding
|
||||
);
|
||||
auto builtSlot = slotLayout;
|
||||
builtSlot.index = row * cols + col;
|
||||
builtSlot.position = position;
|
||||
layout->add(builtSlot);
|
||||
add(builtSlot);
|
||||
}
|
||||
}
|
||||
|
||||
if (addpanel) {
|
||||
add(InventoryPanel(
|
||||
coord,
|
||||
glm::vec2(width, height),
|
||||
glm::vec4(0, 0, 0, 0.5f)));
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryBuilder::add(SlotLayout slotLayout) {
|
||||
uint width = InventoryView::SLOT_SIZE;
|
||||
uint height = InventoryView::SLOT_SIZE;
|
||||
|
||||
auto coord = slotLayout.position;
|
||||
auto lsize = layout->getSize();
|
||||
if (coord.x + width > lsize.x) {
|
||||
lsize.x = coord.x + width;
|
||||
}
|
||||
if (coord.y + height > lsize.y) {
|
||||
lsize.y = coord.y + height;
|
||||
}
|
||||
layout->add(slotLayout);
|
||||
void InventoryBuilder::add(SlotLayout layout) {
|
||||
view->add(view->addSlot(layout), layout.position);
|
||||
}
|
||||
|
||||
void InventoryBuilder::add(InventoryPanel panel) {
|
||||
layout->add(panel);
|
||||
}
|
||||
|
||||
std::unique_ptr<InventoryLayout> InventoryBuilder::build() {
|
||||
return std::unique_ptr<InventoryLayout>(layout.release());
|
||||
std::shared_ptr<InventoryView> InventoryBuilder::build() {
|
||||
return view;
|
||||
}
|
||||
|
||||
SlotView::SlotView(
|
||||
ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
const Content* content,
|
||||
SlotLayout layout)
|
||||
: UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
frontend(frontend),
|
||||
interaction(interaction),
|
||||
content(content),
|
||||
stack(stack),
|
||||
layout(layout) {
|
||||
SlotLayout layout
|
||||
) : UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
|
||||
layout(layout)
|
||||
{
|
||||
setColor(glm::vec4(0, 0, 0, 0.2f));
|
||||
}
|
||||
|
||||
// performance disaster
|
||||
void SlotView::draw(const GfxContext* pctx, Assets* assets) {
|
||||
if (bound == nullptr)
|
||||
throw std::runtime_error("unbound slot");
|
||||
ItemStack& stack = *bound;
|
||||
|
||||
glm::vec2 coord = calcCoord();
|
||||
|
||||
int slotSize = InventoryView::SLOT_SIZE;
|
||||
@ -197,10 +143,10 @@ void SlotView::draw(const GfxContext* pctx, Assets* assets) {
|
||||
case item_icon_type::none:
|
||||
break;
|
||||
case item_icon_type::block: {
|
||||
Block* cblock = content->requireBlock(item->icon);
|
||||
const Block& cblock = content->requireBlock(item->icon);
|
||||
batch->texture(previews->getTexture());
|
||||
|
||||
UVRegion region = previews->get(cblock->name);
|
||||
UVRegion region = previews->get(cblock.name);
|
||||
batch->rect(
|
||||
coord.x, coord.y, slotSize, slotSize,
|
||||
0, 0, 0, region, false, true, tint);
|
||||
@ -250,11 +196,16 @@ bool SlotView::isHighlighted() const {
|
||||
}
|
||||
|
||||
void SlotView::clicked(gui::GUI* gui, int button) {
|
||||
if (bound == nullptr)
|
||||
throw std::runtime_error("unbound slot");
|
||||
|
||||
ItemStack& grabbed = interaction->getGrabbedItem();
|
||||
ItemStack& stack = *bound;
|
||||
|
||||
if (button == mousecode::BUTTON_1) {
|
||||
if (Events::pressed(keycode::LEFT_SHIFT)) {
|
||||
if (layout.shareFunc) {
|
||||
layout.shareFunc(stack);
|
||||
layout.shareFunc(layout.index, stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -302,42 +253,69 @@ void SlotView::focus(gui::GUI* gui) {
|
||||
clicked(gui, 0);
|
||||
}
|
||||
|
||||
InventoryView::InventoryView(
|
||||
const Content* content,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
std::unique_ptr<InventoryLayout> layout)
|
||||
: Container(glm::vec2(), glm::vec2()),
|
||||
content(content),
|
||||
indices(content->getIndices()),
|
||||
inventory(inventory),
|
||||
layout(std::move(layout)),
|
||||
frontend(frontend),
|
||||
interaction(interaction) {
|
||||
setSize(this->layout->getSize());
|
||||
void SlotView::bind(
|
||||
ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
) {
|
||||
bound = &stack;
|
||||
content = frontend->getLevel()->content;
|
||||
this->frontend = frontend;
|
||||
this->interaction = interaction;
|
||||
}
|
||||
|
||||
const SlotLayout& SlotView::getLayout() const {
|
||||
return layout;
|
||||
}
|
||||
|
||||
InventoryView::InventoryView() : Container(glm::vec2(), glm::vec2()) {
|
||||
setColor(glm::vec4(0, 0, 0, 0.0f));
|
||||
}
|
||||
|
||||
InventoryView::~InventoryView() {}
|
||||
|
||||
void InventoryView::build() {
|
||||
size_t index = 0;
|
||||
for (auto& slot : layout->getSlots()) {
|
||||
if (index >= inventory->size())
|
||||
break;
|
||||
|
||||
ItemStack& item = inventory->getSlot(index);
|
||||
std::shared_ptr<SlotView> InventoryView::addSlot(SlotLayout layout) {
|
||||
uint width = InventoryView::SLOT_SIZE + layout.padding;
|
||||
uint height = InventoryView::SLOT_SIZE + layout.padding;
|
||||
|
||||
auto view = std::make_shared<SlotView>(
|
||||
item, frontend, interaction, content, slot
|
||||
auto coord = layout.position;
|
||||
auto vsize = getSize();
|
||||
if (coord.x + width > vsize.x) {
|
||||
vsize.x = coord.x + width;
|
||||
}
|
||||
if (coord.y + height > vsize.y) {
|
||||
vsize.y = coord.y + height;
|
||||
}
|
||||
setSize(vsize);
|
||||
|
||||
auto slot = std::make_shared<SlotView>(layout);
|
||||
if (!layout.background) {
|
||||
slot->setColor(glm::vec4());
|
||||
}
|
||||
slots.push_back(slot.get());
|
||||
return slot;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> InventoryView::getInventory() const {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
void InventoryView::bind(
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
) {
|
||||
this->frontend = frontend;
|
||||
this->interaction = interaction;
|
||||
this->inventory = inventory;
|
||||
content = frontend->getLevel()->content;
|
||||
indices = content->getIndices();
|
||||
for (auto slot : slots) {
|
||||
slot->bind(
|
||||
inventory->getSlot(slot->getLayout().index),
|
||||
frontend, interaction
|
||||
);
|
||||
if (!slot.background) {
|
||||
view->setColor(glm::vec4());
|
||||
}
|
||||
slots.push_back(view.get());
|
||||
add(view, slot.position);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,27 +327,116 @@ void InventoryView::setSelected(int index) {
|
||||
}
|
||||
|
||||
void InventoryView::setCoord(glm::vec2 coord) {
|
||||
Container::setCoord(coord - layout->getOrigin());
|
||||
Container::setCoord(coord - origin);
|
||||
}
|
||||
|
||||
void InventoryView::setOrigin(glm::vec2 origin) {
|
||||
this->origin = origin;
|
||||
}
|
||||
|
||||
glm::vec2 InventoryView::getOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
void InventoryView::setInventory(std::shared_ptr<Inventory> inventory) {
|
||||
this->inventory = inventory;
|
||||
}
|
||||
|
||||
InventoryLayout* InventoryView::getLayout() const {
|
||||
return layout.get();
|
||||
#include "../coders/xml.h"
|
||||
#include "gui/gui_xml.h"
|
||||
|
||||
static itemsharefunc readShareFunc(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelement& element) {
|
||||
auto consumer = scripting::create_int_array_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("sharefunc").getText()
|
||||
);
|
||||
return [=](uint slot, ItemStack& stack) {
|
||||
int args[] {int(view->getInventory()->getId()), int(slot)};
|
||||
consumer(args, 2);
|
||||
};
|
||||
}
|
||||
|
||||
void InventoryView::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 coord = calcCoord();
|
||||
auto batch = pctx->getBatch2D();
|
||||
static void readSlot(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelement element) {
|
||||
int index = element->attr("index", "0").asInt();
|
||||
bool itemSource = element->attr("item-source", "false").asBool();
|
||||
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr);
|
||||
if (element->has("coord")) {
|
||||
layout.position = element->attr("coord").asVec2();
|
||||
}
|
||||
if (element->has("sharefunc")) {
|
||||
layout.shareFunc = readShareFunc(view, reader, element);
|
||||
}
|
||||
auto slot = view->addSlot(layout);
|
||||
reader.readUINode(reader, element, *slot);
|
||||
view->add(slot);
|
||||
}
|
||||
|
||||
batch->texture(nullptr);
|
||||
static void readSlotsGrid(InventoryView* view, gui::UiXmlReader& reader, xml::xmlelement element) {
|
||||
int startIndex = element->attr("start-index", "0").asInt();
|
||||
int rows = element->attr("rows", "0").asInt();
|
||||
int cols = element->attr("cols", "0").asInt();
|
||||
int count = element->attr("count", "0").asInt();
|
||||
const int slotSize = InventoryView::SLOT_SIZE;
|
||||
int interval = element->attr("interval", "-1").asInt();
|
||||
if (interval < 0) {
|
||||
interval = InventoryView::SLOT_INTERVAL;
|
||||
}
|
||||
int padding = element->attr("padding", "-1").asInt();
|
||||
if (padding < 0) {
|
||||
padding = interval;
|
||||
}
|
||||
if (rows == 0) {
|
||||
rows = ceildiv(count, cols);
|
||||
} else if (cols == 0) {
|
||||
cols = ceildiv(count, rows);
|
||||
} else if (count == 0) {
|
||||
count = rows * cols;
|
||||
}
|
||||
bool itemSource = element->attr("item-source", "false").asBool();
|
||||
SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr);
|
||||
if (element->has("coord")) {
|
||||
layout.position = element->attr("coord").asVec2();
|
||||
}
|
||||
if (element->has("sharefunc")) {
|
||||
layout.shareFunc = readShareFunc(view, reader, element);
|
||||
}
|
||||
layout.padding = padding;
|
||||
|
||||
for (auto& panel : layout->getPanels()) {
|
||||
glm::vec2 size = panel.size;
|
||||
glm::vec2 pos = coord + panel.position;
|
||||
batch->color = panel.color;
|
||||
batch->rect(pos.x-1, pos.y-1, size.x+2, size.y+2);
|
||||
glm::vec2 size (
|
||||
cols * slotSize + (cols - 1) * interval + padding * 2,
|
||||
rows * slotSize + (rows - 1) * interval + padding * 2
|
||||
);
|
||||
int idx = 0;
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++, idx++) {
|
||||
if (idx >= count) {
|
||||
return;
|
||||
}
|
||||
SlotLayout slotLayout = layout;
|
||||
slotLayout.index = startIndex + idx;
|
||||
slotLayout.position += glm::vec2(
|
||||
padding + col * (slotSize + interval),
|
||||
padding + row * (slotSize + interval)
|
||||
);
|
||||
auto slot = view->addSlot(slotLayout);
|
||||
view->add(slot, slotLayout.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryView::createReaders(gui::UiXmlReader& reader) {
|
||||
reader.add("inventory", [=](gui::UiXmlReader& reader, xml::xmlelement element) {
|
||||
auto view = std::make_shared<InventoryView>();
|
||||
reader.addIgnore("slots-grid");
|
||||
reader.readUINode(reader, element, *view);
|
||||
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->getTag() == "slot") {
|
||||
readSlot(view.get(), reader, sub);
|
||||
} else if (sub->getTag() == "slots-grid") {
|
||||
readSlotsGrid(view.get(), reader, sub);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,133 +18,16 @@ class ContentIndices;
|
||||
class LevelFrontend;
|
||||
class Inventory;
|
||||
|
||||
typedef std::function<void(ItemStack&)> itemsharefunc;
|
||||
typedef std::function<void(ItemStack&, ItemStack&)> slotcallback;
|
||||
namespace gui {
|
||||
class UiXmlReader;
|
||||
}
|
||||
|
||||
class InventoryInteraction;
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
struct SlotLayout {
|
||||
glm::vec2 position;
|
||||
bool background;
|
||||
bool itemSource;
|
||||
itemsharefunc shareFunc;
|
||||
slotcallback rightClick;
|
||||
|
||||
SlotLayout(glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
itemsharefunc shareFunc,
|
||||
slotcallback rightClick);
|
||||
};
|
||||
|
||||
// temporary unused
|
||||
struct InventoryPanel {
|
||||
glm::vec2 position;
|
||||
glm::vec2 size;
|
||||
glm::vec4 color;
|
||||
|
||||
InventoryPanel(glm::vec2 position,
|
||||
glm::vec2 size,
|
||||
glm::vec4 color);
|
||||
};
|
||||
|
||||
class InventoryLayout {
|
||||
glm::vec2 size {};
|
||||
glm::vec2 origin {};
|
||||
std::vector<SlotLayout> slots;
|
||||
std::vector<InventoryPanel> panels;
|
||||
public:
|
||||
InventoryLayout(glm::vec2 size);
|
||||
|
||||
void add(SlotLayout slot);
|
||||
void add(InventoryPanel panel);
|
||||
void setSize(glm::vec2 size);
|
||||
void setOrigin(glm::vec2 origin);
|
||||
|
||||
glm::vec2 getSize() const;
|
||||
glm::vec2 getOrigin() const;
|
||||
|
||||
std::vector<SlotLayout>& getSlots();
|
||||
std::vector<InventoryPanel>& getPanels();
|
||||
};
|
||||
|
||||
class InventoryBuilder {
|
||||
std::unique_ptr<InventoryLayout> layout;
|
||||
public:
|
||||
InventoryBuilder();
|
||||
|
||||
void addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 coord,
|
||||
int padding,
|
||||
bool addpanel,
|
||||
SlotLayout slotLayout);
|
||||
|
||||
void add(SlotLayout slotLayout);
|
||||
void add(InventoryPanel panel);
|
||||
|
||||
std::unique_ptr<InventoryLayout> build();
|
||||
};
|
||||
|
||||
class SlotView : public gui::UINode {
|
||||
LevelFrontend* frontend;
|
||||
InventoryInteraction* interaction;
|
||||
const Content* const content;
|
||||
ItemStack& stack;
|
||||
bool highlighted = false;
|
||||
|
||||
SlotLayout layout;
|
||||
public:
|
||||
SlotView(ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
const Content* content,
|
||||
SlotLayout layout);
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
void setHighlighted(bool flag);
|
||||
bool isHighlighted() const;
|
||||
|
||||
virtual void clicked(gui::GUI*, int) override;
|
||||
virtual void focus(gui::GUI*) override;
|
||||
};
|
||||
|
||||
class InventoryView : public gui::Container {
|
||||
const Content* content;
|
||||
const ContentIndices* indices;
|
||||
|
||||
std::shared_ptr<Inventory> inventory;
|
||||
std::unique_ptr<InventoryLayout> layout;
|
||||
LevelFrontend* frontend;
|
||||
InventoryInteraction* interaction;
|
||||
|
||||
std::vector<SlotView*> slots;
|
||||
public:
|
||||
InventoryView(
|
||||
const Content* content,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction,
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
std::unique_ptr<InventoryLayout> layout);
|
||||
|
||||
virtual ~InventoryView();
|
||||
|
||||
void build();
|
||||
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
void setInventory(std::shared_ptr<Inventory> inventory);
|
||||
|
||||
virtual void setCoord(glm::vec2 coord) override;
|
||||
|
||||
InventoryLayout* getLayout() const;
|
||||
|
||||
void setSelected(int index);
|
||||
|
||||
static const int SLOT_INTERVAL = 4;
|
||||
static const int SLOT_SIZE = ITEM_ICON_SIZE;
|
||||
};
|
||||
using itemsharefunc = std::function<void(uint, ItemStack&)>;
|
||||
using slotcallback = std::function<void(ItemStack&, ItemStack&)>;
|
||||
|
||||
class InventoryInteraction {
|
||||
ItemStack grabbedItem;
|
||||
@ -156,4 +39,105 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct SlotLayout {
|
||||
int index;
|
||||
glm::vec2 position;
|
||||
bool background;
|
||||
bool itemSource;
|
||||
itemsharefunc shareFunc;
|
||||
slotcallback rightClick;
|
||||
int padding = 0;
|
||||
|
||||
SlotLayout(int index,
|
||||
glm::vec2 position,
|
||||
bool background,
|
||||
bool itemSource,
|
||||
itemsharefunc shareFunc,
|
||||
slotcallback rightClick);
|
||||
};
|
||||
|
||||
class SlotView : public gui::UINode {
|
||||
LevelFrontend* frontend = nullptr;
|
||||
InventoryInteraction* interaction = nullptr;
|
||||
const Content* content;
|
||||
SlotLayout layout;
|
||||
bool highlighted = false;
|
||||
|
||||
ItemStack* bound = nullptr;
|
||||
public:
|
||||
SlotView(SlotLayout layout);
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
void setHighlighted(bool flag);
|
||||
bool isHighlighted() const;
|
||||
|
||||
virtual void clicked(gui::GUI*, int) override;
|
||||
virtual void focus(gui::GUI*) override;
|
||||
|
||||
void bind(
|
||||
ItemStack& stack,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
);
|
||||
|
||||
const SlotLayout& getLayout() const;
|
||||
};
|
||||
|
||||
class InventoryView : public gui::Container {
|
||||
const Content* content;
|
||||
const ContentIndices* indices;
|
||||
|
||||
std::shared_ptr<Inventory> inventory;
|
||||
LevelFrontend* frontend = nullptr;
|
||||
InventoryInteraction* interaction = nullptr;
|
||||
|
||||
std::vector<SlotView*> slots;
|
||||
glm::vec2 origin {};
|
||||
public:
|
||||
InventoryView();
|
||||
virtual ~InventoryView();
|
||||
|
||||
void setInventory(std::shared_ptr<Inventory> inventory);
|
||||
|
||||
virtual void setCoord(glm::vec2 coord) override;
|
||||
|
||||
void setOrigin(glm::vec2 origin);
|
||||
glm::vec2 getOrigin() const;
|
||||
|
||||
void setSelected(int index);
|
||||
|
||||
void bind(
|
||||
std::shared_ptr<Inventory> inventory,
|
||||
LevelFrontend* frontend,
|
||||
InventoryInteraction* interaction
|
||||
);
|
||||
|
||||
std::shared_ptr<SlotView> addSlot(SlotLayout layout);
|
||||
|
||||
std::shared_ptr<Inventory> getInventory() const;
|
||||
|
||||
static void createReaders(gui::UiXmlReader& reader);
|
||||
|
||||
static const int SLOT_INTERVAL = 4;
|
||||
static const int SLOT_SIZE = ITEM_ICON_SIZE;
|
||||
};
|
||||
|
||||
class InventoryBuilder {
|
||||
std::shared_ptr<InventoryView> view;
|
||||
public:
|
||||
InventoryBuilder();
|
||||
|
||||
void addGrid(
|
||||
int cols, int count,
|
||||
glm::vec2 coord,
|
||||
int padding,
|
||||
bool addpanel,
|
||||
SlotLayout slotLayout
|
||||
);
|
||||
|
||||
void add(SlotLayout slotLayout);
|
||||
std::shared_ptr<InventoryView> build();
|
||||
};
|
||||
|
||||
#endif // FRONTEND_INVENTORY_VIEW_H_
|
||||
|
||||
@ -24,5 +24,4 @@ public:
|
||||
Atlas* getBlocksAtlas() const;
|
||||
};
|
||||
|
||||
|
||||
#endif // FRONTEND_LEVEL_FRONTEND_H_
|
||||
|
||||
70
src/frontend/UiDocument.cpp
Normal file
70
src/frontend/UiDocument.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include "UiDocument.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "gui/UINode.h"
|
||||
#include "gui/panels.h"
|
||||
#include "InventoryView.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
#include "../files/files.h"
|
||||
#include "../frontend/gui/gui_xml.h"
|
||||
|
||||
UiDocument::UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
std::shared_ptr<gui::UINode> root,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
) : id(id), script(script), root(root), env(std::move(env)) {
|
||||
collect(map, root);
|
||||
}
|
||||
|
||||
|
||||
const uinodes_map& UiDocument::getMap() const {
|
||||
return map;
|
||||
}
|
||||
|
||||
const std::string& UiDocument::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
const std::shared_ptr<gui::UINode> UiDocument::getRoot() const {
|
||||
return root;
|
||||
}
|
||||
|
||||
const uidocscript& UiDocument::getScript() const {
|
||||
return script;
|
||||
}
|
||||
|
||||
int UiDocument::getEnvironment() const {
|
||||
return env->getId();
|
||||
}
|
||||
|
||||
void UiDocument::collect(uinodes_map& map, std::shared_ptr<gui::UINode> node) {
|
||||
const std::string& id = node->getId();
|
||||
if (!id.empty()) {
|
||||
map[id] = node;
|
||||
}
|
||||
auto container = dynamic_cast<gui::Container*>(node.get());
|
||||
if (container) {
|
||||
for (auto subnode : container->getNodes()) {
|
||||
collect(map, subnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<UiDocument> UiDocument::read(AssetsLoader& loader, int penv, std::string namesp, fs::path file) {
|
||||
const std::string text = files::read_string(file);
|
||||
auto xmldoc = xml::parse(file.u8string(), text);
|
||||
|
||||
auto env = scripting::create_doc_environment(penv, namesp);
|
||||
gui::UiXmlReader reader(*env, loader);
|
||||
InventoryView::createReaders(reader);
|
||||
auto view = reader.readXML(
|
||||
file.u8string(), xmldoc->getRoot()
|
||||
);
|
||||
uidocscript script {};
|
||||
auto scriptFile = fs::path(file.u8string()+".lua");
|
||||
if (fs::is_regular_file(scriptFile)) {
|
||||
scripting::load_layout_script(env->getId(), namesp, scriptFile, script);
|
||||
}
|
||||
return std::make_unique<UiDocument>(namesp, script, view, std::move(env));
|
||||
}
|
||||
54
src/frontend/UiDocument.h
Normal file
54
src/frontend/UiDocument.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef FRONTEND_UI_DOCUMENT_H_
|
||||
#define FRONTEND_UI_DOCUMENT_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace gui {
|
||||
class UINode;
|
||||
}
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
struct uidocscript {
|
||||
int environment;
|
||||
bool onopen : 1;
|
||||
bool onclose : 1;
|
||||
};
|
||||
|
||||
using uinodes_map = std::unordered_map<std::string, std::shared_ptr<gui::UINode>>;
|
||||
|
||||
class AssetsLoader;
|
||||
|
||||
class UiDocument {
|
||||
std::string id;
|
||||
uidocscript script;
|
||||
uinodes_map map;
|
||||
std::shared_ptr<gui::UINode> root;
|
||||
std::unique_ptr<scripting::Environment> env;
|
||||
public:
|
||||
UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
std::shared_ptr<gui::UINode> root,
|
||||
std::unique_ptr<scripting::Environment> env
|
||||
);
|
||||
|
||||
const std::string& getId() const;
|
||||
const uinodes_map& getMap() const;
|
||||
const std::shared_ptr<gui::UINode> getRoot() const;
|
||||
const uidocscript& getScript() const;
|
||||
int getEnvironment() const;
|
||||
/* Collect map of all uinodes having identifiers */
|
||||
static void collect(uinodes_map& map, std::shared_ptr<gui::UINode> node);
|
||||
|
||||
static std::unique_ptr<UiDocument> read(AssetsLoader& loader, int env, std::string namesp, fs::path file);
|
||||
};
|
||||
|
||||
#endif // FRONTEND_UI_DOCUMENT_H_
|
||||
@ -157,7 +157,7 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool hudVisible
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_proj", camera->getProjection());
|
||||
shader->uniformMatrix("u_view", camera->getView());
|
||||
shader->uniform1f("u_gamma", 1.0f);
|
||||
shader->uniform1f("u_gamma", settings.graphics.gamma);
|
||||
shader->uniform1f("u_fogFactor", fogFactor);
|
||||
shader->uniform1f("u_fogCurve", settings.graphics.fogCurve);
|
||||
shader->uniform3f("u_cameraPos", camera->position);
|
||||
|
||||
@ -5,10 +5,7 @@
|
||||
using gui::UINode;
|
||||
using gui::Align;
|
||||
|
||||
using glm::vec2;
|
||||
using glm::vec4;
|
||||
|
||||
UINode::UINode(vec2 coord, vec2 size) : coord(coord), size(size) {
|
||||
UINode::UINode(glm::vec2 coord, glm::vec2 size) : coord(coord), size(size) {
|
||||
}
|
||||
|
||||
UINode::~UINode() {
|
||||
@ -67,13 +64,13 @@ bool UINode::isFocused() const {
|
||||
}
|
||||
|
||||
bool UINode::isInside(glm::vec2 pos) {
|
||||
vec2 coord = calcCoord();
|
||||
vec2 size = getSize();
|
||||
glm::vec2 coord = calcCoord();
|
||||
glm::vec2 size = getSize();
|
||||
return (pos.x >= coord.x && pos.y >= coord.y &&
|
||||
pos.x < coord.x + size.x && pos.y < coord.y + size.y);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UINode::getAt(vec2 pos, std::shared_ptr<UINode> self) {
|
||||
std::shared_ptr<UINode> UINode::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -96,7 +93,7 @@ bool UINode::isResizing() const {
|
||||
return resizing;
|
||||
}
|
||||
|
||||
vec2 UINode::calcCoord() const {
|
||||
glm::vec2 UINode::calcCoord() const {
|
||||
if (parent) {
|
||||
return coord + parent->calcCoord() + parent->contentOffset();
|
||||
}
|
||||
@ -109,19 +106,19 @@ void UINode::scrolled(int value) {
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setCoord(vec2 coord) {
|
||||
void UINode::setCoord(glm::vec2 coord) {
|
||||
this->coord = coord;
|
||||
}
|
||||
|
||||
vec2 UINode::getSize() const {
|
||||
glm::vec2 UINode::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void UINode::setSize(vec2 size) {
|
||||
void UINode::setSize(glm::vec2 size) {
|
||||
this->size = size;
|
||||
}
|
||||
|
||||
void UINode::setColor(vec4 color) {
|
||||
void UINode::setColor(glm::vec4 color) {
|
||||
this->color = color;
|
||||
this->hoverColor = color;
|
||||
}
|
||||
@ -134,17 +131,33 @@ glm::vec4 UINode::getHoverColor() const {
|
||||
return hoverColor;
|
||||
}
|
||||
|
||||
vec4 UINode::getColor() const {
|
||||
glm::vec4 UINode::getColor() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
void UINode::setMargin(vec4 margin) {
|
||||
void UINode::setMargin(glm::vec4 margin) {
|
||||
this->margin = margin;
|
||||
}
|
||||
|
||||
vec4 UINode::getMargin() const {
|
||||
glm::vec4 UINode::getMargin() const {
|
||||
return margin;
|
||||
}
|
||||
|
||||
void UINode::setZIndex(int zindex) {
|
||||
this->zindex = zindex;
|
||||
}
|
||||
|
||||
int UINode::getZIndex() const {
|
||||
return zindex;
|
||||
}
|
||||
|
||||
void UINode::lock() {
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setId(const std::string& id) {
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
const std::string& UINode::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
class GfxContext;
|
||||
@ -21,6 +22,7 @@ namespace gui {
|
||||
};
|
||||
|
||||
class UINode {
|
||||
std::string id = "";
|
||||
protected:
|
||||
glm::vec2 coord;
|
||||
glm::vec2 size;
|
||||
@ -33,6 +35,7 @@ namespace gui {
|
||||
bool focused = false;
|
||||
bool interactive = true;
|
||||
bool resizing = true;
|
||||
int zindex = 0;
|
||||
Align align = Align::left;
|
||||
UINode* parent = nullptr;
|
||||
UINode(glm::vec2 coord, glm::vec2 size);
|
||||
@ -66,6 +69,9 @@ namespace gui {
|
||||
virtual void setMargin(glm::vec4 margin);
|
||||
glm::vec4 getMargin() const;
|
||||
|
||||
virtual void setZIndex(int idx);
|
||||
int getZIndex() const;
|
||||
|
||||
virtual void focus(GUI*) {focused = true;}
|
||||
virtual void click(GUI*, int x, int y);
|
||||
virtual void clicked(GUI*, int button) {}
|
||||
@ -110,6 +116,9 @@ namespace gui {
|
||||
virtual void setSize(glm::vec2 size);
|
||||
virtual void refresh() {};
|
||||
virtual void lock();
|
||||
|
||||
void setId(const std::string& id);
|
||||
const std::string& getId() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -177,13 +177,20 @@ Button* Button::listenAction(onaction action) {
|
||||
return this;
|
||||
}
|
||||
|
||||
void Button::textAlign(Align align) {
|
||||
void Button::setTextAlign(Align align) {
|
||||
if (label) {
|
||||
label->setAlign(align);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Align Button::getTextAlign() const {
|
||||
if (label) {
|
||||
return label->getAlign();
|
||||
}
|
||||
return Align::left;
|
||||
}
|
||||
|
||||
// ============================== RichButton ==================================
|
||||
RichButton::RichButton(vec2 size) : Container(vec2(), size) {
|
||||
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
|
||||
@ -262,7 +269,7 @@ void TextBox::typed(unsigned int codepoint) {
|
||||
|
||||
bool TextBox::validate() {
|
||||
if (validator) {
|
||||
valid = validator(input);
|
||||
valid = validator(getText());
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
|
||||
@ -17,9 +17,6 @@ class Batch2D;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
using wstringsupplier = std::function<std::wstring()>;
|
||||
using wstringconsumer = std::function<void(std::wstring)>;
|
||||
|
||||
using doublesupplier = std::function<double()>;
|
||||
using doubleconsumer = std::function<void(double)>;
|
||||
|
||||
@ -49,7 +46,7 @@ namespace gui {
|
||||
protected:
|
||||
std::string texture;
|
||||
public:
|
||||
Image(std::string texture, glm::vec2 size);
|
||||
Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
};
|
||||
@ -73,7 +70,8 @@ namespace gui {
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual Button* listenAction(onaction action);
|
||||
|
||||
virtual void textAlign(Align align);
|
||||
virtual Align getTextAlign() const;
|
||||
virtual void setTextAlign(Align align);
|
||||
|
||||
virtual void setText(std::wstring text);
|
||||
virtual std::wstring getText() const;
|
||||
|
||||
@ -6,140 +6,94 @@
|
||||
#include "panels.h"
|
||||
#include "controls.h"
|
||||
|
||||
#include "../../assets/AssetsLoader.h"
|
||||
#include "../locale/langs.h"
|
||||
#include "../../logic/scripting/scripting.h"
|
||||
#include "../../util/stringutil.h"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static double readDouble(const std::string& str, size_t offset, size_t len) {
|
||||
double value;
|
||||
auto res = std::from_chars(str.data()+offset, str.data()+offset+len, value);
|
||||
if (res.ptr != str.data()+offset+len) {
|
||||
throw std::runtime_error("invalid number format "+escape_string(str));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Read 2d vector formatted `x,y`*/
|
||||
static glm::vec2 readVec2(const std::string& str) {
|
||||
size_t pos = str.find(',');
|
||||
if (pos == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec2 value "+escape_string(str));
|
||||
}
|
||||
return glm::vec2(
|
||||
readDouble(str, 0, pos),
|
||||
readDouble(str, pos+1, str.length()-pos-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read 3d vector formatted `x,y,z`*/
|
||||
[[maybe_unused]]
|
||||
static glm::vec3 readVec3(const std::string& str) {
|
||||
size_t pos1 = str.find(',');
|
||||
if (pos1 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec3 value "+escape_string(str));
|
||||
}
|
||||
size_t pos2 = str.find(',', pos1+1);
|
||||
if (pos2 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec3 value "+escape_string(str));
|
||||
}
|
||||
return glm::vec3(
|
||||
readDouble(str, 0, pos1),
|
||||
readDouble(str, pos1+1, pos2),
|
||||
readDouble(str, pos2+1, str.length()-pos2-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read 4d vector formatted `x,y,z,w`*/
|
||||
static glm::vec4 readVec4(const std::string& str) {
|
||||
size_t pos1 = str.find(',');
|
||||
if (pos1 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(str));
|
||||
}
|
||||
size_t pos2 = str.find(',', pos1+1);
|
||||
if (pos2 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(str));
|
||||
}
|
||||
size_t pos3 = str.find(',', pos2+1);
|
||||
if (pos3 == std::string::npos) {
|
||||
throw std::runtime_error("invalid vec4 value "+escape_string(str));
|
||||
}
|
||||
return glm::vec4(
|
||||
readDouble(str, 0, pos1),
|
||||
readDouble(str, pos1+1, pos2-pos1-1),
|
||||
readDouble(str, pos2+1, pos3-pos2-1),
|
||||
readDouble(str, pos3+1, str.length()-pos3-1)
|
||||
);
|
||||
}
|
||||
|
||||
/* Read RGBA color. Supported formats:
|
||||
- "#RRGGBB" or "#RRGGBBAA" hex color */
|
||||
static glm::vec4 readColor(const std::string& str) {
|
||||
if (str[0] == '#') {
|
||||
if (str.length() != 7 && str.length() != 9) {
|
||||
throw std::runtime_error("#RRGGBB or #RRGGBBAA required");
|
||||
}
|
||||
int a = 255;
|
||||
int r = (hexchar2int(str[1]) << 4) | hexchar2int(str[2]);
|
||||
int g = (hexchar2int(str[3]) << 4) | hexchar2int(str[4]);
|
||||
int b = (hexchar2int(str[5]) << 4) | hexchar2int(str[6]);
|
||||
if (str.length() == 9) {
|
||||
a = (hexchar2int(str[7]) << 4) | hexchar2int(str[8]);
|
||||
}
|
||||
return glm::vec4(
|
||||
r / 255.f,
|
||||
g / 255.f,
|
||||
b / 255.f,
|
||||
a / 255.f
|
||||
);
|
||||
} else {
|
||||
throw std::runtime_error("hex colors are only supported");
|
||||
}
|
||||
static Align align_from_string(const std::string& str, Align def) {
|
||||
if (str == "left") return Align::left;
|
||||
if (str == "center") return Align::center;
|
||||
if (str == "right") return Align::right;
|
||||
return def;
|
||||
}
|
||||
|
||||
/* Read basic UINode properties */
|
||||
static void readUINode(xml::xmlelement element, UINode& node) {
|
||||
static void _readUINode(xml::xmlelement element, UINode& node) {
|
||||
if (element->has("id")) {
|
||||
node.setId(element->attr("id").getText());
|
||||
}
|
||||
if (element->has("coord")) {
|
||||
node.setCoord(readVec2(element->attr("coord").getText()));
|
||||
node.setCoord(element->attr("coord").asVec2());
|
||||
}
|
||||
if (element->has("size")) {
|
||||
node.setSize(readVec2(element->attr("size").getText()));
|
||||
node.setSize(element->attr("size").asVec2());
|
||||
}
|
||||
if (element->has("color")) {
|
||||
node.setColor(readColor(element->attr("color").getText()));
|
||||
node.setColor(element->attr("color").asColor());
|
||||
}
|
||||
if (element->has("margin")) {
|
||||
node.setMargin(element->attr("margin").asVec4());
|
||||
}
|
||||
if (element->has("z-index")) {
|
||||
node.setZIndex(element->attr("z-index").asInt());
|
||||
}
|
||||
std::string alignName = element->attr("align", "").getText();
|
||||
node.setAlign(align_from_string(alignName, node.getAlign()));
|
||||
}
|
||||
|
||||
static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Container& container) {
|
||||
readUINode(element, container);
|
||||
|
||||
static void _readContainer(UiXmlReader& reader, xml::xmlelement element, Container& container) {
|
||||
_readUINode(element, container);
|
||||
|
||||
if (element->has("scrollable")) {
|
||||
container.setScrollable(element->attr("scrollable").asBool());
|
||||
}
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->isText())
|
||||
continue;
|
||||
container.add(reader.readUINode(sub));
|
||||
auto subnode = reader.readUINode(sub);
|
||||
if (subnode) {
|
||||
container.add(subnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, Container& container) {
|
||||
_readContainer(reader, element, container);
|
||||
}
|
||||
|
||||
void UiXmlReader::readUINode(UiXmlReader& reader, xml::xmlelement element, UINode& node) {
|
||||
_readUINode(element, node);
|
||||
}
|
||||
|
||||
static void _readPanel(UiXmlReader& reader, xml::xmlelement element, Panel& panel) {
|
||||
readUINode(element, panel);
|
||||
_readUINode(element, panel);
|
||||
|
||||
if (element->has("padding")) {
|
||||
panel.setPadding(readVec4(element->attr("padding").getText()));
|
||||
glm::vec4 padding = element->attr("padding").asVec4();
|
||||
panel.setPadding(padding);
|
||||
glm::vec2 size = panel.getSize();
|
||||
panel.setSize(glm::vec2(
|
||||
size.x + padding.x + padding.z,
|
||||
size.y + padding.y + padding.w
|
||||
));
|
||||
}
|
||||
|
||||
if (element->has("margin")) {
|
||||
panel.setMargin(readVec4(element->attr("margin").getText()));
|
||||
}
|
||||
|
||||
if (element->has("size")) {
|
||||
panel.setResizing(false);
|
||||
}
|
||||
|
||||
if (element->has("max-length")) {
|
||||
panel.setMaxLength(element->attr("max-length").asInt());
|
||||
}
|
||||
for (auto& sub : element->getElements()) {
|
||||
if (sub->isText())
|
||||
continue;
|
||||
panel.add(reader.readUINode(sub));
|
||||
auto subnode = reader.readUINode(sub);
|
||||
if (subnode) {
|
||||
panel.add(subnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +101,9 @@ static void _readPanel(UiXmlReader& reader, xml::xmlelement element, Panel& pane
|
||||
static std::wstring readAndProcessInnerText(xml::xmlelement element) {
|
||||
std::wstring text = L"";
|
||||
if (element->size() == 1) {
|
||||
text = util::str2wstr_utf8(element->sub(0)->attr("#").getText());
|
||||
std::string source = element->sub(0)->attr("#").getText();
|
||||
util::trim(source);
|
||||
text = util::str2wstr_utf8(source);
|
||||
if (text[0] == '@') {
|
||||
text = langs::get(text.substr(1));
|
||||
}
|
||||
@ -158,7 +114,7 @@ static std::wstring readAndProcessInnerText(xml::xmlelement element) {
|
||||
static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, xml::xmlelement element) {
|
||||
std::wstring text = readAndProcessInnerText(element);
|
||||
auto label = std::make_shared<Label>(text);
|
||||
readUINode(element, *label);
|
||||
_readUINode(element, *label);
|
||||
return label;
|
||||
}
|
||||
|
||||
@ -174,11 +130,18 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, xml::xmlelement e
|
||||
_readPanel(reader, element, *button);
|
||||
|
||||
if (element->has("onclick")) {
|
||||
runnable callback = scripting::create_runnable("<onclick>", element->attr("onclick").getText());
|
||||
auto callback = scripting::create_runnable(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("onclick").getText(),
|
||||
reader.getFilename()+".lua"
|
||||
);
|
||||
button->listenAction([callback](GUI*) {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
if (element->has("text-align")) {
|
||||
button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign()));
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
@ -188,10 +151,30 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, xml::xmlelement
|
||||
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
|
||||
_readPanel(reader, element, *textbox);
|
||||
textbox->setText(text);
|
||||
|
||||
if (element->has("consumer")) {
|
||||
auto consumer = scripting::create_wstring_consumer(
|
||||
reader.getEnvironment().getId(),
|
||||
element->attr("consumer").getText(),
|
||||
reader.getFilename()+"lua"
|
||||
);
|
||||
textbox->textConsumer(consumer);
|
||||
}
|
||||
return textbox;
|
||||
}
|
||||
|
||||
UiXmlReader::UiXmlReader() {
|
||||
static std::shared_ptr<UINode> readImage(UiXmlReader& reader, xml::xmlelement element) {
|
||||
std::string src = element->attr("src", "").getText();
|
||||
auto image = std::make_shared<Image>(src);
|
||||
_readUINode(element, *image);
|
||||
reader.getAssetsLoader().add(ASSET_TEXTURE, "textures/"+src+".png", src, nullptr);
|
||||
return image;
|
||||
}
|
||||
|
||||
UiXmlReader::UiXmlReader(const scripting::Environment& env, AssetsLoader& assetsLoader)
|
||||
: env(env), assetsLoader(assetsLoader)
|
||||
{
|
||||
add("image", readImage);
|
||||
add("label", readLabel);
|
||||
add("button", readButton);
|
||||
add("textbox", readTextBox);
|
||||
@ -202,11 +185,22 @@ void UiXmlReader::add(const std::string& tag, uinode_reader reader) {
|
||||
readers[tag] = reader;
|
||||
}
|
||||
|
||||
bool UiXmlReader::hasReader(const std::string& tag) const {
|
||||
return readers.find(tag) != readers.end();
|
||||
}
|
||||
|
||||
void UiXmlReader::addIgnore(const std::string& tag) {
|
||||
ignored.insert(tag);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UiXmlReader::readUINode(xml::xmlelement element) {
|
||||
const std::string& tag = element->getTag();
|
||||
|
||||
auto found = readers.find(tag);
|
||||
if (found == readers.end()) {
|
||||
if (ignored.find(tag) != ignored.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
throw std::runtime_error("unsupported element '"+tag+"'");
|
||||
}
|
||||
return found->second(*this, element);
|
||||
@ -216,7 +210,28 @@ std::shared_ptr<UINode> UiXmlReader::readXML(
|
||||
const std::string& filename,
|
||||
const std::string& source
|
||||
) {
|
||||
this->filename = filename;
|
||||
auto document = xml::parse(filename, source);
|
||||
auto root = document->getRoot();
|
||||
return readUINode(root);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UiXmlReader::readXML(
|
||||
const std::string& filename,
|
||||
xml::xmlelement root
|
||||
) {
|
||||
this->filename = filename;
|
||||
return readUINode(root);
|
||||
}
|
||||
|
||||
const std::string& UiXmlReader::getFilename() const {
|
||||
return filename;
|
||||
}
|
||||
|
||||
const scripting::Environment& UiXmlReader::getEnvironment() const {
|
||||
return env;
|
||||
}
|
||||
|
||||
AssetsLoader& UiXmlReader::getAssetsLoader() {
|
||||
return assetsLoader;
|
||||
}
|
||||
|
||||
@ -2,11 +2,18 @@
|
||||
#define FRONTEND_GUI_GUI_XML_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "GUI.h"
|
||||
#include "../../coders/xml.h"
|
||||
|
||||
namespace scripting {
|
||||
class Environment;
|
||||
}
|
||||
|
||||
class AssetsLoader;
|
||||
|
||||
namespace gui {
|
||||
class UiXmlReader;
|
||||
|
||||
@ -14,17 +21,46 @@ namespace gui {
|
||||
|
||||
class UiXmlReader {
|
||||
std::unordered_map<std::string, uinode_reader> readers;
|
||||
std::unordered_set<std::string> ignored;
|
||||
std::string filename;
|
||||
const scripting::Environment& env;
|
||||
AssetsLoader& assetsLoader;
|
||||
|
||||
public:
|
||||
UiXmlReader();
|
||||
UiXmlReader(const scripting::Environment& env, AssetsLoader& assetsLoader);
|
||||
|
||||
void add(const std::string& tag, uinode_reader reader);
|
||||
bool hasReader(const std::string& tag) const;
|
||||
void addIgnore(const std::string& tag);
|
||||
|
||||
std::shared_ptr<UINode> readUINode(xml::xmlelement element);
|
||||
|
||||
void readUINode(
|
||||
UiXmlReader& reader,
|
||||
xml::xmlelement element,
|
||||
UINode& node
|
||||
);
|
||||
|
||||
void readUINode(
|
||||
UiXmlReader& reader,
|
||||
xml::xmlelement element,
|
||||
Container& container
|
||||
);
|
||||
|
||||
std::shared_ptr<UINode> readXML(
|
||||
const std::string& filename,
|
||||
const std::string& source
|
||||
);
|
||||
|
||||
std::shared_ptr<UINode> readXML(
|
||||
const std::string& filename,
|
||||
xml::xmlelement root
|
||||
);
|
||||
|
||||
const scripting::Environment& getEnvironment() const;
|
||||
const std::string& getFilename() const;
|
||||
|
||||
AssetsLoader& getAssetsLoader();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -144,6 +144,16 @@ void Container::setSize(glm::vec2 size) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::refresh() {
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
||||
return a->getZIndex() < b->getZIndex();
|
||||
});
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& Container::getNodes() const {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Panel::Panel(vec2 size, glm::vec4 padding, float interval)
|
||||
: Container(vec2(), size),
|
||||
padding(padding),
|
||||
@ -186,6 +196,7 @@ void Panel::add(std::shared_ptr<UINode> node) {
|
||||
}
|
||||
|
||||
void Panel::refresh() {
|
||||
UINode::refresh();
|
||||
float x = padding.x;
|
||||
float y = padding.y;
|
||||
vec2 size = getSize();
|
||||
|
||||
@ -46,7 +46,10 @@ namespace gui {
|
||||
virtual void setScrollable(bool flag);
|
||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||
virtual glm::vec2 contentOffset() override {return glm::vec2(0.0f, scroll);};
|
||||
virtual void setSize(glm::vec2 size);
|
||||
virtual void setSize(glm::vec2 size) override;
|
||||
virtual void refresh() override;
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
||||
};
|
||||
|
||||
class Panel : public Container {
|
||||
|
||||
@ -41,17 +41,20 @@
|
||||
#include "BlocksPreview.h"
|
||||
#include "InventoryView.h"
|
||||
#include "LevelFrontend.h"
|
||||
#include "UiDocument.h"
|
||||
#include "../engine.h"
|
||||
#include "../delegates.h"
|
||||
#include "../core_defs.h"
|
||||
#include "../items/ItemDef.h"
|
||||
#include "../items/Inventory.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
|
||||
using glm::vec2;
|
||||
using glm::vec3;
|
||||
using glm::vec4;
|
||||
using namespace gui;
|
||||
|
||||
static std::shared_ptr<Label> create_label(gui::wstringsupplier supplier) {
|
||||
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
||||
auto label = std::make_shared<Label>(L"-");
|
||||
label->textSupplier(supplier);
|
||||
return label;
|
||||
@ -172,15 +175,15 @@ std::shared_ptr<InventoryView> HudRenderer::createContentAccess() {
|
||||
auto indices = content->getIndices();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
|
||||
|
||||
int itemsCount = indices->countItemDefs();
|
||||
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
||||
for (int id = 1; id < itemsCount; id++) {
|
||||
accessInventory->getSlot(id-1).set(ItemStack(id, 1));
|
||||
}
|
||||
|
||||
SlotLayout slotLayout(glm::vec2(), false, true,
|
||||
[=](ItemStack& item) {
|
||||
SlotLayout slotLayout(-1, glm::vec2(), false, true,
|
||||
[=](uint, ItemStack& item) {
|
||||
auto copy = ItemStack(item);
|
||||
inventory->move(copy, indices);
|
||||
},
|
||||
@ -190,68 +193,27 @@ std::shared_ptr<InventoryView> HudRenderer::createContentAccess() {
|
||||
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout);
|
||||
auto layout = builder.build();
|
||||
|
||||
auto contentAccess = std::make_shared<InventoryView>(
|
||||
content,
|
||||
frontend,
|
||||
interaction.get(),
|
||||
accessInventory,
|
||||
std::move(layout)
|
||||
);
|
||||
contentAccess->build();
|
||||
return contentAccess;
|
||||
auto view = builder.build();
|
||||
view->bind(accessInventory, frontend, interaction.get());
|
||||
return view;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> HudRenderer::createHotbar() {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
auto content = level->content;
|
||||
|
||||
SlotLayout slotLayout(glm::vec2(), false, false, nullptr, nullptr);
|
||||
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr);
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout);
|
||||
auto layout = builder.build();
|
||||
auto view = builder.build();
|
||||
|
||||
layout->setOrigin(glm::vec2(layout->getSize().x/2, 0));
|
||||
auto view = std::make_shared<InventoryView>(
|
||||
content,
|
||||
frontend,
|
||||
interaction.get(),
|
||||
inventory,
|
||||
std::move(layout)
|
||||
);
|
||||
view->build();
|
||||
view->setOrigin(glm::vec2(view->getSize().x/2, 0));
|
||||
view->bind(inventory, frontend, interaction.get());
|
||||
view->setInteractive(false);
|
||||
return view;
|
||||
}
|
||||
|
||||
std::shared_ptr<InventoryView> HudRenderer::createInventory() {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
auto content = level->content;
|
||||
|
||||
SlotLayout slotLayout(glm::vec2(), true, false, [=](ItemStack& stack) {
|
||||
stack.clear();
|
||||
}, nullptr);
|
||||
|
||||
InventoryBuilder builder;
|
||||
builder.addGrid(10, inventory->size(), glm::vec2(), 4, true, slotLayout);
|
||||
auto layout = builder.build();
|
||||
|
||||
auto view = std::make_shared<InventoryView>(
|
||||
content,
|
||||
frontend,
|
||||
interaction.get(),
|
||||
inventory,
|
||||
std::move(layout)
|
||||
);
|
||||
view->build();
|
||||
return view;
|
||||
}
|
||||
|
||||
HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
|
||||
: assets(engine->getAssets()),
|
||||
gui(engine->getGUI()),
|
||||
@ -261,14 +223,16 @@ HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
|
||||
|
||||
interaction = std::make_unique<InventoryInteraction>();
|
||||
grabbedItemView = std::make_shared<SlotView>(
|
||||
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr)
|
||||
);
|
||||
grabbedItemView->bind(
|
||||
interaction->getGrabbedItem(),
|
||||
frontend,
|
||||
interaction.get(),
|
||||
frontend->getLevel()->content,
|
||||
SlotLayout(glm::vec2(), false, false, nullptr, nullptr)
|
||||
frontend,
|
||||
interaction.get()
|
||||
);
|
||||
grabbedItemView->setColor(glm::vec4());
|
||||
grabbedItemView->setInteractive(false);
|
||||
grabbedItemView->setZIndex(1);
|
||||
|
||||
contentAccess = createContentAccess();
|
||||
contentAccessPanel = std::make_shared<Panel>(
|
||||
@ -279,10 +243,9 @@ HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
|
||||
contentAccessPanel->setScrollable(true);
|
||||
|
||||
hotbarView = createHotbar();
|
||||
inventoryView = createInventory();
|
||||
|
||||
darkOverlay = std::make_unique<Panel>(glm::vec2(4000.0f));
|
||||
darkOverlay->setColor(glm::vec4(0, 0, 0, 0.5f));
|
||||
darkOverlay->setZIndex(-1);
|
||||
|
||||
uicamera = std::make_unique<Camera>(vec3(), 1);
|
||||
uicamera->perspective = false;
|
||||
@ -290,18 +253,21 @@ HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
|
||||
|
||||
debugPanel = createDebugPanel(engine);
|
||||
menu->reset();
|
||||
|
||||
debugPanel->setZIndex(2);
|
||||
|
||||
gui->addBack(darkOverlay);
|
||||
gui->addBack(hotbarView);
|
||||
gui->add(debugPanel);
|
||||
gui->add(contentAccessPanel);
|
||||
gui->add(inventoryView);
|
||||
gui->add(grabbedItemView);
|
||||
}
|
||||
|
||||
HudRenderer::~HudRenderer() {
|
||||
gui->remove(grabbedItemView);
|
||||
gui->remove(inventoryView);
|
||||
if (inventoryView) {
|
||||
gui->remove(inventoryView);
|
||||
}
|
||||
gui->remove(hotbarView);
|
||||
gui->remove(darkOverlay);
|
||||
gui->remove(contentAccessPanel);
|
||||
@ -344,7 +310,7 @@ void HudRenderer::update(bool visible) {
|
||||
if (inventoryOpen) {
|
||||
closeInventory();
|
||||
} else {
|
||||
inventoryOpen = true;
|
||||
openInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,7 +319,6 @@ void HudRenderer::update(bool visible) {
|
||||
}
|
||||
|
||||
vec2 invSize = contentAccessPanel->getSize();
|
||||
inventoryView->setVisible(inventoryOpen);
|
||||
contentAccessPanel->setVisible(inventoryOpen);
|
||||
contentAccessPanel->setSize(vec2(invSize.x, Window::height));
|
||||
hotbarView->setVisible(visible);
|
||||
@ -378,10 +343,29 @@ void HudRenderer::update(bool visible) {
|
||||
darkOverlay->setVisible(pause);
|
||||
}
|
||||
|
||||
void HudRenderer::openInventory() {
|
||||
auto level = frontend->getLevel();
|
||||
auto player = level->player;
|
||||
auto inventory = player->getInventory();
|
||||
|
||||
inventoryOpen = true;
|
||||
|
||||
inventoryDocument = assets->getLayout("core:inventory");
|
||||
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
||||
inventoryView->bind(inventory, frontend, interaction.get());
|
||||
scripting::on_ui_open(inventoryDocument, inventory.get());
|
||||
|
||||
gui->add(inventoryView);
|
||||
}
|
||||
|
||||
void HudRenderer::closeInventory() {
|
||||
scripting::on_ui_close(inventoryDocument, inventoryView->getInventory().get());
|
||||
inventoryOpen = false;
|
||||
ItemStack& grabbed = interaction->getGrabbedItem();
|
||||
grabbed.clear();
|
||||
gui->remove(inventoryView);
|
||||
inventoryView = nullptr;
|
||||
inventoryDocument = nullptr;
|
||||
}
|
||||
|
||||
void HudRenderer::draw(const GfxContext& ctx){
|
||||
@ -402,7 +386,6 @@ void HudRenderer::draw(const GfxContext& ctx){
|
||||
uishader->use();
|
||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||
|
||||
// Draw selected item preview
|
||||
hotbarView->setCoord(glm::vec2(width/2, height-65));
|
||||
hotbarView->setSelected(player->getChosenSlot());
|
||||
|
||||
@ -416,11 +399,24 @@ void HudRenderer::draw(const GfxContext& ctx){
|
||||
batch->line(width/2+5, height/2-5, width/2-5, height/2+5, 0.9f, 0.9f, 0.9f, 1.0f);
|
||||
}
|
||||
|
||||
if (level->player->debug) {
|
||||
const int dmwidth = 256;
|
||||
const float dmscale = 4000.0f;
|
||||
static float deltameter[dmwidth]{};
|
||||
static int index=0;
|
||||
index = index + 1 % dmwidth;
|
||||
deltameter[index%dmwidth] = glm::min(0.2f, 1.f/fps)*dmscale;
|
||||
batch->lineWidth(1);
|
||||
for (int i = index+1; i < index+dmwidth; i++) {
|
||||
int j = i % dmwidth;
|
||||
batch->line(width-dmwidth+i-index, height-deltameter[j],
|
||||
width-dmwidth+i-index, height, 1.0f, 1.0f, 1.0f, 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
if (inventoryOpen) {
|
||||
auto caLayout = contentAccess->getLayout();
|
||||
auto invLayout = inventoryView->getLayout();
|
||||
float caWidth = caLayout->getSize().x;
|
||||
glm::vec2 invSize = invLayout->getSize();
|
||||
float caWidth = contentAccess->getSize().x;
|
||||
glm::vec2 invSize = inventoryView->getSize();
|
||||
|
||||
float width = viewport.getWidth();
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ class Engine;
|
||||
class SlotView;
|
||||
class InventoryView;
|
||||
class LevelFrontend;
|
||||
class UiDocument;
|
||||
class InventoryInteraction;
|
||||
|
||||
namespace gui {
|
||||
@ -40,7 +41,6 @@ class HudRenderer {
|
||||
std::shared_ptr<gui::Panel> contentAccessPanel;
|
||||
std::shared_ptr<InventoryView> contentAccess;
|
||||
std::shared_ptr<InventoryView> hotbarView;
|
||||
std::shared_ptr<InventoryView> inventoryView;
|
||||
std::shared_ptr<gui::UINode> debugPanel;
|
||||
std::shared_ptr<gui::Panel> darkOverlay;
|
||||
std::unique_ptr<InventoryInteraction> interaction;
|
||||
@ -48,11 +48,12 @@ class HudRenderer {
|
||||
gui::GUI* gui;
|
||||
LevelFrontend* frontend;
|
||||
|
||||
std::shared_ptr<gui::UINode> createDebugPanel(Engine* engine);
|
||||
std::shared_ptr<InventoryView> inventoryView = nullptr;
|
||||
UiDocument* inventoryDocument = nullptr;
|
||||
|
||||
std::shared_ptr<gui::UINode> createDebugPanel(Engine* engine);
|
||||
std::shared_ptr<InventoryView> createContentAccess();
|
||||
std::shared_ptr<InventoryView> createHotbar();
|
||||
std::shared_ptr<InventoryView> createInventory();
|
||||
public:
|
||||
HudRenderer(Engine* engine, LevelFrontend* frontend);
|
||||
~HudRenderer();
|
||||
@ -64,6 +65,7 @@ public:
|
||||
bool isInventoryOpen() const;
|
||||
bool isPause() const;
|
||||
|
||||
void openInventory();
|
||||
void closeInventory();
|
||||
};
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "../window/Window.h"
|
||||
#include "../engine.h"
|
||||
#include "../settings.h"
|
||||
#include "../delegates.h"
|
||||
#include "../content/Content.h"
|
||||
#include "../content/ContentLUT.h"
|
||||
#include "../content/ContentPack.h"
|
||||
@ -37,8 +38,6 @@ using glm::vec4;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace gui;
|
||||
|
||||
const int PACKS_PANEL_WIDTH = 550;
|
||||
|
||||
inline uint64_t randU64() {
|
||||
srand(time(NULL));
|
||||
return rand() ^ (rand() << 8) ^
|
||||
@ -48,7 +47,7 @@ inline uint64_t randU64() {
|
||||
((uint64_t)rand() << 56);
|
||||
}
|
||||
|
||||
static std::shared_ptr<Label> create_label(gui::wstringsupplier supplier) {
|
||||
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
||||
auto label = std::make_shared<Label>(L"-");
|
||||
label->textSupplier(supplier);
|
||||
return label;
|
||||
@ -224,8 +223,8 @@ std::shared_ptr<Panel> create_worlds_panel(Engine* engine) {
|
||||
auto namews = util::str2wstr_utf8(name);
|
||||
|
||||
auto btn = std::make_shared<RichButton>(vec2(390, 46));
|
||||
btn->setColor(vec4(1.0f, 1.0f, 1.0f, 0.1f));
|
||||
btn->setHoverColor(vec4(1.0f, 1.0f, 1.0f, 0.17f));
|
||||
btn->setColor(vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
btn->setHoverColor(vec4(0.09f, 0.17f, 0.2f, 0.6f));
|
||||
btn->listenAction([=](GUI*) {
|
||||
open_world(name, engine);
|
||||
});
|
||||
@ -277,13 +276,14 @@ std::shared_ptr<Panel> create_packs_panel(
|
||||
packconsumer callback
|
||||
){
|
||||
auto assets = engine->getAssets();
|
||||
auto panel = std::make_shared<Panel>(vec2(PACKS_PANEL_WIDTH, 200), vec4(5.0f));
|
||||
auto panel = std::make_shared<Panel>(vec2(550, 200), vec4(5.0f));
|
||||
panel->setColor(vec4(1.0f, 1.0f, 1.0f, 0.07f));
|
||||
panel->setMaxLength(400);
|
||||
panel->setScrollable(true);
|
||||
|
||||
for (auto& pack : packs) {
|
||||
auto packpanel = std::make_shared<RichButton>(vec2(390, 80));
|
||||
auto packpanel = std::make_shared<RichButton>(vec2(540, 80));
|
||||
packpanel->setColor(vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
if (callback) {
|
||||
packpanel->listenAction([=](GUI*) {
|
||||
callback(pack);
|
||||
@ -291,7 +291,9 @@ std::shared_ptr<Panel> create_packs_panel(
|
||||
}
|
||||
auto idlabel = std::make_shared<Label>("["+pack.id+"]");
|
||||
idlabel->setColor(vec4(1, 1, 1, 0.5f));
|
||||
packpanel->add(idlabel, vec2(PACKS_PANEL_WIDTH-40-idlabel->getSize().x, 2));
|
||||
idlabel->setSize(vec2(300, 25));
|
||||
idlabel->setAlign(Align::right);
|
||||
packpanel->add(idlabel, vec2(215, 2));
|
||||
|
||||
auto titlelabel = std::make_shared<Label>(pack.title);
|
||||
packpanel->add(titlelabel, vec2(78, 6));
|
||||
@ -309,7 +311,9 @@ std::shared_ptr<Panel> create_packs_panel(
|
||||
if (!pack.creator.empty()) {
|
||||
auto creatorlabel = std::make_shared<Label>("@"+pack.creator);
|
||||
creatorlabel->setColor(vec4(0.8f, 1.0f, 0.9f, 0.7f));
|
||||
packpanel->add(creatorlabel, vec2(PACKS_PANEL_WIDTH-40-creatorlabel->getSize().x, 60));
|
||||
creatorlabel->setSize(vec2(300, 20));
|
||||
creatorlabel->setAlign(Align::right);
|
||||
packpanel->add(creatorlabel, vec2(215, 60));
|
||||
}
|
||||
|
||||
auto descriptionlabel = std::make_shared<Label>(pack.description);
|
||||
@ -317,8 +321,6 @@ std::shared_ptr<Panel> create_packs_panel(
|
||||
packpanel->add(descriptionlabel, vec2(80, 28));
|
||||
|
||||
packpanel->add(std::make_shared<Image>(icon, vec2(64)), vec2(8));
|
||||
|
||||
packpanel->setColor(vec4(0.06f, 0.12f, 0.18f, 0.7f));
|
||||
panel->add(packpanel);
|
||||
}
|
||||
if (backbutton) {
|
||||
@ -331,7 +333,7 @@ std::shared_ptr<Panel> create_packs_panel(
|
||||
void create_content_panel(Engine* engine) {
|
||||
auto menu = engine->getGUI()->getMenu();
|
||||
auto paths = engine->getPaths();
|
||||
auto mainPanel = create_page(engine, "content", PACKS_PANEL_WIDTH, 0.0f, 5);
|
||||
auto mainPanel = create_page(engine, "content", 550, 0.0f, 5);
|
||||
|
||||
std::vector<ContentPack> scanned;
|
||||
ContentPack::scan(engine->getPaths(), scanned);
|
||||
|
||||
42
src/items/Inventories.cpp
Normal file
42
src/items/Inventories.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "Inventories.h"
|
||||
#include "../world/Level.h"
|
||||
#include "../world/World.h"
|
||||
|
||||
Inventories::Inventories(Level& level) : level(level) {
|
||||
}
|
||||
|
||||
Inventories::~Inventories() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::create(size_t size) {
|
||||
int64_t id = level.getWorld()->getNextInventoryId();
|
||||
auto inv = std::make_shared<Inventory>(id, size);
|
||||
store(inv);
|
||||
return inv;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::createVirtual(size_t size) {
|
||||
int64_t id;
|
||||
do {
|
||||
id = -std::max(1L, std::abs(random.rand64()));
|
||||
} while (map.find(id) != map.end());
|
||||
|
||||
auto inv = std::make_shared<Inventory>(id, size);
|
||||
store(inv);
|
||||
return inv;
|
||||
}
|
||||
|
||||
void Inventories::store(std::shared_ptr<Inventory> inv) {
|
||||
map[inv->getId()] = inv;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Inventories::get(int64_t id) {
|
||||
auto found = map.find(id);
|
||||
if (found == map.end())
|
||||
return nullptr;
|
||||
return found->second;
|
||||
}
|
||||
|
||||
const inventories_map& Inventories::getMap() const {
|
||||
return map;
|
||||
}
|
||||
39
src/items/Inventories.h
Normal file
39
src/items/Inventories.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef ITEMS_INVENTORIES_H_
|
||||
#define ITEMS_INVENTORIES_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Inventory.h"
|
||||
#include "../maths/util.h"
|
||||
|
||||
class Level;
|
||||
|
||||
using inventories_map = std::unordered_map<int64_t, std::shared_ptr<Inventory>>;
|
||||
|
||||
/* Inventories runtime storage */
|
||||
class Inventories {
|
||||
Level& level;
|
||||
inventories_map map;
|
||||
PseudoRandom random;
|
||||
public:
|
||||
Inventories(Level& level);
|
||||
~Inventories();
|
||||
|
||||
/* Create new inventory with new id */
|
||||
std::shared_ptr<Inventory> create(size_t size);
|
||||
|
||||
/* Create runtime-only inventory (has negative id) */
|
||||
std::shared_ptr<Inventory> createVirtual(size_t size);
|
||||
|
||||
/* Store inventory */
|
||||
void store(std::shared_ptr<Inventory> inv);
|
||||
|
||||
/* Get inventory by id (works with both real and virtual)*/
|
||||
std::shared_ptr<Inventory> get(int64_t id);
|
||||
|
||||
const inventories_map& getMap() const;
|
||||
};
|
||||
|
||||
#endif // ITEMS_INVENTORIES_H_
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include "../content/ContentLUT.h"
|
||||
|
||||
Inventory::Inventory(uint id, size_t size) : id(id), slots(size) {
|
||||
Inventory::Inventory(int64_t id, size_t size) : id(id), slots(size) {
|
||||
}
|
||||
|
||||
ItemStack& Inventory::getSlot(size_t index) {
|
||||
@ -47,7 +47,10 @@ void Inventory::move(
|
||||
void Inventory::deserialize(dynamic::Map* src) {
|
||||
id = src->getNum("id", 1);
|
||||
auto slotsarr = src->list("slots");
|
||||
size_t slotscount = std::min(slotsarr->size(), slots.size());
|
||||
size_t slotscount = slotsarr->size();
|
||||
while (slots.size() < slotscount) {
|
||||
slots.push_back(ItemStack());
|
||||
}
|
||||
for (size_t i = 0; i < slotscount; i++) {
|
||||
auto item = slotsarr->map(i);
|
||||
itemid_t id = item->getInt("id", ITEM_EMPTY);
|
||||
|
||||
@ -14,10 +14,10 @@ class ContentLUT;
|
||||
class ContentIndices;
|
||||
|
||||
class Inventory : Serializable {
|
||||
uint id;
|
||||
int64_t id;
|
||||
std::vector<ItemStack> slots;
|
||||
public:
|
||||
Inventory(uint id, size_t size);
|
||||
Inventory(int64_t id, size_t size);
|
||||
|
||||
ItemStack& getSlot(size_t index);
|
||||
size_t findEmptySlot(size_t begin=0, size_t end=-1) const;
|
||||
@ -40,10 +40,14 @@ public:
|
||||
|
||||
static void convert(dynamic::Map* data, const ContentLUT* lut);
|
||||
|
||||
inline uint getId() const {
|
||||
inline int64_t getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
inline bool isVirtual() const {
|
||||
return id < 0;
|
||||
}
|
||||
|
||||
static const size_t npos;
|
||||
};
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ public:
|
||||
} rt;
|
||||
|
||||
ItemDef(std::string name);
|
||||
ItemDef(const ItemDef&) = delete;
|
||||
};
|
||||
|
||||
#endif //CONTENT_ITEMS_ITEM_DEF_H_
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "../util/timeutil.h"
|
||||
#include "../maths/fastmaths.h"
|
||||
#include "../items/Inventory.h"
|
||||
#include "../items/Inventories.h"
|
||||
|
||||
#include "scripting/scripting.h"
|
||||
|
||||
@ -46,6 +47,10 @@ int Clock::getTickRate() const {
|
||||
return tickRate;
|
||||
}
|
||||
|
||||
int Clock::getTickId() const {
|
||||
return tickId;
|
||||
}
|
||||
|
||||
BlocksController::BlocksController(Level* level, uint padding)
|
||||
: level(level),
|
||||
chunks(level->chunks),
|
||||
@ -143,3 +148,24 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||
auto chunk = chunks->getChunkByVoxel(x, y, z);
|
||||
if (chunk == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
int lx = x - chunk->x * CHUNK_W;
|
||||
int lz = z - chunk->z * CHUNK_D;
|
||||
auto inv = chunk->getBlockInventory(lx, y, lz);
|
||||
if (inv == nullptr) {
|
||||
auto indices = level->content->getIndices();
|
||||
auto def = indices->getBlockDef(chunk->voxels[vox_index(lx, y, lz)].id);
|
||||
int invsize = def->inventorySize;
|
||||
if (invsize == 0) {
|
||||
return 0;
|
||||
}
|
||||
inv = level->inventories->create(invsize);
|
||||
chunk->addBlockInventory(inv, lx, y, lz);
|
||||
}
|
||||
return inv->getId();
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ public:
|
||||
int getParts() const;
|
||||
int getPart() const;
|
||||
int getTickRate() const;
|
||||
int getTickId() const;
|
||||
};
|
||||
|
||||
class BlocksController {
|
||||
@ -46,7 +47,7 @@ public:
|
||||
void update(float delta);
|
||||
void randomTick(int tickid, int parts);
|
||||
void onBlocksTick(int tickid, int parts);
|
||||
uint createBlockInventory(int x, int y, int z);
|
||||
int64_t createBlockInventory(int x, int y, int z);
|
||||
};
|
||||
|
||||
#endif // LOGIC_BLOCKS_CONTROLLER_H_
|
||||
|
||||
@ -306,7 +306,7 @@ void PlayerController::updateInteraction(){
|
||||
if (def->grounded && !chunks->isSolidBlock(x, y-1, z)) {
|
||||
chosenBlock = 0;
|
||||
}
|
||||
if (chosenBlock != vox->id) {
|
||||
if (chosenBlock != vox->id && chosenBlock) {
|
||||
chunks->set(x, y, z, chosenBlock, states);
|
||||
lighting->onBlockSet(x,y,z, chosenBlock);
|
||||
if (def->rt.funcsset.onplaced) {
|
||||
|
||||
265
src/logic/scripting/LuaState.cpp
Normal file
265
src/logic/scripting/LuaState.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
#include "LuaState.h"
|
||||
|
||||
#include "api_lua.h"
|
||||
#include "../../util/stringutil.h"
|
||||
|
||||
lua::luaerror::luaerror(const std::string& message) : std::runtime_error(message) {
|
||||
}
|
||||
|
||||
lua::LuaState::LuaState() {
|
||||
L = luaL_newstate();
|
||||
if (L == nullptr) {
|
||||
throw lua::luaerror("could not to initialize Lua");
|
||||
}
|
||||
// Allowed standard libraries
|
||||
luaopen_base(L);
|
||||
luaopen_math(L);
|
||||
luaopen_string(L);
|
||||
luaopen_table(L);
|
||||
luaopen_debug(L);
|
||||
|
||||
std::cout << LUA_VERSION << std::endl;
|
||||
# ifdef LUAJIT_VERSION
|
||||
luaopen_jit(L);
|
||||
std::cout << LUAJIT_VERSION << std::endl;
|
||||
# endif // LUAJIT_VERSION
|
||||
|
||||
createFuncs();
|
||||
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
setglobal(envName(0));
|
||||
}
|
||||
|
||||
const std::string lua::LuaState::envName(int env) const {
|
||||
return "_ENV"+util::mangleid(env);
|
||||
}
|
||||
|
||||
lua::LuaState::~LuaState() {
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
void lua::LuaState::logError(const std::string& text) {
|
||||
std::cerr << text << std::endl;
|
||||
}
|
||||
|
||||
void lua::LuaState::addfunc(const std::string& name, lua_CFunction func) {
|
||||
lua_pushcfunction(L, func);
|
||||
lua_setglobal(L, name.c_str());
|
||||
}
|
||||
|
||||
bool lua::LuaState::getglobal(const std::string& name) {
|
||||
lua_getglobal(L, name.c_str());
|
||||
if (lua_isnil(L, lua_gettop(L))) {
|
||||
lua_pop(L, lua_gettop(L));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lua::LuaState::hasglobal(const std::string& name) {
|
||||
lua_getglobal(L, name.c_str());
|
||||
if (lua_isnil(L, lua_gettop(L))) {
|
||||
lua_pop(L, lua_gettop(L));
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, lua_gettop(L));
|
||||
return true;
|
||||
}
|
||||
|
||||
void lua::LuaState::setglobal(const std::string& name) {
|
||||
lua_setglobal(L, name.c_str());
|
||||
}
|
||||
|
||||
bool lua::LuaState::rename(const std::string& from, const std::string& to) {
|
||||
const char* src = from.c_str();
|
||||
lua_getglobal(L, src);
|
||||
if (lua_isnil(L, lua_gettop(L))) {
|
||||
lua_pop(L, lua_gettop(L));
|
||||
return false;
|
||||
}
|
||||
lua_setglobal(L, to.c_str());
|
||||
|
||||
// remove previous
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, src);
|
||||
return true;
|
||||
}
|
||||
|
||||
void lua::LuaState::remove(const std::string& name) {
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, name.c_str());
|
||||
}
|
||||
|
||||
void lua::LuaState::createFuncs() {
|
||||
openlib("pack", packlib, 0);
|
||||
openlib("world", worldlib, 0);
|
||||
openlib("player", playerlib, 0);
|
||||
openlib("inventory", inventorylib, 0);
|
||||
openlib("item", itemlib, 0);
|
||||
openlib("time", timelib, 0);
|
||||
openlib("file", filelib, 0);
|
||||
|
||||
addfunc("print", l_print);
|
||||
|
||||
addfunc("block_index", l_block_index);
|
||||
addfunc("block_name", l_block_name);
|
||||
addfunc("blocks_count", l_blocks_count);
|
||||
addfunc("is_solid_at", l_is_solid_at);
|
||||
addfunc("is_replaceable_at", l_is_replaceable_at);
|
||||
addfunc("set_block", l_set_block);
|
||||
addfunc("get_block", l_get_block);
|
||||
addfunc("get_block_X", l_get_block_x);
|
||||
addfunc("get_block_Y", l_get_block_y);
|
||||
addfunc("get_block_Z", l_get_block_z);
|
||||
addfunc("get_block_states", l_get_block_states);
|
||||
addfunc("get_block_user_bits", l_get_block_user_bits);
|
||||
addfunc("set_block_user_bits", l_set_block_user_bits);
|
||||
}
|
||||
|
||||
void lua::LuaState::loadbuffer(int env, const std::string& src, const std::string& file) {
|
||||
if (luaL_loadbuffer(L, src.c_str(), src.length(), file.c_str())) {
|
||||
throw lua::luaerror(lua_tostring(L, -1));
|
||||
}
|
||||
if (env && getglobal(envName(env))) {
|
||||
lua_setfenv(L, -2);
|
||||
}
|
||||
}
|
||||
|
||||
int lua::LuaState::call(int argc) {
|
||||
if (lua_pcall(L, argc, LUA_MULTRET, 0)) {
|
||||
throw lua::luaerror(lua_tostring(L, -1));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua::LuaState::callNoThrow(int argc) {
|
||||
if (lua_pcall(L, argc, LUA_MULTRET, 0)) {
|
||||
logError(lua_tostring(L, -1));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua::LuaState::eval(int env, const std::string& src, const std::string& file) {
|
||||
auto srcText = "return ("+src+")";
|
||||
loadbuffer(env, srcText, file);
|
||||
return call(0);
|
||||
}
|
||||
|
||||
int lua::LuaState::execute(int env, const std::string& src, const std::string& file) {
|
||||
loadbuffer(env, src, file);
|
||||
return callNoThrow(0);
|
||||
}
|
||||
|
||||
int lua::LuaState::gettop() const {
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
int lua::LuaState::pushinteger(luaint x) {
|
||||
lua_pushinteger(L, x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua::LuaState::pushnumber(luanumber x) {
|
||||
lua_pushnumber(L, x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua::LuaState::pushivec3(luaint x, luaint y, luaint z) {
|
||||
lua::pushivec3(L, x, y, z);
|
||||
return 3;
|
||||
}
|
||||
|
||||
int lua::LuaState::pushstring(const std::string& str) {
|
||||
lua_pushstring(L, str.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua::LuaState::pushenv(int env) {
|
||||
if (getglobal(envName(env))) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lua::LuaState::pushvalue(int idx) {
|
||||
lua_pushvalue(L, idx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lua::LuaState::pushglobals() {
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void lua::LuaState::pop(int n) {
|
||||
lua_pop(L, n);
|
||||
}
|
||||
|
||||
int lua::LuaState::pushnil() {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool lua::LuaState::getfield(const std::string& name) {
|
||||
lua_getfield(L, -1, name.c_str());
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, -1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lua::LuaState::setfield(const std::string& name, int idx) {
|
||||
lua_setfield(L, idx, name.c_str());
|
||||
}
|
||||
|
||||
bool lua::LuaState::toboolean(int index) {
|
||||
return lua_toboolean(L, index);
|
||||
}
|
||||
|
||||
lua::luaint lua::LuaState::tointeger(int index) {
|
||||
return lua_tointeger(L, index);
|
||||
}
|
||||
|
||||
void lua::LuaState::openlib(const std::string& name, const luaL_Reg* libfuncs, int nup) {
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, libfuncs, nup);
|
||||
lua_setglobal(L, name.c_str());
|
||||
}
|
||||
|
||||
const std::string lua::LuaState::storeAnonymous() {
|
||||
auto funcId = uintptr_t(lua_topointer(L, lua_gettop(L)));
|
||||
auto funcName = "F$"+util::mangleid(funcId);
|
||||
lua_setglobal(L, funcName.c_str());
|
||||
return funcName;
|
||||
}
|
||||
|
||||
int lua::LuaState::createEnvironment(int parent) {
|
||||
int id = nextEnvironment++;
|
||||
|
||||
// local env = {}
|
||||
lua_createtable(L, 0, 1);
|
||||
|
||||
// setmetatable(env, {__index=_G})
|
||||
lua_createtable(L, 0, 1);
|
||||
if (parent == 0 || true) {
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
} else {
|
||||
if (pushenv(parent) == 0) {
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
}
|
||||
}
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
// envname = env
|
||||
setglobal(envName(id));
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
void lua::LuaState::removeEnvironment(int id) {
|
||||
lua_pushnil(L);
|
||||
setglobal(envName(id));
|
||||
}
|
||||
61
src/logic/scripting/LuaState.h
Normal file
61
src/logic/scripting/LuaState.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef LOGIC_SCRIPTING_LUA_STATE_H_
|
||||
#define LOGIC_SCRIPTING_LUA_STATE_H_
|
||||
|
||||
#include <lua.hpp>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace lua {
|
||||
using luaint = lua_Integer;
|
||||
using luanumber = lua_Number;
|
||||
|
||||
class luaerror : public std::runtime_error {
|
||||
public:
|
||||
luaerror(const std::string& message);
|
||||
};
|
||||
|
||||
class LuaState {
|
||||
lua_State* L;
|
||||
int nextEnvironment = 1;
|
||||
|
||||
void logError(const std::string& text);
|
||||
void initEnvironment();
|
||||
public:
|
||||
LuaState();
|
||||
~LuaState();
|
||||
|
||||
const std::string envName(int env) const;
|
||||
void loadbuffer(int env, const std::string& src, const std::string& file);
|
||||
int gettop() const;
|
||||
int pushivec3(luaint x, luaint y, luaint z);
|
||||
int pushinteger(luaint x);
|
||||
int pushnumber(luanumber x);
|
||||
int pushstring(const std::string& str);
|
||||
int pushenv(int env);
|
||||
int pushvalue(int idx);
|
||||
int pushnil();
|
||||
int pushglobals();
|
||||
void pop(int n=1);
|
||||
bool getfield(const std::string& name);
|
||||
void setfield(const std::string& name, int idx=-2);
|
||||
bool toboolean(int index);
|
||||
luaint tointeger(int index);
|
||||
int call(int argc);
|
||||
int callNoThrow(int argc);
|
||||
int execute(int env, const std::string& src, const std::string& file="<string>");
|
||||
int eval(int env, const std::string& src, const std::string& file="<eval>");
|
||||
void openlib(const std::string& name, const luaL_Reg* libfuncs, int nup);
|
||||
void addfunc(const std::string& name, lua_CFunction func);
|
||||
bool getglobal(const std::string& name);
|
||||
void setglobal(const std::string& name);
|
||||
bool hasglobal(const std::string& name);
|
||||
bool rename(const std::string& from, const std::string& to);
|
||||
void remove(const std::string& name);;
|
||||
void createFuncs();
|
||||
int createEnvironment(int parent);
|
||||
void removeEnvironment(int id);
|
||||
const std::string storeAnonymous();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LOGIC_SCRIPTING_LUA_STATE_H_
|
||||
@ -1,432 +0,0 @@
|
||||
#include "api_lua.h"
|
||||
#include "scripting.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include "../../files/files.h"
|
||||
#include "../../physics/Hitbox.h"
|
||||
#include "../../objects/Player.h"
|
||||
#include "../../world/Level.h"
|
||||
#include "../../world/World.h"
|
||||
#include "../../content/Content.h"
|
||||
#include "../../voxels/Block.h"
|
||||
#include "../../voxels/Chunks.h"
|
||||
#include "../../voxels/voxel.h"
|
||||
#include "../../lighting/Lighting.h"
|
||||
#include "../../logic/BlocksController.h"
|
||||
#include "../../window/Window.h"
|
||||
#include "../../engine.h"
|
||||
|
||||
inline int lua_pushivec3(lua_State* L, int x, int y, int z) {
|
||||
lua_pushinteger(L, x);
|
||||
lua_pushinteger(L, y);
|
||||
lua_pushinteger(L, z);
|
||||
return 3;
|
||||
}
|
||||
|
||||
inline void openlib(lua_State* L, const char* name, const luaL_Reg* libfuncs, int nup) {
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, libfuncs, nup);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
|
||||
/* == file library == */
|
||||
static int l_file_resolve(lua_State* L) {
|
||||
std::string path = lua_tostring(L, 1);
|
||||
fs::path resolved = scripting::engine->getPaths()->resolve(path);
|
||||
lua_pushstring(L, resolved.u8string().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_read(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
if (fs::is_regular_file(path)) {
|
||||
lua_pushstring(L, files::read_string(path).c_str());
|
||||
return 1;
|
||||
}
|
||||
return luaL_error(L, "file does not exists '%s'", path.u8string().c_str());
|
||||
}
|
||||
|
||||
static int l_file_write(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
const char* text = lua_tostring(L, 2);
|
||||
files::write_string(path, text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_exists(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::exists(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_isfile(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::is_regular_file(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_isdir(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::is_directory(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_length(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
if (fs::exists(path)){
|
||||
lua_pushinteger(L, fs::file_size(path));
|
||||
} else {
|
||||
lua_pushinteger(L, -1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_mkdir(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::create_directory(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg filelib [] = {
|
||||
{"resolve", l_file_resolve},
|
||||
{"read", l_file_read},
|
||||
{"write", l_file_write},
|
||||
{"exists", l_file_exists},
|
||||
{"isfile", l_file_isfile},
|
||||
{"isdir", l_file_isdir},
|
||||
{"length", l_file_length},
|
||||
{"mkdir", l_file_mkdir},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == time library == */
|
||||
static int l_time_uptime(lua_State* L) {
|
||||
lua_pushnumber(L, Window::time());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg timelib [] = {
|
||||
{"uptime", l_time_uptime},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == pack library == */
|
||||
static int l_pack_get_folder(lua_State* L) {
|
||||
std::string packName = lua_tostring(L, 1);
|
||||
if (packName == "core") {
|
||||
auto folder = scripting::engine->getPaths()
|
||||
->getResources().u8string()+"/";
|
||||
lua_pushstring(L, folder.c_str());
|
||||
return 1;
|
||||
}
|
||||
for (auto& pack : scripting::engine->getContentPacks()) {
|
||||
if (pack.id == packName) {
|
||||
lua_pushstring(L, (pack.folder.u8string()+"/").c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lua_pushstring(L, "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg packlib [] = {
|
||||
{"get_folder", l_pack_get_folder},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == world library == */
|
||||
static int l_world_get_total_time(lua_State* L) {
|
||||
lua_pushnumber(L, scripting::level->world->totalTime);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_world_get_day_time(lua_State* L) {
|
||||
lua_pushnumber(L, scripting::level->world->daytime);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_world_set_day_time(lua_State* L) {
|
||||
double value = lua_tonumber(L, 1);
|
||||
scripting::level->world->daytime = fmod(value, 1.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_world_get_seed(lua_State* L) {
|
||||
lua_pushinteger(L, scripting::level->world->getSeed());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg worldlib [] = {
|
||||
{"get_total_time", l_world_get_total_time},
|
||||
{"get_day_time", l_world_get_day_time},
|
||||
{"set_day_time", l_world_set_day_time},
|
||||
{"get_seed", l_world_get_seed},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == player library ==*/
|
||||
static int l_player_get_pos(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
glm::vec3 pos = scripting::level->player->hitbox->position;
|
||||
lua_pushnumber(L, pos.x);
|
||||
lua_pushnumber(L, pos.y);
|
||||
lua_pushnumber(L, pos.z);
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int l_player_get_rot(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
glm::vec2 rot = scripting::level->player->cam;
|
||||
lua_pushnumber(L, rot.x);
|
||||
lua_pushnumber(L, rot.y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int l_player_set_rot(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
double x = lua_tonumber(L, 2);
|
||||
double y = lua_tonumber(L, 3);
|
||||
glm::vec2& cam = scripting::level->player->cam;
|
||||
cam.x = x;
|
||||
cam.y = y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_player_set_pos(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
double x = lua_tonumber(L, 2);
|
||||
double y = lua_tonumber(L, 3);
|
||||
double z = lua_tonumber(L, 4);
|
||||
scripting::level->player->hitbox->position = glm::vec3(x, y, z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg playerlib [] = {
|
||||
{"get_pos", l_player_get_pos},
|
||||
{"set_pos", l_player_set_pos},
|
||||
{"get_rot", l_player_get_rot},
|
||||
{"set_rot", l_player_set_rot},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == blocks-related functions == */
|
||||
static int l_block_name(lua_State* L) {
|
||||
int id = lua_tointeger(L, 1);
|
||||
auto def = scripting::content->getIndices()->getBlockDef(id);
|
||||
lua_pushstring(L, def->name.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static 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->isSolidBlock(x, y, z));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_blocks_count(lua_State* L) {
|
||||
lua_pushinteger(L, scripting::content->getIndices()->countBlockDefs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_block_index(lua_State* L) {
|
||||
auto name = lua_tostring(L, 1);
|
||||
lua_pushinteger(L, scripting::content->requireBlock(name)->rt.id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static 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);
|
||||
int states = lua_tointeger(L, 5);
|
||||
bool noupdate = lua_toboolean(L, 6);
|
||||
scripting::level->chunks->set(x, y, z, id, states);
|
||||
scripting::level->lighting->onBlockSet(x,y,z, id);
|
||||
if (!noupdate)
|
||||
scripting::blocks->updateSides(x, y, z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static int l_get_block_x(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);
|
||||
if (vox == nullptr) {
|
||||
return lua_pushivec3(L, 1, 0, 0);
|
||||
}
|
||||
auto def = scripting::level->content->getIndices()->getBlockDef(vox->id);
|
||||
if (!def->rotatable) {
|
||||
return lua_pushivec3(L, 1, 0, 0);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
return lua_pushivec3(L, rot.axisX.x, rot.axisX.y, rot.axisX.z);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_block_y(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);
|
||||
if (vox == nullptr) {
|
||||
return lua_pushivec3(L, 0, 1, 0);
|
||||
}
|
||||
auto def = scripting::level->content->getIndices()->getBlockDef(vox->id);
|
||||
if (!def->rotatable) {
|
||||
return lua_pushivec3(L, 0, 1, 0);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
return lua_pushivec3(L, rot.axisY.x, rot.axisY.y, rot.axisY.z);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_block_z(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);
|
||||
if (vox == nullptr) {
|
||||
return lua_pushivec3(L, 0, 0, 1);
|
||||
}
|
||||
auto def = scripting::level->content->getIndices()->getBlockDef(vox->id);
|
||||
if (!def->rotatable) {
|
||||
return lua_pushivec3(L, 0, 0, 1);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
return lua_pushivec3(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_block_states(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 states = vox == nullptr ? 0 : vox->states;
|
||||
lua_pushinteger(L, states);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_get_block_user_bits(lua_State* L) {
|
||||
int x = lua_tointeger(L, 1);
|
||||
int y = lua_tointeger(L, 2);
|
||||
int z = lua_tointeger(L, 3);
|
||||
int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||
int bits = lua_tointeger(L, 5);
|
||||
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
lua_pushinteger(L, 0);
|
||||
return 1;
|
||||
}
|
||||
uint mask = ((1 << bits) - 1) << offset;
|
||||
uint data = (vox->states & mask) >> offset;
|
||||
lua_pushinteger(L, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_set_block_user_bits(lua_State* L) {
|
||||
int x = lua_tointeger(L, 1);
|
||||
int y = lua_tointeger(L, 2);
|
||||
int z = lua_tointeger(L, 3);
|
||||
int offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||
int bits = lua_tointeger(L, 5);
|
||||
|
||||
uint mask = ((1 << bits) - 1) << offset;
|
||||
int value = (lua_tointeger(L, 6) << offset) & mask;
|
||||
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
vox->states = (vox->states & (~mask)) | value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_replaceable_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->isReplaceableBlock(x, y, z));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Modified version of luaB_print from lbaselib.c
|
||||
static int l_print(lua_State* L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
lua_getglobal(L, "tostring");
|
||||
for (int i=1; i<=n; i++) {
|
||||
lua_pushvalue(L, -1); /* function to be called */
|
||||
lua_pushvalue(L, i); /* value to print */
|
||||
lua_call(L, 1, 1);
|
||||
const char* s = lua_tostring(L, -1); /* get result */
|
||||
if (s == NULL)
|
||||
return luaL_error(L, LUA_QL("tostring") " must return a string to "
|
||||
LUA_QL("print"));
|
||||
if (i > 1)
|
||||
std::cout << "\t";
|
||||
std::cout << s;
|
||||
lua_pop(L, 1); /* pop result */
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define lua_addfunc(L, FUNC, NAME) (lua_pushcfunction(L, FUNC),\
|
||||
lua_setglobal(L, NAME))
|
||||
|
||||
void apilua::create_funcs(lua_State* L) {
|
||||
openlib(L, "pack", packlib, 0);
|
||||
openlib(L, "world", worldlib, 0);
|
||||
openlib(L, "player", playerlib, 0);
|
||||
openlib(L, "time", timelib, 0);
|
||||
openlib(L, "file", filelib, 0);
|
||||
|
||||
lua_addfunc(L, l_print, "print");
|
||||
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_is_replaceable_at, "is_replaceable_at");
|
||||
lua_addfunc(L, l_set_block, "set_block");
|
||||
lua_addfunc(L, l_get_block, "get_block");
|
||||
lua_addfunc(L, l_get_block_x, "get_block_X");
|
||||
lua_addfunc(L, l_get_block_y, "get_block_Y");
|
||||
lua_addfunc(L, l_get_block_z, "get_block_Z");
|
||||
lua_addfunc(L, l_get_block_states, "get_block_states");
|
||||
lua_addfunc(L, l_get_block_user_bits, "get_block_user_bits");
|
||||
lua_addfunc(L, l_set_block_user_bits, "set_block_user_bits");
|
||||
}
|
||||
@ -1,14 +1,548 @@
|
||||
#ifndef LOGIC_SCRIPTING_API_LUA_H_
|
||||
#define LOGIC_SCRIPTING_API_LUA_H_
|
||||
|
||||
#include <lua.hpp>
|
||||
#include "scripting.h"
|
||||
#include "lua_util.h"
|
||||
|
||||
#ifndef LUAJIT_VERSION
|
||||
#pragma message("better use LuaJIT instead of plain Lua")
|
||||
#endif // LUAJIT_VERSION
|
||||
#include <glm/glm.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace apilua {
|
||||
extern void create_funcs(lua_State* L);
|
||||
#include "../../files/files.h"
|
||||
#include "../../physics/Hitbox.h"
|
||||
#include "../../objects/Player.h"
|
||||
#include "../../world/Level.h"
|
||||
#include "../../world/World.h"
|
||||
#include "../../content/Content.h"
|
||||
#include "../../voxels/Block.h"
|
||||
#include "../../voxels/Chunks.h"
|
||||
#include "../../voxels/voxel.h"
|
||||
#include "../../items/ItemDef.h"
|
||||
#include "../../items/ItemStack.h"
|
||||
#include "../../items/Inventory.h"
|
||||
#include "../../items/Inventories.h"
|
||||
#include "../../lighting/Lighting.h"
|
||||
#include "../../logic/BlocksController.h"
|
||||
#include "../../window/Window.h"
|
||||
#include "../../engine.h"
|
||||
|
||||
/* == file library == */
|
||||
static int l_file_resolve(lua_State* L) {
|
||||
std::string path = lua_tostring(L, 1);
|
||||
fs::path resolved = scripting::engine->getPaths()->resolve(path);
|
||||
lua_pushstring(L, resolved.u8string().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_read(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
if (fs::is_regular_file(path)) {
|
||||
lua_pushstring(L, files::read_string(path).c_str());
|
||||
return 1;
|
||||
}
|
||||
return luaL_error(L, "file does not exists '%s'", path.u8string().c_str());
|
||||
}
|
||||
|
||||
static int l_file_write(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
const char* text = lua_tostring(L, 2);
|
||||
files::write_string(path, text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_exists(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::exists(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_isfile(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::is_regular_file(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_isdir(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::is_directory(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_length(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
if (fs::exists(path)){
|
||||
lua_pushinteger(L, fs::file_size(path));
|
||||
} else {
|
||||
lua_pushinteger(L, -1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_file_mkdir(lua_State* L) {
|
||||
auto paths = scripting::engine->getPaths();
|
||||
fs::path path = paths->resolve(lua_tostring(L, 1));
|
||||
lua_pushboolean(L, fs::create_directory(path));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg filelib [] = {
|
||||
{"resolve", l_file_resolve},
|
||||
{"read", l_file_read},
|
||||
{"write", l_file_write},
|
||||
{"exists", l_file_exists},
|
||||
{"isfile", l_file_isfile},
|
||||
{"isdir", l_file_isdir},
|
||||
{"length", l_file_length},
|
||||
{"mkdir", l_file_mkdir},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == time library == */
|
||||
static int l_time_uptime(lua_State* L) {
|
||||
lua_pushnumber(L, Window::time());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg timelib [] = {
|
||||
{"uptime", l_time_uptime},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == pack library == */
|
||||
static int l_pack_get_folder(lua_State* L) {
|
||||
std::string packName = lua_tostring(L, 1);
|
||||
if (packName == "core") {
|
||||
auto folder = scripting::engine->getPaths()
|
||||
->getResources().u8string()+"/";
|
||||
lua_pushstring(L, folder.c_str());
|
||||
return 1;
|
||||
}
|
||||
for (auto& pack : scripting::engine->getContentPacks()) {
|
||||
if (pack.id == packName) {
|
||||
lua_pushstring(L, (pack.folder.u8string()+"/").c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lua_pushstring(L, "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg packlib [] = {
|
||||
{"get_folder", l_pack_get_folder},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == world library == */
|
||||
static int l_world_get_total_time(lua_State* L) {
|
||||
lua_pushnumber(L, scripting::level->world->totalTime);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_world_get_day_time(lua_State* L) {
|
||||
lua_pushnumber(L, scripting::level->world->daytime);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_world_set_day_time(lua_State* L) {
|
||||
double value = lua_tonumber(L, 1);
|
||||
scripting::level->world->daytime = fmod(value, 1.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_world_get_seed(lua_State* L) {
|
||||
lua_pushinteger(L, scripting::level->world->getSeed());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg worldlib [] = {
|
||||
{"get_total_time", l_world_get_total_time},
|
||||
{"get_day_time", l_world_get_day_time},
|
||||
{"set_day_time", l_world_set_day_time},
|
||||
{"get_seed", l_world_get_seed},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == player library ==*/
|
||||
static int l_player_get_pos(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
glm::vec3 pos = scripting::level->player->hitbox->position;
|
||||
lua_pushnumber(L, pos.x);
|
||||
lua_pushnumber(L, pos.y);
|
||||
lua_pushnumber(L, pos.z);
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int l_player_get_rot(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
glm::vec2 rot = scripting::level->player->cam;
|
||||
lua_pushnumber(L, rot.x);
|
||||
lua_pushnumber(L, rot.y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int l_player_set_rot(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
lua::luanumber x = lua_tonumber(L, 2);
|
||||
lua::luanumber y = lua_tonumber(L, 3);
|
||||
glm::vec2& cam = scripting::level->player->cam;
|
||||
cam.x = x;
|
||||
cam.y = y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_player_set_pos(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
lua::luanumber x = lua_tonumber(L, 2);
|
||||
lua::luanumber y = lua_tonumber(L, 3);
|
||||
lua::luanumber z = lua_tonumber(L, 4);
|
||||
scripting::level->player->hitbox->position = glm::vec3(x, y, z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_player_get_inv(lua_State* L) {
|
||||
int playerid = lua_tointeger(L, 1);
|
||||
if (playerid != 1)
|
||||
return 0;
|
||||
Player* player = scripting::level->player;
|
||||
lua_pushinteger(L, player->getInventory()->getId());
|
||||
lua_pushinteger(L, player->getChosenSlot());
|
||||
return 2;
|
||||
}
|
||||
|
||||
static const luaL_Reg playerlib [] = {
|
||||
{"get_pos", l_player_get_pos},
|
||||
{"set_pos", l_player_set_pos},
|
||||
{"get_rot", l_player_get_rot},
|
||||
{"set_rot", l_player_set_rot},
|
||||
{"get_inventory", l_player_get_inv},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static void validate_itemid(lua_State* L, itemid_t id) {
|
||||
if (id >= scripting::indices->countItemDefs()) {
|
||||
luaL_error(L, "invalid item id");
|
||||
}
|
||||
}
|
||||
|
||||
/* == inventory library == */
|
||||
static int l_inventory_get(lua_State* L) {
|
||||
lua::luaint invid = lua_tointeger(L, 1);
|
||||
lua::luaint slotid = lua_tointeger(L, 2);
|
||||
auto inv = scripting::level->inventories->get(invid);
|
||||
if (inv == nullptr) {
|
||||
luaL_error(L, "inventory does not exists in runtime: %d", invid);
|
||||
}
|
||||
if (slotid < 0 || uint64_t(slotid) >= inv->size()) {
|
||||
luaL_error(L, "slot index is out of range [0, inventory.size(invid)]");
|
||||
}
|
||||
ItemStack& item = inv->getSlot(slotid);
|
||||
lua_pushinteger(L, item.getItemId());
|
||||
lua_pushinteger(L, item.getCount());
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int l_inventory_set(lua_State* L) {
|
||||
lua::luaint invid = lua_tointeger(L, 1);
|
||||
lua::luaint slotid = lua_tointeger(L, 2);
|
||||
lua::luaint itemid = lua_tointeger(L, 3);
|
||||
lua::luaint count = lua_tointeger(L, 4);
|
||||
validate_itemid(L, itemid);
|
||||
|
||||
auto inv = scripting::level->inventories->get(invid);
|
||||
if (inv == nullptr) {
|
||||
luaL_error(L, "inventory does not exists in runtime: %d", invid);
|
||||
}
|
||||
if (slotid < 0 || uint64_t(slotid) >= inv->size()) {
|
||||
luaL_error(L, "slot index is out of range [0, inventory.size(invid)]");
|
||||
}
|
||||
ItemStack& item = inv->getSlot(slotid);
|
||||
item.set(ItemStack(itemid, count));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_inventory_size(lua_State* L) {
|
||||
lua::luaint invid = lua_tointeger(L, 1);
|
||||
auto inv = scripting::level->inventories->get(invid);
|
||||
if (inv == nullptr) {
|
||||
luaL_error(L, "inventory does not exists in runtime: %d", invid);
|
||||
}
|
||||
lua_pushinteger(L, inv->size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_inventory_add(lua_State* L) {
|
||||
lua::luaint invid = lua_tointeger(L, 1);
|
||||
lua::luaint itemid = lua_tointeger(L, 2);
|
||||
lua::luaint count = lua_tointeger(L, 3);
|
||||
validate_itemid(L, itemid);
|
||||
|
||||
auto inv = scripting::level->inventories->get(invid);
|
||||
if (inv == nullptr) {
|
||||
luaL_error(L, "inventory does not exists in runtime: %d", invid);
|
||||
}
|
||||
ItemStack item(itemid, count);
|
||||
inv->move(item, scripting::indices);
|
||||
lua_pushinteger(L, item.getCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_inventory_get_block(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
int64_t id = scripting::blocks->createBlockInventory(x, y, z);
|
||||
lua_pushinteger(L, id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg inventorylib [] = {
|
||||
{"get", l_inventory_get},
|
||||
{"set", l_inventory_set},
|
||||
{"size", l_inventory_size},
|
||||
{"add", l_inventory_add},
|
||||
{"get_block", l_inventory_get_block},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == item library == */
|
||||
static int l_item_name(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua::luaint id = lua_tointeger(L, 1);
|
||||
if (id < 0 || size_t(id) >= indices->countItemDefs()) {
|
||||
return 0;
|
||||
}
|
||||
auto def = indices->getItemDef(id);
|
||||
lua_pushstring(L, def->name.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_item_index(lua_State* L) {
|
||||
auto name = lua_tostring(L, 1);
|
||||
lua_pushinteger(L, scripting::content->requireItem(name).rt.id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_item_stack_size(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua::luaint id = lua_tointeger(L, 1);
|
||||
if (id < 0 || size_t(id) >= indices->countItemDefs()) {
|
||||
return 0;
|
||||
}
|
||||
auto def = indices->getItemDef(id);
|
||||
lua_pushinteger(L, def->stackSize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_item_defs_count(lua_State* L) {
|
||||
lua_pushinteger(L, scripting::indices->countItemDefs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg itemlib [] = {
|
||||
{"index", l_item_index},
|
||||
{"name", l_item_name},
|
||||
{"stack_size", l_item_stack_size},
|
||||
{"defs_count", l_item_defs_count},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* == blocks-related functions == */
|
||||
static int l_block_name(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua::luaint id = lua_tointeger(L, 1);
|
||||
if (id < 0 || size_t(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
lua_pushstring(L, def->name.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_is_solid_at(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
|
||||
lua_pushboolean(L, scripting::level->chunks->isSolidBlock(x, y, z));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_blocks_count(lua_State* L) {
|
||||
lua_pushinteger(L, scripting::indices->countBlockDefs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_block_index(lua_State* L) {
|
||||
auto name = lua_tostring(L, 1);
|
||||
lua_pushinteger(L, scripting::content->requireBlock(name).rt.id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_set_block(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
lua::luaint id = lua_tointeger(L, 4);
|
||||
lua::luaint states = lua_tointeger(L, 5);
|
||||
bool noupdate = lua_toboolean(L, 6);
|
||||
if (id < 0 || size_t(id) >= scripting::indices->countBlockDefs()) {
|
||||
return 0;
|
||||
}
|
||||
scripting::level->chunks->set(x, y, z, id, states);
|
||||
scripting::level->lighting->onBlockSet(x,y,z, id);
|
||||
if (!noupdate)
|
||||
scripting::blocks->updateSides(x, y, z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_block(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint 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;
|
||||
}
|
||||
|
||||
static int l_get_block_x(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return lua::pushivec3(L, 1, 0, 0);
|
||||
}
|
||||
auto def = scripting::level->content->getIndices()->getBlockDef(vox->id);
|
||||
if (!def->rotatable) {
|
||||
return lua::pushivec3(L, 1, 0, 0);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
return lua::pushivec3(L, rot.axisX.x, rot.axisX.y, rot.axisX.z);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_block_y(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return lua::pushivec3(L, 0, 1, 0);
|
||||
}
|
||||
auto def = scripting::level->content->getIndices()->getBlockDef(vox->id);
|
||||
if (!def->rotatable) {
|
||||
return lua::pushivec3(L, 0, 1, 0);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
return lua::pushivec3(L, rot.axisY.x, rot.axisY.y, rot.axisY.z);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_block_z(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return lua::pushivec3(L, 0, 0, 1);
|
||||
}
|
||||
auto def = scripting::level->content->getIndices()->getBlockDef(vox->id);
|
||||
if (!def->rotatable) {
|
||||
return lua::pushivec3(L, 0, 0, 1);
|
||||
} else {
|
||||
const CoordSystem& rot = def->rotations.variants[vox->rotation()];
|
||||
return lua::pushivec3(L, rot.axisZ.x, rot.axisZ.y, rot.axisZ.z);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_block_states(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
int states = vox == nullptr ? 0 : vox->states;
|
||||
lua_pushinteger(L, states);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_get_block_user_bits(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
lua::luaint offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||
lua::luaint bits = lua_tointeger(L, 5);
|
||||
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
lua_pushinteger(L, 0);
|
||||
return 1;
|
||||
}
|
||||
uint mask = ((1 << bits) - 1) << offset;
|
||||
uint data = (vox->states & mask) >> offset;
|
||||
lua_pushinteger(L, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_set_block_user_bits(lua_State* L) {
|
||||
lua::luaint x = lua_tointeger(L, 1);
|
||||
lua::luaint y = lua_tointeger(L, 2);
|
||||
lua::luaint z = lua_tointeger(L, 3);
|
||||
lua::luaint offset = lua_tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||
lua::luaint bits = lua_tointeger(L, 5);
|
||||
|
||||
uint mask = ((1 << bits) - 1) << offset;
|
||||
lua::luaint value = (lua_tointeger(L, 6) << offset) & mask;
|
||||
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
vox->states = (vox->states & (~mask)) | value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_replaceable_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->isReplaceableBlock(x, y, z));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Modified version of luaB_print from lbaselib.c
|
||||
static int l_print(lua_State* L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
lua_getglobal(L, "tostring");
|
||||
for (int i=1; i<=n; i++) {
|
||||
lua_pushvalue(L, -1); /* function to be called */
|
||||
lua_pushvalue(L, i); /* value to print */
|
||||
lua_call(L, 1, 1);
|
||||
const char* s = lua_tostring(L, -1); /* get result */
|
||||
if (s == NULL)
|
||||
return luaL_error(L, LUA_QL("tostring") " must return a string to "
|
||||
LUA_QL("print"));
|
||||
if (i > 1)
|
||||
std::cout << "\t";
|
||||
std::cout << s;
|
||||
lua_pop(L, 1); /* pop result */
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // LOGIC_SCRIPTING_API_LUA_H_
|
||||
|
||||
15
src/logic/scripting/lua_util.h
Normal file
15
src/logic/scripting/lua_util.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef LOGIC_SCRIPTING_LUA_UTIL_H_
|
||||
#define LOGIC_SCRIPTING_LUA_UTIL_H_
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace lua {
|
||||
inline int pushivec3(lua_State* L, luaint x, luaint y, luaint z) {
|
||||
lua_pushinteger(L, x);
|
||||
lua_pushinteger(L, y);
|
||||
lua_pushinteger(L, z);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LOGIC_SCRIPTING_LUA_UTIL_H_
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <lua.hpp>
|
||||
|
||||
#include "../../content/ContentPack.h"
|
||||
#include "../../files/engine_paths.h"
|
||||
@ -11,55 +10,37 @@
|
||||
#include "../../world/Level.h"
|
||||
#include "../../voxels/Block.h"
|
||||
#include "../../items/ItemDef.h"
|
||||
#include "../../items/Inventory.h"
|
||||
#include "../../logic/BlocksController.h"
|
||||
#include "../../frontend/UiDocument.h"
|
||||
#include "../../engine.h"
|
||||
#include "api_lua.h"
|
||||
#include "LuaState.h"
|
||||
#include "../../util/stringutil.h"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
namespace scripting {
|
||||
extern lua_State* L;
|
||||
extern lua::LuaState* state;
|
||||
}
|
||||
|
||||
Engine* scripting::engine = nullptr;
|
||||
lua_State* scripting::L = nullptr;
|
||||
lua::LuaState* scripting::state = nullptr;
|
||||
Level* scripting::level = nullptr;
|
||||
const Content* scripting::content = nullptr;
|
||||
const ContentIndices* scripting::indices = nullptr;
|
||||
BlocksController* scripting::blocks = nullptr;
|
||||
|
||||
static void handleError(lua_State* L) {
|
||||
std::cerr << "lua error: " << lua_tostring(L,-1) << std::endl;
|
||||
Environment::Environment(int env) : env(env) {
|
||||
}
|
||||
|
||||
inline int lua_pushivec3(lua_State* L, int x, int y, int z) {
|
||||
lua_pushinteger(L, x);
|
||||
lua_pushinteger(L, y);
|
||||
lua_pushinteger(L, z);
|
||||
return 3;
|
||||
}
|
||||
|
||||
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;
|
||||
Environment::~Environment() {
|
||||
if (env) {
|
||||
state->removeEnvironment(env);
|
||||
}
|
||||
lua_setglobal(L, dst);
|
||||
delete_global(L, src);
|
||||
return true;
|
||||
}
|
||||
|
||||
int call_func(lua_State* L, int argc, const std::string& name) {
|
||||
if (lua_pcall(L, argc, LUA_MULTRET, 0)) {
|
||||
handleError(L);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
int Environment::getId() const {
|
||||
return env;
|
||||
}
|
||||
|
||||
void load_script(fs::path name) {
|
||||
@ -67,217 +48,286 @@ void load_script(fs::path name) {
|
||||
fs::path file = paths->getResources()/fs::path("scripts")/name;
|
||||
|
||||
std::string src = files::read_string(file);
|
||||
if (luaL_loadbuffer(L, src.c_str(), src.length(), file.u8string().c_str())) {
|
||||
handleError(L);
|
||||
return;
|
||||
}
|
||||
call_func(L, 0, file.u8string());
|
||||
state->execute(0, src, file.u8string());
|
||||
}
|
||||
|
||||
void scripting::initialize(Engine* engine) {
|
||||
scripting::engine = engine;
|
||||
|
||||
L = luaL_newstate();
|
||||
if (L == nullptr) {
|
||||
throw std::runtime_error("could not to initialize Lua");
|
||||
}
|
||||
|
||||
// Allowed standard libraries
|
||||
luaopen_base(L);
|
||||
luaopen_math(L);
|
||||
luaopen_string(L);
|
||||
luaopen_table(L);
|
||||
|
||||
// io-manipulations will be implemented via api functions
|
||||
|
||||
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);
|
||||
state = new lua::LuaState();
|
||||
|
||||
load_script(fs::path("stdlib.lua"));
|
||||
}
|
||||
|
||||
// todo: luaL state check
|
||||
runnable scripting::create_runnable(
|
||||
const std::string& filename,
|
||||
const std::string& src
|
||||
int env,
|
||||
const std::string& src,
|
||||
const std::string& file
|
||||
) {
|
||||
return [=](){
|
||||
if (luaL_loadbuffer(L, src.c_str(), src.length(), filename.c_str())) {
|
||||
handleError(L);
|
||||
state->execute(env, src, file);
|
||||
};
|
||||
}
|
||||
|
||||
wstringconsumer scripting::create_wstring_consumer(
|
||||
int env,
|
||||
const std::string& src,
|
||||
const std::string& file
|
||||
) {
|
||||
return [=](const std::wstring& x){
|
||||
try {
|
||||
if (state->eval(env, src, file) == 0)
|
||||
return;
|
||||
} catch (lua::luaerror err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
call_func(L, 0, filename);
|
||||
state->pushstring(util::wstr2str_utf8(x));
|
||||
state->callNoThrow(1);
|
||||
};
|
||||
}
|
||||
|
||||
int_array_consumer scripting::create_int_array_consumer(
|
||||
int env,
|
||||
const std::string& src,
|
||||
const std::string& file
|
||||
) {
|
||||
return [=](const int arr[], size_t len){
|
||||
try {
|
||||
if (state->eval(env, src, file) == 0)
|
||||
return;
|
||||
} catch (lua::luaerror err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
for (uint i = 0; i < len; i++) {
|
||||
state->pushinteger(arr[i]);
|
||||
}
|
||||
state->callNoThrow(len);
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<Environment> scripting::create_environment(int parent) {
|
||||
return std::make_unique<Environment>(state->createEnvironment(parent));
|
||||
}
|
||||
|
||||
std::unique_ptr<Environment> scripting::create_pack_environment(const ContentPack& pack) {
|
||||
int id = state->createEnvironment(0);
|
||||
state->pushenv(id);
|
||||
state->pushvalue(-1);
|
||||
state->setfield("PACK_ENV");
|
||||
state->pushstring(pack.id);
|
||||
state->setfield("PACK_ID");
|
||||
state->pop();
|
||||
return std::make_unique<Environment>(id);
|
||||
}
|
||||
|
||||
std::unique_ptr<Environment> scripting::create_doc_environment(int parent, const std::string& name) {
|
||||
int id = state->createEnvironment(parent);
|
||||
state->pushenv(id);
|
||||
state->pushvalue(-1);
|
||||
state->setfield("DOC_ENV");
|
||||
state->pushstring(name.c_str());
|
||||
state->setfield("DOC_NAME");
|
||||
state->pop();
|
||||
return std::make_unique<Environment>(id);
|
||||
}
|
||||
|
||||
void scripting::on_world_load(Level* level, BlocksController* blocks) {
|
||||
scripting::level = level;
|
||||
scripting::content = level->content;
|
||||
scripting::indices = level->content->getIndices();
|
||||
scripting::blocks = blocks;
|
||||
load_script("world.lua");
|
||||
|
||||
for (auto& pack : scripting::engine->getContentPacks()) {
|
||||
std::string name = pack.id+".worldopen";
|
||||
lua_getglobal(L, name.c_str());
|
||||
if (lua_isnil(L, lua_gettop(L))) {
|
||||
lua_pop(L, lua_gettop(L));
|
||||
continue;
|
||||
if (state->getglobal(pack.id+".worldopen")) {
|
||||
state->callNoThrow(0);
|
||||
}
|
||||
call_func(L, 0, name);
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_world_save() {
|
||||
for (auto& pack : scripting::engine->getContentPacks()) {
|
||||
std::string name = pack.id+".worldsave";
|
||||
lua_getglobal(L, name.c_str());
|
||||
if (lua_isnil(L, lua_gettop(L))) {
|
||||
lua_pop(L, lua_gettop(L));
|
||||
continue;
|
||||
if (state->getglobal(pack.id+".worldsave")) {
|
||||
state->callNoThrow(0);
|
||||
}
|
||||
call_func(L, 0, name);
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_world_quit() {
|
||||
for (auto& pack : scripting::engine->getContentPacks()) {
|
||||
std::string name = pack.id+".worldquit";
|
||||
lua_getglobal(L, name.c_str());
|
||||
if (lua_isnil(L, lua_gettop(L))) {
|
||||
lua_pop(L, lua_gettop(L));
|
||||
continue;
|
||||
if (state->getglobal(pack.id+".worldquit")) {
|
||||
state->callNoThrow(0);
|
||||
}
|
||||
call_func(L, 0, name);
|
||||
}
|
||||
scripting::level = nullptr;
|
||||
scripting::content = nullptr;
|
||||
scripting::indices = nullptr;
|
||||
}
|
||||
|
||||
void scripting::on_blocks_tick(const Block* block, int tps) {
|
||||
std::string name = block->name+".blockstick";
|
||||
lua_getglobal(L, name.c_str());
|
||||
lua_pushinteger(L, tps);
|
||||
call_func(L, 1, name);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushinteger(tps);
|
||||
state->callNoThrow(1);
|
||||
}
|
||||
}
|
||||
|
||||
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_pushivec3(L, x, y, z);
|
||||
call_func(L, 3, name);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->callNoThrow(3);
|
||||
}
|
||||
}
|
||||
|
||||
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_pushivec3(L, x, y, z);
|
||||
call_func(L, 3, name);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->callNoThrow(3);
|
||||
}
|
||||
}
|
||||
|
||||
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_pushivec3(L, x, y, z);
|
||||
lua_pushinteger(L, 1); // player id placeholder
|
||||
call_func(L, 4, name);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->pushinteger(1); // playerid placeholder
|
||||
state->callNoThrow(4);
|
||||
}
|
||||
}
|
||||
|
||||
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_pushivec3(L, x, y, z);
|
||||
lua_pushinteger(L, 1); // player id placeholder
|
||||
call_func(L, 4, name);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->pushinteger(1); // playerid placeholder
|
||||
state->callNoThrow(4);
|
||||
}
|
||||
}
|
||||
|
||||
bool scripting::on_block_interact(Player* player, const Block* block, int x, int y, int z) {
|
||||
std::string name = block->name+".oninteract";
|
||||
lua_getglobal(L, name.c_str());
|
||||
lua_pushivec3(L, x, y, z);
|
||||
lua_pushinteger(L, 1);
|
||||
if (call_func(L, 4, name)) {
|
||||
return lua_toboolean(L, -1);
|
||||
std::string name = block->name+".interact";
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->pushinteger(1); // playerid placeholder
|
||||
if (state->callNoThrow(4)) {
|
||||
return state->toboolean(-1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool scripting::on_item_use_on_block(Player* player, const ItemDef* item, int x, int y, int z) {
|
||||
std::string name = item->name+".useon";
|
||||
lua_getglobal(L, name.c_str());
|
||||
lua_pushivec3(L, x, y, z);
|
||||
lua_pushinteger(L, 1); // player id placeholder
|
||||
if (call_func(L, 4, name)) {
|
||||
return lua_toboolean(L, -1);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->pushinteger(1); // playerid placeholder
|
||||
if (state->callNoThrow(4)) {
|
||||
return state->toboolean(-1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool scripting::on_item_break_block(Player* player, const ItemDef* item, int x, int y, int z) {
|
||||
std::string name = item->name+".blockbreakby";
|
||||
lua_getglobal(L, name.c_str());
|
||||
lua_pushivec3(L, x, y, z);
|
||||
lua_pushinteger(L, 1); // player id placeholder
|
||||
if (call_func(L, 4, name)) {
|
||||
return lua_toboolean(L, -1);
|
||||
if (state->getglobal(name)) {
|
||||
state->pushivec3(x, y, z);
|
||||
state->pushinteger(1); // playerid placeholder
|
||||
if (state->callNoThrow(4)) {
|
||||
return state->toboolean(-1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: refactor
|
||||
|
||||
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())) {
|
||||
handleError(L);
|
||||
return;
|
||||
void scripting::on_ui_open(UiDocument* layout, Inventory* inventory) {
|
||||
std::string name = layout->getId()+".open";
|
||||
if (state->getglobal(name)) {
|
||||
state->pushinteger(inventory->getId());
|
||||
state->callNoThrow(1);
|
||||
}
|
||||
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());
|
||||
funcsset->oninteract=rename_global(L, "on_interact", (prefix+".oninteract").c_str());
|
||||
funcsset->onblockstick=rename_global(L, "on_blocks_tick", (prefix+".blockstick").c_str());
|
||||
}
|
||||
|
||||
void scripting::load_item_script(std::string prefix, fs::path file, item_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())) {
|
||||
handleError(L);
|
||||
return;
|
||||
void scripting::on_ui_close(UiDocument* layout, Inventory* inventory) {
|
||||
std::string name = layout->getId()+".close";
|
||||
if (state->getglobal(name)) {
|
||||
state->pushinteger(inventory->getId());
|
||||
state->callNoThrow(1);
|
||||
}
|
||||
call_func(L, 0, "<script>");
|
||||
funcsset->init=rename_global(L, "init", (prefix+".init").c_str());
|
||||
funcsset->on_use_on_block=rename_global(L, "on_use_on_block", (prefix+".useon").c_str());
|
||||
funcsset->on_block_break_by=rename_global(L, "on_block_break_by", (prefix+".blockbreakby").c_str());
|
||||
}
|
||||
|
||||
void scripting::load_world_script(std::string prefix, fs::path file) {
|
||||
bool register_event(int env, const std::string& name, const std::string& id) {
|
||||
if (state->pushenv(env) == 0) {
|
||||
state->pushglobals();
|
||||
}
|
||||
if (state->getfield(name)) {
|
||||
// remove previous name
|
||||
state->pushnil();
|
||||
state->setfield(name, -3);
|
||||
// add new global name
|
||||
state->setglobal(id);
|
||||
state->pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void scripting::load_block_script(int env, 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())) {
|
||||
handleError(L);
|
||||
return;
|
||||
}
|
||||
call_func(L, 0, "<script>");
|
||||
rename_global(L, "init", (prefix+".init").c_str());
|
||||
rename_global(L, "on_world_open", (prefix+".worldopen").c_str());
|
||||
rename_global(L, "on_world_save", (prefix+".worldsave").c_str());
|
||||
rename_global(L, "on_world_quit", (prefix+".worldquit").c_str());
|
||||
state->execute(env, src, file.u8string());
|
||||
funcsset.init = register_event(env, "init", prefix+".init");
|
||||
funcsset.update = register_event(env, "on_update", prefix+".update");
|
||||
funcsset.randupdate = register_event(env, "on_random_update", prefix+".randupdate");
|
||||
funcsset.onbroken = register_event(env, "on_broken", prefix+".broken");
|
||||
funcsset.onplaced = register_event(env, "on_placed", prefix+".placed");
|
||||
funcsset.oninteract = register_event(env, "on_interact", prefix+".interact");
|
||||
funcsset.onblockstick = register_event(env, "on_blocks_tick", prefix+".blockstick");
|
||||
}
|
||||
|
||||
void scripting::load_item_script(int env, std::string prefix, fs::path file, item_funcs_set& funcsset) {
|
||||
std::string src = files::read_string(file);
|
||||
std::cout << "loading script " << file.u8string() << std::endl;
|
||||
state->execute(env, src, file.u8string());
|
||||
|
||||
funcsset.init = register_event(env, "init", prefix+".init");
|
||||
funcsset.on_use_on_block = register_event(env, "on_use_on_block", prefix+".useon");
|
||||
funcsset.on_block_break_by = register_event(env, "on_block_break_by", prefix+".blockbreakby");
|
||||
}
|
||||
|
||||
void scripting::load_world_script(int env, std::string prefix, fs::path file) {
|
||||
std::string src = files::read_string(file);
|
||||
std::cout << "loading script " << file.u8string() << std::endl;
|
||||
|
||||
state->loadbuffer(env, src, file.u8string());
|
||||
state->callNoThrow(0);
|
||||
|
||||
register_event(env, "init", prefix+".init");
|
||||
register_event(env, "on_world_open", prefix+".worldopen");
|
||||
register_event(env, "on_world_save", prefix+".worldsave");
|
||||
register_event(env, "on_world_quit", prefix+".worldquit");
|
||||
}
|
||||
|
||||
void scripting::load_layout_script(int env, std::string prefix, fs::path file, uidocscript& script) {
|
||||
std::string src = files::read_string(file);
|
||||
std::cout << "loading script " << file.u8string() << std::endl;
|
||||
|
||||
script.environment = env;
|
||||
state->loadbuffer(env, src, file.u8string());
|
||||
state->callNoThrow(0);
|
||||
script.onopen = register_event(env, "on_open", prefix+".open");
|
||||
script.onclose = register_event(env, "on_close", prefix+".close");
|
||||
}
|
||||
|
||||
void scripting::close() {
|
||||
lua_close(L);
|
||||
delete state;
|
||||
|
||||
L = nullptr;
|
||||
state = nullptr;
|
||||
content = nullptr;
|
||||
indices = nullptr;
|
||||
level = nullptr;
|
||||
}
|
||||
|
||||
@ -5,28 +5,65 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class LuaState;
|
||||
|
||||
class Engine;
|
||||
class Content;
|
||||
struct ContentPack;
|
||||
class ContentIndices;
|
||||
class Level;
|
||||
class Block;
|
||||
class Player;
|
||||
class ItemDef;
|
||||
class Inventory;
|
||||
class UiDocument;
|
||||
struct block_funcs_set;
|
||||
struct item_funcs_set;
|
||||
struct uidocscript;
|
||||
class BlocksController;
|
||||
|
||||
namespace scripting {
|
||||
using int_array_consumer = std::function<void(const int[], size_t)>;
|
||||
|
||||
extern Engine* engine;
|
||||
extern const Content* content;
|
||||
extern const ContentIndices* indices;
|
||||
extern Level* level;
|
||||
extern BlocksController* blocks;
|
||||
|
||||
/* Lua environment wrapper for automatic deletion */
|
||||
class Environment {
|
||||
int env;
|
||||
public:
|
||||
Environment(int env);
|
||||
~Environment();
|
||||
|
||||
int getId() const;
|
||||
};
|
||||
|
||||
void initialize(Engine* engine);
|
||||
|
||||
runnable create_runnable(
|
||||
const std::string& filename,
|
||||
const std::string& source
|
||||
int env,
|
||||
const std::string& src,
|
||||
const std::string& file="<string>"
|
||||
);
|
||||
|
||||
wstringconsumer create_wstring_consumer(
|
||||
int env,
|
||||
const std::string& src,
|
||||
const std::string& file="<string>"
|
||||
);
|
||||
|
||||
int_array_consumer create_int_array_consumer(
|
||||
int env,
|
||||
const std::string& src,
|
||||
const std::string& file="<string>"
|
||||
);
|
||||
|
||||
std::unique_ptr<Environment> create_environment(int parent=0);
|
||||
std::unique_ptr<Environment> create_pack_environment(const ContentPack& pack);
|
||||
std::unique_ptr<Environment> create_doc_environment(int parent, const std::string& name);
|
||||
|
||||
void on_world_load(Level* level, BlocksController* blocks);
|
||||
void on_world_save();
|
||||
@ -37,10 +74,28 @@ namespace scripting {
|
||||
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);
|
||||
bool on_block_interact(Player* player, const Block* block, int x, int y, int z);
|
||||
|
||||
/* Called on RMB click on block with the item selected
|
||||
@return true if prevents default action */
|
||||
bool on_item_use_on_block(Player* player, const ItemDef* item, int x, int y, int z);
|
||||
/* Called on LMB click on block with the item selected
|
||||
@return true if prevents default action */
|
||||
bool on_item_break_block(Player* player, const ItemDef* item, int x, int y, int z);
|
||||
void load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset);
|
||||
void load_item_script(std::string prefix, fs::path file, item_funcs_set* funcsset);
|
||||
void load_world_script(std::string prefix, fs::path file);
|
||||
|
||||
/* Called on UI view show */
|
||||
void on_ui_open(UiDocument* layout, Inventory* inventory);
|
||||
/* Called on UI view close*/
|
||||
void on_ui_close(UiDocument* layout, Inventory* inventory);
|
||||
|
||||
/* Load script associated with a Block */
|
||||
void load_block_script(int env, std::string prefix, fs::path file, block_funcs_set& funcsset);
|
||||
/* Load script associated with an Item */
|
||||
void load_item_script(int env, std::string prefix, fs::path file, item_funcs_set& funcsset);
|
||||
/* Load package-specific world script */
|
||||
void load_world_script(int env, std::string prefix, fs::path file);
|
||||
/* Load script associated with an UiDocument */
|
||||
void load_layout_script(int env, std::string prefix, fs::path file, uidocscript& script);
|
||||
/* Finalize lua state. Using scripting after will lead to Lua panic */
|
||||
|
||||
void close();
|
||||
}
|
||||
|
||||
49
src/maths/util.h
Normal file
49
src/maths/util.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef MATHS_UTIL_H_
|
||||
#define MATHS_UTIL_H_
|
||||
|
||||
#include <ctime>
|
||||
#include <stdint.h>
|
||||
|
||||
class PseudoRandom {
|
||||
unsigned short seed;
|
||||
public:
|
||||
PseudoRandom(){
|
||||
seed = (unsigned short)time(0);
|
||||
}
|
||||
|
||||
int rand(){
|
||||
seed = (seed + 0x7ed5 + (seed << 6));
|
||||
seed = (seed ^ 0xc23c ^ (seed >> 9));
|
||||
seed = (seed + 0x1656 + (seed << 3));
|
||||
seed = ((seed + 0xa264) ^ (seed << 4));
|
||||
seed = (seed + 0xfd70 - (seed << 3));
|
||||
seed = (seed ^ 0xba49 ^ (seed >> 8));
|
||||
|
||||
return (int)seed;
|
||||
}
|
||||
|
||||
int32_t rand32() {
|
||||
return (rand() << 16) | rand();
|
||||
}
|
||||
|
||||
uint32_t randU32() {
|
||||
return (rand() << 16) | rand();
|
||||
}
|
||||
|
||||
int64_t rand64() {
|
||||
uint64_t x = randU32();
|
||||
uint64_t y = randU32();
|
||||
return (x << 32ULL) | y;
|
||||
}
|
||||
|
||||
void setSeed(int number){
|
||||
seed = ((unsigned short)(number*23729) ^ (unsigned short)(number+16786));
|
||||
rand();
|
||||
}
|
||||
void setSeed(int number1, int number2){
|
||||
seed = (((unsigned short)(number1*23729) | (unsigned short)(number2%16786)) ^ (unsigned short)(number2*number1));
|
||||
rand();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // MATHS_UTIL_H_
|
||||
@ -18,10 +18,10 @@ const float FLIGHT_SPEED_MUL = 4.0f;
|
||||
const float CHEAT_SPEED_MUL = 5.0f;
|
||||
const float JUMP_FORCE = 8.0f;
|
||||
|
||||
Player::Player(glm::vec3 position, float speed) :
|
||||
Player::Player(glm::vec3 position, float speed, std::shared_ptr<Inventory> inv) :
|
||||
speed(speed),
|
||||
chosenSlot(0),
|
||||
inventory(new Inventory(0, 40)),
|
||||
inventory(inv),
|
||||
camera(new Camera(position, glm::radians(90.0f))),
|
||||
spCamera(new Camera(position, glm::radians(90.0f))),
|
||||
tpCamera(new Camera(position, glm::radians(90.0f))),
|
||||
@ -195,7 +195,6 @@ std::unique_ptr<dynamic::Map> Player::serialize() const {
|
||||
}
|
||||
|
||||
void Player::deserialize(dynamic::Map *src) {
|
||||
|
||||
auto posarr = src->list("position");
|
||||
glm::vec3& position = hitbox->position;
|
||||
position.x = posarr->num(0);
|
||||
|
||||
@ -48,7 +48,7 @@ public:
|
||||
|
||||
glm::vec2 cam = {};
|
||||
|
||||
Player(glm::vec3 position, float speed);
|
||||
Player(glm::vec3 position, float speed, std::shared_ptr<Inventory> inv);
|
||||
~Player();
|
||||
|
||||
void teleport(glm::vec3 position);
|
||||
|
||||
@ -47,6 +47,7 @@ struct GraphicsSettings {
|
||||
/* Fog opacity is calculated as `pow(depth*k, fogCurve)` where k depends on chunksLoadDistance.
|
||||
Use values in range [1.0 - 2.0] where 1.0 is linear, 2.0 is quadratic */
|
||||
float fogCurve = 1.6f;
|
||||
float gamma = 1.0f;
|
||||
/* Enable blocks backlight to prevent complete darkness */
|
||||
bool backlight = true;
|
||||
/* Enable chunks frustum culling */
|
||||
|
||||
@ -228,6 +228,14 @@ std::string util::base64_encode(const ubyte* data, size_t size) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string util::mangleid(uint64_t value) {
|
||||
// todo: use base64
|
||||
std::stringstream ss;
|
||||
ss << std::hex << value;
|
||||
std::string result(ss.str());
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::vector<ubyte> util::base64_decode(const char* str, size_t size) {
|
||||
std::vector<ubyte> bytes((size/4)*3);
|
||||
ubyte* dst = bytes.data();
|
||||
@ -279,3 +287,7 @@ double util::parse_double(const std::string& str) {
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
double util::parse_double(const std::string& str, size_t offset, size_t len) {
|
||||
return parse_double(str.substr(offset, len));
|
||||
}
|
||||
|
||||
@ -27,9 +27,12 @@ namespace util {
|
||||
extern std::vector<ubyte> base64_decode(const char* str, size_t size);
|
||||
extern std::vector<ubyte> base64_decode(const std::string& str);
|
||||
|
||||
extern std::string mangleid(uint64_t value);
|
||||
|
||||
extern int replaceAll(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
extern double parse_double(const std::string& str);
|
||||
extern double parse_double(const std::string& str, size_t offset, size_t len);
|
||||
}
|
||||
|
||||
#endif // UTIL_STRINGUTIL_H_
|
||||
@ -82,7 +82,7 @@ public:
|
||||
std::vector<glm::vec3> modelExtraPoints = {}; //initially made for tetragons
|
||||
std::vector<UVRegion> modelUVs = {}; // boxes' tex-UVs also there
|
||||
uint8_t emission[4] {0, 0, 0, 0};
|
||||
ubyte drawGroup = 0;
|
||||
uint8_t drawGroup = 0;
|
||||
BlockModel model = BlockModel::block;
|
||||
bool lightPassing = false;
|
||||
bool skyLightPassing = false;
|
||||
@ -110,6 +110,7 @@ public:
|
||||
|
||||
Block(std::string name);
|
||||
Block(std::string name, std::string texture);
|
||||
Block(const Block&) = delete;
|
||||
};
|
||||
|
||||
#endif /* VOXELS_BLOCK_H_ */
|
||||
|
||||
@ -49,6 +49,16 @@ void Chunk::addBlockInventory(std::shared_ptr<Inventory> inventory,
|
||||
setUnsaved(true);
|
||||
}
|
||||
|
||||
void Chunk::removeBlockInventory(uint x, uint y, uint z) {
|
||||
if (inventories.erase(vox_index(x, y, z))) {
|
||||
setUnsaved(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::setBlockInventories(chunk_inventories_map map) {
|
||||
inventories = map;
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Chunk::getBlockInventory(uint x, uint y, uint z) const {
|
||||
if (x >= CHUNK_W || y >= CHUNK_H || z >= CHUNK_D)
|
||||
return nullptr;
|
||||
@ -93,7 +103,7 @@ ubyte* Chunk::encode() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool Chunk::decode(ubyte* data) {
|
||||
bool Chunk::decode(const ubyte* data) {
|
||||
for (uint i = 0; i < CHUNK_VOL; i++) {
|
||||
voxel& vox = voxels[i];
|
||||
|
||||
|
||||
@ -17,12 +17,14 @@ struct ChunkFlag {
|
||||
static const int UNSAVED = 0x10;
|
||||
static const int LOADED_LIGHTS = 0x20;
|
||||
};
|
||||
#define CHUNK_DATA_LEN (CHUNK_VOL*4)
|
||||
constexpr int CHUNK_DATA_LEN = CHUNK_VOL*4;
|
||||
|
||||
class Lightmap;
|
||||
class ContentLUT;
|
||||
class Inventory;
|
||||
|
||||
using chunk_inventories_map = std::unordered_map<uint, std::shared_ptr<Inventory>>;
|
||||
|
||||
class Chunk {
|
||||
public:
|
||||
int x, z;
|
||||
@ -32,7 +34,7 @@ public:
|
||||
int flags = 0;
|
||||
|
||||
/* Block inventories map where key is index of block in voxels array */
|
||||
std::unordered_map<uint, std::shared_ptr<Inventory>> inventories;
|
||||
chunk_inventories_map inventories;
|
||||
|
||||
Chunk(int x, int z);
|
||||
|
||||
@ -55,6 +57,8 @@ public:
|
||||
@return inventory id or 0 if block does not exists */
|
||||
void addBlockInventory(std::shared_ptr<Inventory> inventory,
|
||||
uint x, uint y, uint z);
|
||||
void removeBlockInventory(uint x, uint y, uint z);
|
||||
void setBlockInventories(chunk_inventories_map map);
|
||||
|
||||
/* @return inventory bound to the given block or nullptr */
|
||||
std::shared_ptr<Inventory> getBlockInventory(uint x, uint y, uint z) const;
|
||||
@ -88,7 +92,7 @@ public:
|
||||
/**
|
||||
* @return true if all is fine
|
||||
**/
|
||||
bool decode(ubyte* data);
|
||||
bool decode(const ubyte* data);
|
||||
|
||||
static void convert(ubyte* data, const ContentLUT* lut);
|
||||
};
|
||||
|
||||
@ -21,8 +21,7 @@ Chunks::Chunks(int w, int d,
|
||||
WorldFiles* wfile,
|
||||
LevelEvents* events,
|
||||
const Content* content)
|
||||
: content(content),
|
||||
contentIds(content->getIndices()),
|
||||
: contentIds(content->getIndices()),
|
||||
chunks(w*d),
|
||||
chunksSecond(w*d),
|
||||
w(w), d(d), ox(ox), oz(oz),
|
||||
@ -163,6 +162,9 @@ void Chunks::set(int x, int y, int z, int id, uint8_t states){
|
||||
int lz = z - cz * CHUNK_D;
|
||||
|
||||
voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
||||
auto def = contentIds->getBlockDef(vox.id);
|
||||
if (def->inventorySize == 0)
|
||||
chunk->removeBlockInventory(lx, y, lz);
|
||||
vox.id = id;
|
||||
vox.states = states;
|
||||
|
||||
|
||||
@ -19,7 +19,6 @@ class LevelEvents;
|
||||
|
||||
/* Player-centred chunks matrix */
|
||||
class Chunks {
|
||||
const Content* const content;
|
||||
const ContentIndices* const contentIds;
|
||||
public:
|
||||
std::vector<std::shared_ptr<Chunk>> chunks;
|
||||
|
||||
@ -57,6 +57,8 @@ std::shared_ptr<Chunk> ChunksStorage::create(int x, int z) {
|
||||
std::unique_ptr<ubyte[]> data(wfile->getChunk(chunk->x, chunk->z));
|
||||
if (data) {
|
||||
chunk->decode(data.get());
|
||||
auto invs = wfile->fetchInventories(chunk->x, chunk->z);
|
||||
chunk->setBlockInventories(std::move(invs));
|
||||
chunk->setLoaded(true);
|
||||
verifyLoadedChunk(level->content->getIndices(), chunk.get());
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
#include "../content/Content.h"
|
||||
#include "../maths/voxmaths.h"
|
||||
#include "../maths/util.h"
|
||||
#include "../core_defs.h"
|
||||
|
||||
// TODO: do something with long conditions + move magic numbers to constants
|
||||
@ -62,36 +63,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class PseudoRandom {
|
||||
unsigned short seed;
|
||||
public:
|
||||
PseudoRandom(){
|
||||
seed = (unsigned short)time(0);
|
||||
}
|
||||
|
||||
int rand(){
|
||||
seed = (seed + 0x7ed5 + (seed << 6));
|
||||
seed = (seed ^ 0xc23c ^ (seed >> 9));
|
||||
seed = (seed + 0x1656 + (seed << 3));
|
||||
seed = ((seed + 0xa264) ^ (seed << 4));
|
||||
seed = (seed + 0xfd70 - (seed << 3));
|
||||
seed = (seed ^ 0xba49 ^ (seed >> 8));
|
||||
|
||||
return (int)seed;
|
||||
}
|
||||
|
||||
void setSeed(int number){
|
||||
seed = ((unsigned short)(number*23729) ^ (unsigned short)(number+16786));
|
||||
rand();
|
||||
}
|
||||
void setSeed(int number1,int number2){
|
||||
seed = (((unsigned short)(number1*23729) | (unsigned short)(number2%16786)) ^ (unsigned short)(number2*number1));
|
||||
rand();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
float calc_height(fnl_state *noise, int cur_x, int cur_z){
|
||||
float height = 0;
|
||||
|
||||
@ -110,16 +81,16 @@ float calc_height(fnl_state *noise, int cur_x, int cur_z){
|
||||
}
|
||||
|
||||
WorldGenerator::WorldGenerator(const Content* content)
|
||||
: idStone(content->requireBlock("base:stone")->rt.id),
|
||||
idDirt(content->requireBlock("base:dirt")->rt.id),
|
||||
idGrassBlock(content->requireBlock("base:grass_block")->rt.id),
|
||||
idSand(content->requireBlock("base:sand")->rt.id),
|
||||
idWater(content->requireBlock("base:water")->rt.id),
|
||||
idWood(content->requireBlock("base:wood")->rt.id),
|
||||
idLeaves(content->requireBlock("base:leaves")->rt.id),
|
||||
idGrass(content->requireBlock("base:grass")->rt.id),
|
||||
idFlower(content->requireBlock("base:flower")->rt.id),
|
||||
idBazalt(content->requireBlock("base:bazalt")->rt.id) {}
|
||||
: idStone(content->requireBlock("base:stone").rt.id),
|
||||
idDirt(content->requireBlock("base:dirt").rt.id),
|
||||
idGrassBlock(content->requireBlock("base:grass_block").rt.id),
|
||||
idSand(content->requireBlock("base:sand").rt.id),
|
||||
idWater(content->requireBlock("base:water").rt.id),
|
||||
idWood(content->requireBlock("base:wood").rt.id),
|
||||
idLeaves(content->requireBlock("base:leaves").rt.id),
|
||||
idGrass(content->requireBlock("base:grass").rt.id),
|
||||
idFlower(content->requireBlock("base:flower").rt.id),
|
||||
idBazalt(content->requireBlock("base:bazalt").rt.id) {}
|
||||
|
||||
int generate_tree(fnl_state *noise,
|
||||
PseudoRandom* random,
|
||||
|
||||
@ -335,4 +335,4 @@ bool Window::tryToMaximize(GLFWwindow* window, GLFWmonitor* monitor) {
|
||||
glfwSetWindowPos(window, workArea.x + (workArea.z - Window::width) / 2,
|
||||
workArea.y + (workArea.w - Window::height) / 2 + windowFrame.y / 2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "../physics/PhysicsSolver.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../items/Inventory.h"
|
||||
#include "../items/Inventories.h"
|
||||
|
||||
Level::Level(World* world, const Content* content, Player* player, EngineSettings& settings)
|
||||
: world(world),
|
||||
@ -30,6 +31,8 @@ Level::Level(World* world, const Content* content, Player* player, EngineSetting
|
||||
events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type type, Chunk* chunk) {
|
||||
this->chunksStorage->remove(chunk->x, chunk->z);
|
||||
});
|
||||
|
||||
inventories = std::make_unique<Inventories>(*this);
|
||||
}
|
||||
|
||||
Level::~Level(){
|
||||
|
||||
@ -11,6 +11,7 @@ class World;
|
||||
class Player;
|
||||
class Chunks;
|
||||
class Inventory;
|
||||
class Inventories;
|
||||
class LevelEvents;
|
||||
class Lighting;
|
||||
class PhysicsSolver;
|
||||
@ -23,6 +24,7 @@ public:
|
||||
Player* player;
|
||||
Chunks* chunks;
|
||||
ChunksStorage* chunksStorage;
|
||||
std::unique_ptr<Inventories> inventories;
|
||||
|
||||
PhysicsSolver* physics;
|
||||
Lighting* lighting;
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "../voxels/ChunksStorage.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../items/Inventories.h"
|
||||
|
||||
world_load_error::world_load_error(std::string message)
|
||||
: std::runtime_error(message) {
|
||||
@ -65,6 +66,7 @@ void World::write(Level* level) {
|
||||
|
||||
const float DEF_PLAYER_Y = 100.0f;
|
||||
const float DEF_PLAYER_SPEED = 4.0f;
|
||||
const int DEF_PLAYER_INVENTORY_SIZE = 40;
|
||||
|
||||
Level* World::create(std::string name,
|
||||
fs::path directory,
|
||||
@ -73,8 +75,13 @@ Level* World::create(std::string name,
|
||||
const Content* content,
|
||||
const std::vector<ContentPack>& packs) {
|
||||
auto world = new World(name, directory, seed, settings, content, packs);
|
||||
auto player = new Player(glm::vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED);
|
||||
return new Level(world, content, player, settings);
|
||||
auto inv = std::make_shared<Inventory>(world->getNextInventoryId(), DEF_PLAYER_INVENTORY_SIZE);
|
||||
auto player = new Player(
|
||||
glm::vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED, inv
|
||||
);
|
||||
auto level = new Level(world, content, player, settings);
|
||||
level->inventories->store(player->getInventory());
|
||||
return level;
|
||||
}
|
||||
|
||||
Level* World::load(fs::path directory,
|
||||
@ -90,10 +97,13 @@ Level* World::load(fs::path directory,
|
||||
throw world_load_error("could not to find world.json");
|
||||
}
|
||||
|
||||
auto player = new Player(glm::vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED);
|
||||
auto inv = std::make_shared<Inventory>(0, DEF_PLAYER_INVENTORY_SIZE);
|
||||
auto player = new Player(
|
||||
glm::vec3(0, DEF_PLAYER_Y, 0), DEF_PLAYER_SPEED, inv
|
||||
);
|
||||
auto level = new Level(world.get(), content, player, settings);
|
||||
wfile->readPlayer(player);
|
||||
|
||||
level->inventories->store(player->getInventory());
|
||||
world.release();
|
||||
return level;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user