diff --git a/res/content/base/scripts/components/drop.lua b/res/content/base/scripts/components/drop.lua index f22e1ad2..1b67eeaf 100644 --- a/res/content/base/scripts/components/drop.lua +++ b/res/content/base/scripts/components/drop.lua @@ -6,15 +6,21 @@ inair = true ready = false target = -1 -local dropitem = ARGS.item +ARGS = ARGS or {} +local dropitem = ARGS.item or {} +if SAVED_DATA.item then + dropitem.id = item.index(SAVED_DATA.item) + dropitem.count = SAVED_DATA.count +end + local scale = {1, 1, 1} local rotation = mat4.rotate({ math.random(), math.random(), math.random() }, 360) function on_save() - SAVED_DATA.test = 5 - print("SAVE ENTITY") + SAVED_DATA.item = item.name(dropitem.id) + SAVED_DATA.count = dropitem.count end do -- setup visuals diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index d62277c7..37b96946 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -144,7 +144,7 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat else if (model == "custom") { def.model = BlockModel::custom; if (root->has("model-primitives")) { - loadCustomBlockModel(def, root->map("model-primitives")); + loadCustomBlockModel(def, root->map("model-primitives").get()); } else { logger.error() << name << ": no 'model-primitives' found"; } diff --git a/src/data/dynamic.cpp b/src/data/dynamic.cpp index 0cc88103..a7e78a44 100644 --- a/src/data/dynamic.cpp +++ b/src/data/dynamic.cpp @@ -55,9 +55,9 @@ integer_t List::integer(size_t index) const { } } -Map* List::map(size_t index) const { +const Map_sptr& List::map(size_t index) const { if (auto* val = std::get_if(&values[index])) { - return val->get(); + return *val; } else { throw std::runtime_error("type error"); } @@ -192,11 +192,11 @@ void Map::num(const std::string& key, uint& dst) const { dst = get(key, static_cast(dst)); } -Map* Map::map(const std::string& key) const { +Map_sptr Map::map(const std::string& key) const { auto found = values.find(key); if (found != values.end()) { if (auto* val = std::get_if(&found->second)) { - return val->get(); + return *val; } } return nullptr; diff --git a/src/data/dynamic.hpp b/src/data/dynamic.hpp index 1153697f..902a5adc 100644 --- a/src/data/dynamic.hpp +++ b/src/data/dynamic.hpp @@ -47,7 +47,7 @@ namespace dynamic { std::string str(size_t index) const; number_t num(size_t index) const; integer_t integer(size_t index) const; - Map* map(size_t index) const; + const Map_sptr& map(size_t index) const; List* list(size_t index) const; bool flag(size_t index) const; @@ -114,7 +114,7 @@ namespace dynamic { void num(const std::string& key, uint64_t& dst) const; void num(const std::string& key, ubyte& dst) const; void num(const std::string& key, double& dst) const; - Map* map(const std::string& key) const; + Map_sptr map(const std::string& key) const; List* list(const std::string& key) const; void flag(const std::string& key, bool& dst) const; diff --git a/src/data/dynamic_util.hpp b/src/data/dynamic_util.hpp index 3a10a123..d9e5566e 100644 --- a/src/data/dynamic_util.hpp +++ b/src/data/dynamic_util.hpp @@ -25,6 +25,15 @@ namespace dynamic { } return list; } + + template + void get_vec(const dynamic::Map_sptr& root, const std::string& name, glm::vec& vec) { + if (const auto& list = root->list(name)) { + for (size_t i = 0; i < n; i++) { + vec[i] = list->num(i); + } + } + } } #endif // DATA_DYNAMIC_UTIL_HPP_ diff --git a/src/files/WorldRegions.cpp b/src/files/WorldRegions.cpp index 9bec7921..6d2485d7 100644 --- a/src/files/WorldRegions.cpp +++ b/src/files/WorldRegions.cpp @@ -435,6 +435,15 @@ chunk_inventories_map WorldRegions::fetchInventories(int x, int z) { return meta; } + dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) { + uint32_t bytesSize; + const ubyte* data = getData(x, z, REGION_LAYER_ENTITIES, bytesSize); + if (data == nullptr) { + return nullptr; + } + return json::from_binary(data, bytesSize); + } + void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) { if (getRegion(x, z, REGION_LAYER_VOXELS)) { throw std::runtime_error("not implemented for in-memory regions"); diff --git a/src/files/WorldRegions.hpp b/src/files/WorldRegions.hpp index f96477de..f681794d 100644 --- a/src/files/WorldRegions.hpp +++ b/src/files/WorldRegions.hpp @@ -5,6 +5,7 @@ #include "../typedefs.hpp" #include "../util/BufferPool.hpp" #include "../voxels/Chunk.hpp" +#include "../data/dynamic_fwd.hpp" #include #include @@ -185,6 +186,7 @@ public: std::unique_ptr getChunk(int x, int z); std::unique_ptr getLights(int x, int z); chunk_inventories_map fetchInventories(int x, int z); + dynamic::Map_sptr fetchEntities(int x, int z); void processRegionVoxels(int x, int z, const regionproc& func); diff --git a/src/logic/scripting/lua/libentity.cpp b/src/logic/scripting/lua/libentity.cpp index 224c112f..c271062a 100644 --- a/src/logic/scripting/lua/libentity.cpp +++ b/src/logic/scripting/lua/libentity.cpp @@ -37,7 +37,10 @@ static int l_spawn(lua::State* L) { if (lua::gettop(L) > 2) { args = lua::tovalue(L, 3); } - level->entities->spawn(scripting::engine->getAssets(), def, pos, args); + Transform transform { + pos, glm::vec3(1.0f), glm::mat3(1.0f), {}, true + }; + level->entities->spawn(scripting::engine->getAssets(), def, transform, args); return 1; } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 8c5c16d3..2e6e76d6 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -276,7 +276,8 @@ void scripting::on_entity_spawn( const EntityDef& def, entityid_t eid, const std::vector>& components, - dynamic::Value args + dynamic::Value args, + dynamic::Map_sptr saved ) { auto L = lua::get_main_thread(); lua::requireglobal(L, STDCOMP); @@ -296,7 +297,15 @@ void scripting::on_entity_spawn( lua::pushvalue(L, args); lua::setfield(L, "ARGS"); - lua::createtable(L, 0, 0); + if (saved == nullptr) { + lua::createtable(L, 0, 0); + } else { + if (auto datamap = saved->map(component->name)) { + lua::pushvalue(L, datamap); + } else { + lua::createtable(L, 0, 0); + } + } lua::setfield(L, "SAVED_DATA"); lua::setfenv(L); diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 328068f2..ad0471fe 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -81,7 +81,8 @@ namespace scripting { const EntityDef& def, entityid_t eid, const std::vector>& components, - dynamic::Value args + dynamic::Value args, + dynamic::Map_sptr saved ); bool on_entity_despawn(const EntityDef& def, const Entity& entity); bool on_entity_grounded(const Entity& entity, float force); diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 38d21585..f514d6cc 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -1,8 +1,10 @@ #include "Entities.hpp" +#include "../debug/Logger.hpp" #include "../data/dynamic_util.hpp" #include "../assets/Assets.hpp" #include "../world/Level.hpp" +#include "../content/Content.hpp" #include "../physics/Hitbox.hpp" #include "../physics/PhysicsSolver.hpp" #include "../graphics/render/ModelBatch.hpp" @@ -12,9 +14,12 @@ #include "../objects/EntityDef.hpp" #include "../objects/rigging.hpp" #include "../logic/scripting/scripting.hpp" +#include "../engine.hpp" #include +static debug::Logger logger("entities"); + void Transform::refresh() { combined = glm::mat4(1.0f); combined = glm::translate(combined, pos); @@ -57,20 +62,26 @@ static triggercallback create_trigger_callback(Entities* entities) { entityid_t Entities::spawn( Assets* assets, EntityDef& def, - glm::vec3 pos, - dynamic::Value args) + Transform transform, + dynamic::Value args, + dynamic::Map_sptr saved, + entityid_t uid) { auto rig = assets->get(def.rigName); if (rig == nullptr) { throw std::runtime_error("rig "+def.rigName+" not found"); } auto entity = registry.create(); - glm::vec3 size(1); - auto id = nextID++; + entityid_t id; + if (uid == 0) { + id = nextID++; + } else { + id = uid; + } registry.emplace(entity, static_cast(id), def); - registry.emplace(entity, pos, size, glm::mat3(1.0f)); + registry.emplace(entity, transform); auto& body = registry.emplace( - entity, true, Hitbox {pos, def.hitbox}, std::vector{}); + entity, true, Hitbox {transform.pos, def.hitbox}, std::vector{}); body.triggers.resize(def.radialTriggers.size() + def.boxTriggers.size()); for (auto& [i, box] : def.boxTriggers) { @@ -98,7 +109,8 @@ entityid_t Entities::spawn( componentName, entity_funcs_set {}, nullptr); scripting.components.emplace_back(std::move(component)); } - scripting::on_entity_spawn(def, id, scripting.components, std::move(args)); + scripting::on_entity_spawn( + def, id, scripting.components, std::move(args), std::move(saved)); return id; } @@ -112,6 +124,37 @@ void Entities::despawn(entityid_t id) { } } +void Entities::loadEntity(const dynamic::Map_sptr& map) { + entityid_t uid = 0; + std::string defname; + map->num("uid", uid); + map->str("def", defname); + if (uid == 0) { + throw std::runtime_error("could not read entity - invalid UID"); + } + auto& def = level->content->entities.require(defname); + Transform transform { + glm::vec3(), glm::vec3(1.0f), glm::mat3(1.0f), {}, true + }; + if (auto tsfmap = map->map("transform")) { + dynamic::get_vec(tsfmap, "pos", transform.pos); + } + dynamic::Map_sptr savedMap = map->map("comps"); + auto assets = scripting::engine->getAssets(); + spawn(assets, def, transform, dynamic::NONE, savedMap, uid); +} + +void Entities::loadEntities(dynamic::Map_sptr root) { + auto list = root->list("data"); + for (size_t i = 0; i < list->size(); i++) { + try { + loadEntity(list->map(i)); + } catch (const std::runtime_error& err) { + logger.error() << "could not read entity: " << err.what(); + } + } +} + void Entities::onSave(const Entity& entity) { scripting::on_entity_save(entity); } @@ -129,7 +172,9 @@ dynamic::Value Entities::serialize(const Entity& entity) { if (transform.size != glm::vec3(1.0f)) { tsfmap.put("size", dynamic::to_value(transform.size)); } - tsfmap.put("rot", dynamic::to_value(transform.rot)); + if (transform.rot != glm::mat3(1.0f)) { + tsfmap.put("rot", dynamic::to_value(transform.rot)); + } } { auto& rigidbody = entity.getRigidbody(); @@ -166,6 +211,7 @@ dynamic::Value Entities::serialize(const Entity& entity) { auto data = scripting::get_component_value(comp->env, "SAVED_DATA"); compsMap.put(comp->name, data); } + std::cout << root << std::endl; } return root; } diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 51bfe0db..8492186c 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -172,8 +172,10 @@ public: entityid_t spawn( Assets* assets, EntityDef& def, - glm::vec3 pos, - dynamic::Value args=dynamic::NONE); + Transform transform, + dynamic::Value args=dynamic::NONE, + dynamic::Map_sptr saved=nullptr, + entityid_t uid=0); std::optional get(entityid_t id) { const auto& found = entities.find(id); @@ -183,6 +185,8 @@ public: return std::nullopt; } + void loadEntities(dynamic::Map_sptr map); + void loadEntity(const dynamic::Map_sptr& map); void onSave(const Entity& entity); std::vector getAllInside(AABB aabb); void despawn(entityid_t id); diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 91301c52..1cb1614a 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -251,10 +251,9 @@ void Player::deserialize(dynamic::Map *src) { src->flag("flight", flight); src->flag("noclip", noclip); setChosenSlot(src->get("chosen-slot", getChosenSlot())); - - auto invmap = src->map("inventory"); - if (invmap) { - getInventory()->deserialize(invmap); + + if (auto invmap = src->map("inventory")) { + getInventory()->deserialize(invmap.get()); } } @@ -264,16 +263,14 @@ void Player::convert(dynamic::Map* data, const ContentLUT* lut) { if (players) { for (uint i = 0; i < players->size(); i++) { auto playerData = players->map(i); - auto inventory = playerData->map("inventory"); - if (inventory) { - Inventory::convert(inventory, lut); + if (auto inventory = playerData->map("inventory")) { + Inventory::convert(inventory.get(), lut); } } } else { - auto inventory = data->map("inventory"); - if (inventory) { - Inventory::convert(inventory, lut); + if (auto inventory = data->map("inventory")) { + Inventory::convert(inventory.get(), lut); } } } diff --git a/src/objects/rigging.cpp b/src/objects/rigging.cpp index 275577aa..fc21cb41 100644 --- a/src/objects/rigging.cpp +++ b/src/objects/rigging.cpp @@ -94,8 +94,8 @@ static std::tuple> read_node( size_t count = 1; if (auto nodesList = root->list("nodes")) { for (size_t i = 0; i < nodesList->size(); i++) { - if (auto map = nodesList->map(i)) { - auto [subcount, subNode] = read_node(map, index+count); + if (const auto& map = nodesList->map(i)) { + auto [subcount, subNode] = read_node(map.get(), index+count); subcount += count; subnodes.push_back(std::move(subNode)); } @@ -114,6 +114,6 @@ std::unique_ptr RigConfig::parse( if (rootNodeMap == nullptr) { throw std::runtime_error("missing 'root' element"); } - auto [count, rootNode] = read_node(rootNodeMap, 0); + auto [count, rootNode] = read_node(rootNodeMap.get(), 0); return std::make_unique(std::string(name), std::move(rootNode), count); } diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 73b690af..bac62015 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -5,6 +5,7 @@ #include "Block.hpp" #include "../content/Content.hpp" #include "../files/WorldFiles.hpp" +#include "../objects/Entities.hpp" #include "../world/Level.hpp" #include "../world/World.hpp" #include "../maths/voxmaths.hpp" @@ -63,6 +64,10 @@ std::shared_ptr ChunksStorage::create(int x, int z) { auto invs = regions.fetchInventories(chunk->x, chunk->z); chunk->setBlockInventories(std::move(invs)); + if (auto map = regions.fetchEntities(chunk->x, chunk->z)) { + level->entities->loadEntities(std::move(map)); + } + chunk->flags.loaded = true; for(auto& entry : chunk->inventories) { level->inventories->store(entry.second); diff --git a/src/world/World.cpp b/src/world/World.cpp index e16b5e6e..0b10dc33 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -110,7 +110,7 @@ std::unique_ptr World::load( DEF_PLAYER_SPEED, level->inventories->create(DEF_PLAYER_INVENTORY_SIZE) ); - player->deserialize(players->map(i)); + player->deserialize(players->map(i).get()); level->inventories->store(player->getInventory()); } } else {