diff --git a/res/content/base/entities/drop.json b/res/content/base/entities/drop.json index e158ddcb..0be03b85 100644 --- a/res/content/base/entities/drop.json +++ b/res/content/base/entities/drop.json @@ -1,4 +1,7 @@ { + "components": [ + "drop" + ], "hitbox": [0.2, 0.125, 0.2], "triggers": [ ["aabb", -0.2, -0.2, -0.2, 0.2, 0.2, 0.2], diff --git a/res/modules/internal/stdcomp.lua b/res/modules/internal/stdcomp.lua index 71976711..a87b0a6e 100644 --- a/res/modules/internal/stdcomp.lua +++ b/res/modules/internal/stdcomp.lua @@ -53,6 +53,7 @@ return { entity.rigidbody = new_Rigidbody(eid) entity.modeltree = new_Modeltree(eid) entity.data = {} + entity.components = {} entities[eid] = entity; return entity end, @@ -62,17 +63,19 @@ return { remove_Entity = function(eid) local entity = entities[eid] if entity then - entity.env = nil + entity.components = nil entities[eid] = nil; end end, update = function() for id,entity in pairs(entities) do - local callback = entity.env.on_update - if callback then - local result, err = pcall(callback) - if err then - print(err) + for _, component in pairs(entity.components) do + local callback = component.on_update + if callback then + local result, err = pcall(callback) + if err then + print(err) + end end end end diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index f363702a..ab23be7c 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -311,7 +311,11 @@ void ContentLoader::loadItem(ItemDef& def, const std::string& name, const fs::pa void ContentLoader::loadEntity(EntityDef& def, const std::string& name, const fs::path& file) { auto root = files::read_json(file); - root->str("script-name", def.scriptName); + if (auto componentsarr = root->list("components")) { + for (size_t i = 0; i < componentsarr->size(); i++) { + def.components.push_back(componentsarr->str(i)); + } + } if (auto boxarr = root->list("hitbox")) { def.hitbox = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2)); } @@ -341,9 +345,11 @@ void ContentLoader::loadEntity(EntityDef& def, const std::string& full, const st auto configFile = folder/fs::path("entities/"+name+".json"); if (fs::exists(configFile)) loadEntity(def, full, configFile); - auto scriptfile = folder/fs::path("scripts/components/"+def.scriptName+".lua"); - if (fs::is_regular_file(scriptfile)) { - scripting::load_entity_component(env, def, scriptfile); + for (auto& componentName : def.components) { + auto scriptfile = folder/fs::path("scripts/components/"+componentName+".lua"); + if (fs::is_regular_file(scriptfile)) { + scripting::load_entity_component(env, componentName, scriptfile); + } } } @@ -437,7 +443,6 @@ void ContentLoader::load() { std::string full = colon == std::string::npos ? pack->id + ":" + name : name; if (colon != std::string::npos) name[colon] = '/'; auto& def = builder.entities.create(full); - if (colon != std::string::npos) def.scriptName = name.substr(0, colon) + '/' + def.scriptName; loadEntity(def, full, name); stats->totalEntities++; } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index c5c99fbc..b6d0ec9e 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -101,7 +101,9 @@ scriptenv scripting::create_doc_environment(const scriptenv& parent, const std:: } [[nodiscard]] -static scriptenv create_entity_environment(const scriptenv& parent, int entityIdx) { +static scriptenv create_component_environment(const scriptenv& parent, + int entityIdx, + const std::string& name) { auto L = lua::get_main_thread(); int id = lua::create_environment(L, *parent); @@ -115,7 +117,12 @@ static scriptenv create_entity_environment(const scriptenv& parent, int entityId lua::pushvalue(L, -2); lua::setfield(L, "entity"); - lua::setfield(L, "env"); + lua::pop(L); + if (lua::getfield(L, "components")) { + lua::pushenv(L, id); + lua::setfield(L, name); + lua::pop(L); + } lua::pop(L); return std::shared_ptr(new int(id), [=](int* id) { @@ -256,10 +263,10 @@ bool scripting::on_item_break_block(Player* player, const ItemDef* item, int x, }); } -scriptenv scripting::on_entity_spawn( +void scripting::on_entity_spawn( const EntityDef& def, entityid_t eid, - entity_funcs_set& funcsset, + const std::vector>& components, dynamic::Value args ) { auto L = lua::get_main_thread(); @@ -268,22 +275,30 @@ scriptenv scripting::on_entity_spawn( lua::pushinteger(L, eid); lua::call(L, 1); } - auto entityenv = create_entity_environment(get_root_environment(), -1); - lua::get_from(L, lua::CHUNKS_TABLE, def.scriptName, true); - lua::pushenv(L, *entityenv); - lua::pushvalue(L, args); - lua::setfield(L, "ARGS"); - lua::setfenv(L); - lua::call_nothrow(L, 0, 0); + for (size_t i = 0; i < components.size()-1; i++) { + lua::pushvalue(L, -1); + } + for (auto& component : components) { + auto compenv = create_component_environment( + get_root_environment(), -1, component->name); + lua::get_from(L, lua::CHUNKS_TABLE, component->name, true); + lua::pushenv(L, *compenv); + lua::pushvalue(L, args); + lua::setfield(L, "ARGS"); + lua::setfenv(L); + lua::call_nothrow(L, 0, 0); - lua::pushenv(L, *entityenv); - funcsset.on_grounded = lua::hasfield(L, "on_grounded"); - funcsset.on_fall = lua::hasfield(L, "on_fall"); - funcsset.on_despawn = lua::hasfield(L, "on_despawn"); - funcsset.on_trigger_enter = lua::hasfield(L, "on_trigger_enter"); - funcsset.on_trigger_exit = lua::hasfield(L, "on_trigger_exit"); - lua::pop(L, 2); - return entityenv; + lua::pushenv(L, *compenv); + auto& funcsset = component->funcsset; + funcsset.on_grounded = lua::hasfield(L, "on_grounded"); + funcsset.on_fall = lua::hasfield(L, "on_fall"); + funcsset.on_despawn = lua::hasfield(L, "on_despawn"); + funcsset.on_trigger_enter = lua::hasfield(L, "on_trigger_enter"); + funcsset.on_trigger_exit = lua::hasfield(L, "on_trigger_exit"); + lua::pop(L, 2); + + component->env = compenv; + } } static bool process_entity_callback( @@ -306,8 +321,10 @@ static bool process_entity_callback( bool scripting::on_entity_despawn(const EntityDef& def, const Entity& entity) { const auto& script = entity.getScripting(); - if (script.funcsset.on_despawn) { - process_entity_callback(script.env, "on_despawn", nullptr); + for (auto& component : script.components) { + if (component->funcsset.on_despawn) { + process_entity_callback(component->env, "on_despawn", nullptr); + } } auto L = lua::get_main_thread(); lua::get_from(L, "stdcomp", "remove_Entity", true); @@ -318,42 +335,50 @@ bool scripting::on_entity_despawn(const EntityDef& def, const Entity& entity) { bool scripting::on_entity_grounded(const Entity& entity, float force) { const auto& script = entity.getScripting(); - if (script.funcsset.on_grounded) { - return process_entity_callback(script.env, "on_grounded", [force](auto L){ - return lua::pushnumber(L, force); - }); + for (auto& component : script.components) { + if (component->funcsset.on_grounded) { + process_entity_callback(component->env, "on_grounded", [force](auto L){ + return lua::pushnumber(L, force); + }); + } } return true; } bool scripting::on_entity_fall(const Entity& entity) { const auto& script = entity.getScripting(); - if (script.funcsset.on_fall) { - return process_entity_callback(script.env, "on_fall", nullptr); + for (auto& component : script.components) { + if (component->funcsset.on_fall) { + process_entity_callback(component->env, "on_fall", nullptr); + } } return true; } void scripting::on_trigger_enter(const Entity& entity, size_t index, entityid_t oid) { const auto& script = entity.getScripting(); - if (script.funcsset.on_trigger_enter) { - process_entity_callback(script.env, "on_trigger_enter", [index, oid](auto L) { - lua::pushinteger(L, index); - lua::pushinteger(L, oid); - return 2; - }); + for (auto& component : script.components) { + if (component->funcsset.on_trigger_enter) { + process_entity_callback(component->env, "on_trigger_enter", [index, oid](auto L) { + lua::pushinteger(L, index); + lua::pushinteger(L, oid); + return 2; + }); + } } } void scripting::on_trigger_exit(const Entity& entity, size_t index, entityid_t oid) { const auto& script = entity.getScripting(); - if (script.funcsset.on_trigger_exit) { - process_entity_callback(script.env, "on_trigger_exit", [index, oid](auto L) { - lua::pushinteger(L, index); - lua::pushinteger(L, oid); - return 2; - }); + for (auto& component : script.components) { + if (component->funcsset.on_trigger_exit) { + process_entity_callback(component->env, "on_trigger_exit", [index, oid](auto L) { + lua::pushinteger(L, index); + lua::pushinteger(L, oid); + return 2; + }); + } } } @@ -447,12 +472,12 @@ void scripting::load_item_script(const scriptenv& senv, const std::string& prefi funcsset.on_block_break_by = register_event(env, "on_block_break_by", prefix+".blockbreakby"); } -void scripting::load_entity_component(const scriptenv& penv, const EntityDef& def, const fs::path& file) { +void scripting::load_entity_component(const scriptenv& penv, const std::string& name, const fs::path& file) { auto L = lua::get_main_thread(); std::string src = files::read_string(file); logger.info() << "script (component) " << file.u8string(); lua::loadbuffer(L, *penv, src, file.u8string()); - lua::store_in(L, lua::CHUNKS_TABLE, def.scriptName); + lua::store_in(L, lua::CHUNKS_TABLE, name); } void scripting::load_world_script(const scriptenv& senv, const std::string& prefix, const fs::path& file) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 72900a37..9bdf7367 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -27,7 +27,7 @@ class Inventory; class UiDocument; struct block_funcs_set; struct item_funcs_set; -struct entity_funcs_set; +struct UserComponent; struct uidocscript; class BlocksController; class LevelController; @@ -76,10 +76,10 @@ namespace scripting { /// @return true if prevents default action bool on_item_break_block(Player* player, const ItemDef* item, int x, int y, int z); - scriptenv on_entity_spawn( - const EntityDef& def, + void on_entity_spawn( + const EntityDef& def, entityid_t eid, - entity_funcs_set&, + const std::vector>& components, dynamic::Value args ); bool on_entity_despawn(const EntityDef& def, const Entity& entity); @@ -124,7 +124,7 @@ namespace scripting { void load_entity_component( const scriptenv& env, - const EntityDef& def, + const std::string& name, const fs::path& file); /// @brief Load package-specific world script diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 058e3ec4..3e3f9afe 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -90,12 +90,15 @@ entityid_t Entities::spawn( create_trigger_callback(this) }; } - auto& scripting = registry.emplace( - entity, entity_funcs_set {}, nullptr); + auto& scripting = registry.emplace(entity); entities[id] = entity; registry.emplace(entity, rig->instance()); - scripting.env = scripting::on_entity_spawn( - def, id, scripting.funcsset, std::move(args)); + for (auto& componentName : def.components) { + auto component = std::make_unique( + componentName, entity_funcs_set {}, nullptr); + scripting.components.emplace_back(std::move(component)); + } + scripting::on_entity_spawn(def, id, scripting.components, std::move(args)); return id; } diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 58ceb7f5..1aca686b 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -6,6 +6,7 @@ #include "../data/dynamic.hpp" #include +#include #include #include #define GLM_ENABLE_EXPERIMENTAL @@ -65,9 +66,23 @@ struct Rigidbody { std::vector triggers; }; -struct Scripting { +struct UserComponent { + std::string name; entity_funcs_set funcsset; scriptenv env; + + UserComponent(const std::string& name, entity_funcs_set funcsset, scriptenv env) + : name(name), funcsset(funcsset), env(env) {} +}; + +struct ScriptComponents { + std::vector> components; + + ScriptComponents() = default; + + ScriptComponents(ScriptComponents&& other) + : components(std::move(other.components)) { + } }; class Level; @@ -116,8 +131,8 @@ public: return registry.get(entity); } - Scripting& getScripting() const { - return registry.get(entity); + ScriptComponents& getScripting() const { + return registry.get(entity); } rigging::Rig& getModeltree() const; diff --git a/src/objects/EntityDef.hpp b/src/objects/EntityDef.hpp index a1044387..d8b87b87 100644 --- a/src/objects/EntityDef.hpp +++ b/src/objects/EntityDef.hpp @@ -15,8 +15,9 @@ namespace rigging { struct EntityDef { /// @brief Entity string id (with prefix included) std::string const name; + + std::vector components; - std::string scriptName = name.substr(name.find(':')+1); glm::vec3 hitbox {0.5f}; std::vector> boxTriggers {}; std::vector> radialTriggers {};