diff --git a/src/items/Inventories.cpp b/src/items/Inventories.cpp new file mode 100644 index 00000000..0b1ac2dc --- /dev/null +++ b/src/items/Inventories.cpp @@ -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 Inventories::create(size_t size) { + int64_t id = level.getWorld()->getNextInventoryId(); + auto inv = std::make_shared(id, size); + store(inv); + return inv; +} + +std::shared_ptr 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(id, size); + store(inv); + return inv; +} + +void Inventories::store(std::shared_ptr inv) { + map[inv->getId()] = inv; +} + +std::shared_ptr 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; +} diff --git a/src/items/Inventories.h b/src/items/Inventories.h new file mode 100644 index 00000000..9ad30343 --- /dev/null +++ b/src/items/Inventories.h @@ -0,0 +1,39 @@ +#ifndef ITEMS_INVENTORIES_H_ +#define ITEMS_INVENTORIES_H_ + +#include +#include +#include + +#include "Inventory.h" +#include "../maths/util.h" + +class Level; + +using inventories_map = std::unordered_map>; + +/* 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 create(size_t size); + + /* Create runtime-only inventory (has negative id) */ + std::shared_ptr createVirtual(size_t size); + + /* Store inventory */ + void store(std::shared_ptr inv); + + /* Get inventory by id (works with both real and virtual)*/ + std::shared_ptr get(int64_t id); + + const inventories_map& getMap() const; +}; + +#endif // ITEMS_INVENTORIES_H_ diff --git a/src/items/Inventory.cpp b/src/items/Inventory.cpp index e414d152..e844d18b 100644 --- a/src/items/Inventory.cpp +++ b/src/items/Inventory.cpp @@ -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) { diff --git a/src/items/Inventory.h b/src/items/Inventory.h index e24fd3df..6185f07d 100644 --- a/src/items/Inventory.h +++ b/src/items/Inventory.h @@ -14,10 +14,10 @@ class ContentLUT; class ContentIndices; class Inventory : Serializable { - uint id; + int64_t id; std::vector 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; }; diff --git a/src/logic/scripting/LuaState.cpp b/src/logic/scripting/LuaState.cpp index 2ae3c085..246f4647 100644 --- a/src/logic/scripting/LuaState.cpp +++ b/src/logic/scripting/LuaState.cpp @@ -94,6 +94,7 @@ 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); diff --git a/src/logic/scripting/api_lua.h b/src/logic/scripting/api_lua.h index 53444a3c..20dece5c 100644 --- a/src/logic/scripting/api_lua.h +++ b/src/logic/scripting/api_lua.h @@ -17,7 +17,9 @@ #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" @@ -229,7 +231,83 @@ static const luaL_Reg playerlib [] = { {NULL, NULL} }; -/* == items-related functions == */ +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 const luaL_Reg inventorylib [] = { + {"get", l_inventory_get}, + {"set", l_inventory_set}, + {"size", l_inventory_size}, + {"add", l_inventory_add}, + {NULL, NULL} +}; + +/* == item library == */ static int l_item_name(lua_State* L) { auto indices = scripting::content->getIndices(); lua::luaint id = lua_tointeger(L, 1); diff --git a/src/logic/scripting/scripting.h b/src/logic/scripting/scripting.h index bd51a886..717ad88a 100644 --- a/src/logic/scripting/scripting.h +++ b/src/logic/scripting/scripting.h @@ -9,7 +9,7 @@ class LuaState; class Engine; class Content; -class ContentPack; +struct ContentPack; class ContentIndices; class Level; class Block; @@ -29,6 +29,7 @@ namespace scripting { extern Level* level; extern BlocksController* blocks; + /* Lua environment wrapper for automatic deletion */ class Environment { int env; public: @@ -64,13 +65,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); + + /* 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(); } diff --git a/src/maths/util.h b/src/maths/util.h new file mode 100644 index 00000000..15774ae4 --- /dev/null +++ b/src/maths/util.h @@ -0,0 +1,49 @@ +#ifndef MATHS_UTIL_H_ +#define MATHS_UTIL_H_ + +#include +#include + +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_ diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 32ad381f..39268de6 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -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 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 Player::serialize() const { } void Player::deserialize(dynamic::Map *src) { - auto posarr = src->list("position"); glm::vec3& position = hitbox->position; position.x = posarr->num(0); diff --git a/src/objects/Player.h b/src/objects/Player.h index 390a2147..18f92e55 100644 --- a/src/objects/Player.h +++ b/src/objects/Player.h @@ -48,7 +48,7 @@ public: glm::vec2 cam = {}; - Player(glm::vec3 position, float speed); + Player(glm::vec3 position, float speed, std::shared_ptr inv); ~Player(); void teleport(glm::vec3 position); diff --git a/src/voxels/WorldGenerator.cpp b/src/voxels/WorldGenerator.cpp index a58ca580..c2d8b766 100644 --- a/src/voxels/WorldGenerator.cpp +++ b/src/voxels/WorldGenerator.cpp @@ -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; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 4bd23183..90d0df46 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -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(*this); } Level::~Level(){ diff --git a/src/world/Level.h b/src/world/Level.h index 2f42a78b..e670422f 100644 --- a/src/world/Level.h +++ b/src/world/Level.h @@ -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; PhysicsSolver* physics; Lighting* lighting; diff --git a/src/world/World.cpp b/src/world/World.cpp index d3e934c5..743a36df 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -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,12 @@ Level* World::create(std::string name, const Content* content, const std::vector& 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(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()); } Level* World::load(fs::path directory, @@ -90,10 +96,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(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; }