From 5269e7c09a0b17c8c972c8ad996b04d42dbcd9cb Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 13 May 2018 00:50:11 -0400 Subject: Started event handlers The AutomatingSystem has been renamed to the ScriptingSystem, since the automatic behavior script is just a special case of the scripts that an entity can exhibit. The AutomatableComponent has largely been moved to the new RunnableComponent (might not be the final name for it). The Lua state object, previously living on the singleton RealizableComponent, is now a member of the ScriptingSystem itself, because it A) doesn't really belong on the realizable entity, and B) a singleton entity seems weird and like a cumbersome attempt to apply the ECS rules to places they don't apply. In a similar vein, the RealizableComponent itself will probably soon be integrated into the RealizingSystem too. The attempt at using Lua environments in order to encapsulate the different behaviors that objects exhibit was scrapped in preference of just creating differently named Lua tables for each prototype. The new PrototypableComponent contains some information about entities which were prototyped. It is partially used by the ScriptingSystem to figure out what event handlers are appropriate, which may not be the best approach. It also has some data about automatic behavior, which also maybe does not belong in this component. The OnTouch event is raised by a player colliding with a physics body with the collider type "event", which may not be the best way to implement this. The result of all of this is that checkpoints now work, although no sound is played, and the result is not persistent across exiting the game. --- src/systems/scripting.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/systems/scripting.h (limited to 'src/systems/scripting.h') diff --git a/src/systems/scripting.h b/src/systems/scripting.h new file mode 100644 index 0000000..d5380f1 --- /dev/null +++ b/src/systems/scripting.h @@ -0,0 +1,28 @@ +#ifndef AUTOMATING_H_E6E5D76E +#define AUTOMATING_H_E6E5D76E + +#include "system.h" +#include + +class ScriptingSystem : public System { +public: + + ScriptingSystem(Game& game); + + void tick(double dt); + + void killScript(id_type entity); + + id_type runBehaviorScript(id_type entity); + + void onTouch(id_type entity, id_type player); + +private: + + template + id_type runScript(std::string event, id_type entity, Args&&... args); + + sol::state engine; +}; + +#endif /* end of include guard: AUTOMATING_H_E6E5D76E */ -- cgit 1.4.1 From 046ee24a341468e9b3ea2983a731dbce18b52ac6 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 13 May 2018 11:00:02 -0400 Subject: Integrated RealizableComponent into RealizingSystem --- scripts/checkpoint.lua | 2 +- src/components/realizable.h | 74 ------------------------------- src/game.cpp | 3 +- src/systems/mapping.cpp | 8 +--- src/systems/playing.cpp | 26 ++++------- src/systems/pondering.cpp | 8 +--- src/systems/realizing.cpp | 105 ++++++++++++++++++-------------------------- src/systems/realizing.h | 66 ++++++++++++++++++++++------ src/systems/scripting.cpp | 45 +++++++------------ src/systems/scripting.h | 2 +- 10 files changed, 127 insertions(+), 212 deletions(-) delete mode 100644 src/components/realizable.h (limited to 'src/systems/scripting.h') diff --git a/scripts/checkpoint.lua b/scripts/checkpoint.lua index 452f81d..a5c8c54 100644 --- a/scripts/checkpoint.lua +++ b/scripts/checkpoint.lua @@ -1,7 +1,7 @@ checkpoint = {} function checkpoint.OnTouch(id, player) - curMap = entity.new(realizing():singleton():realizable().activeMap) + curMap = entity.new(realizing().activeMap) if not player:playable().checkpointMapObject or not curMap:mappable().mapId == player:playable().checkpointMapId or diff --git a/src/components/realizable.h b/src/components/realizable.h deleted file mode 100644 index b749aeb..0000000 --- a/src/components/realizable.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef REALIZABLE_H_36D8D71E -#define REALIZABLE_H_36D8D71E - -#include "component.h" -#include -#include -#include "entity_manager.h" -#include "vector.h" - -class RealizableComponent : public Component { -public: - - using id_type = EntityManager::id_type; - - /** - * Path to the XML file containing the world definition. - * - * @managed_by RealizingSystem - */ - std::string worldFile; - - /** - * Path to the XML file containing the map object prototype definitions. - * - * @managed_by RealizingSystem - */ - std::string prototypeFile; - - /** - * Starting map and player location for a new game. - * - * @managed_by RealizingSystem - */ - int startingMapId; - vec2i startingPos; - - /** - * The set of map entities loaded by this entity. It is only intended for - * there to be one realizable entity, so this should contain all loaded maps. - * The realizable entity has ownership of the loaded maps. - * - * @managed_by RealizingSystem - */ - std::set maps; - - /** - * A lookup table that translates a map ID to the entity representing that - * loaded map. - * - * @managed_by RealizingSystem - */ - std::map entityByMapId; - - /** - * The entity ID of the currently active map. - * - * @managed_by RealizingSystem - */ - id_type activeMap; - - /** - * Whether or not a map has been activated yet. - * - * @managed_by RealizingSystem - */ - bool hasActiveMap = false; - - /** - * The entity ID of the currently active player. - */ - id_type activePlayer; -}; - -#endif /* end of include guard: REALIZABLE_H_36D8D71E */ diff --git a/src/game.cpp b/src/game.cpp index f2992e1..c76349f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -32,7 +32,6 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) Game::Game(std::mt19937& rng) : rng_(rng) { - systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); @@ -42,7 +41,7 @@ Game::Game(std::mt19937& rng) : rng_(rng) systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); - systemManager_.getSystem().initSingleton( + systemManager_.emplaceSystem(*this, "res/maps.xml", "res/entities.xml"); diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index d78c8fe..1275e11 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -1,6 +1,5 @@ #include "mapping.h" #include "components/mappable.h" -#include "components/realizable.h" #include "systems/realizing.h" #include "game.h" #include "consts.h" @@ -20,11 +19,8 @@ inline void addBoundary( void MappingSystem::render(Texture& texture) { - auto& realizable = game_.getEntityManager(). - getComponent( - game_.getSystemManager().getSystem().getSingleton()); - - id_type map = realizable.activeMap; + id_type map = + game_.getSystemManager().getSystem().getActiveMap(); auto& mappable = game_.getEntityManager(). getComponent(map); diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp index dabc9a5..6652099 100644 --- a/src/systems/playing.cpp +++ b/src/systems/playing.cpp @@ -5,7 +5,6 @@ #include "components/playable.h" #include "components/controllable.h" #include "components/orientable.h" -#include "components/realizable.h" #include "systems/mapping.h" #include "systems/pondering.h" #include "systems/orienting.h" @@ -36,13 +35,10 @@ void PlayingSystem::initPlayer() auto& realizing = game_.getSystemManager().getSystem(); - auto& realizable = game_.getEntityManager(). - getComponent(realizing.getSingleton()); - auto& transformable = game_.getEntityManager(). emplaceComponent(player); - transformable.pos = realizable.startingPos; + transformable.pos = realizing.getStartingPos(); transformable.size.w() = 10; transformable.size.h() = 12; @@ -56,13 +52,13 @@ void PlayingSystem::initPlayer() auto& playable = game_.getEntityManager(). emplaceComponent(player); - playable.mapId = realizable.activeMap; - playable.checkpointMapId = realizable.startingMapId; - playable.checkpointPos = realizable.startingPos; + playable.mapId = realizing.getActiveMap(); + playable.checkpointMapId = realizing.getStartingMapId(); + playable.checkpointPos = realizing.getStartingPos(); realizing.enterActiveMap(player); - realizable.activePlayer = player; + realizing.setActivePlayer(player); } void PlayingSystem::changeMap( @@ -77,20 +73,16 @@ void PlayingSystem::changeMap( getComponent(player); auto& pondering = game_.getSystemManager().getSystem(); - auto& realizing = game_.getSystemManager().getSystem(); - auto& realizable = game_.getEntityManager(). - getComponent(realizing.getSingleton()); - - id_type newMapEntity = realizable.entityByMapId[mapId]; + id_type newMapEntity = realizing.getEntityByMapId(mapId); if (playable.mapId != newMapEntity) { - if (playable.mapId == realizable.activeMap) + if (playable.mapId == realizing.getActiveMap()) { realizing.leaveActiveMap(player); - } else if (newMapEntity == realizable.activeMap) + } else if (newMapEntity == realizing.getActiveMap()) { realizing.enterActiveMap(player); } @@ -102,7 +94,7 @@ void PlayingSystem::changeMap( transformable.pos = warpPos; - if (realizable.activePlayer == player) + if (realizing.getActivePlayer() == player) { realizing.loadMap(newMapEntity); } diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index a3eb36d..d841679 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -6,7 +6,6 @@ #include "components/transformable.h" #include "components/orientable.h" #include "components/mappable.h" -#include "components/realizable.h" #include "components/playable.h" #include "systems/orienting.h" #include "systems/playing.h" @@ -485,11 +484,8 @@ void PonderingSystem::detectCollisionsInDirection( CollisionResult& result) { // Get map data. - auto& realizable = game_.getEntityManager(). - getComponent( - game_.getSystemManager().getSystem().getSingleton()); - - id_type mapEntity = realizable.activeMap; + id_type mapEntity = + game_.getSystemManager().getSystem().getActiveMap(); auto& mappable = game_.getEntityManager(). getComponent(mapEntity); diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index 2ee5897..7f5aefb 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp @@ -6,7 +6,6 @@ #include "game.h" #include "consts.h" #include "animation.h" -#include "components/realizable.h" #include "components/mappable.h" #include "components/animatable.h" #include "components/playable.h" @@ -30,25 +29,21 @@ inline xmlChar* getProp(xmlNodePtr node, const char* attr) } // TODO: neither the XML doc nor any of the emplaced entities are properly -// destroyed if this method throws an exception. -EntityManager::id_type RealizingSystem::initSingleton( +// destroyed if this constructor throws an exception. +RealizingSystem::RealizingSystem( + Game& game, std::string worldFile, - std::string prototypeFile) + std::string prototypeFile) : + System(game), + worldFile_(std::move(worldFile)), + prototypeFile_(std::move(prototypeFile)) { - id_type world = game_.getEntityManager().emplaceEntity(); - - auto& realizable = game_.getEntityManager(). - emplaceComponent(world); - - realizable.worldFile = worldFile; - realizable.prototypeFile = prototypeFile; - auto& mapping = game_.getSystemManager().getSystem(); xmlChar* key = nullptr; // Create a mapping between prototype names and the XML trees defining them. - xmlDocPtr protoXml = xmlParseFile(prototypeFile.c_str()); + xmlDocPtr protoXml = xmlParseFile(prototypeFile_.c_str()); if (protoXml == nullptr) { throw std::invalid_argument("Cannot find prototypes file"); @@ -82,7 +77,7 @@ EntityManager::id_type RealizingSystem::initSingleton( } // Create entities from the world definition. - xmlDocPtr doc = xmlParseFile(worldFile.c_str()); + xmlDocPtr doc = xmlParseFile(worldFile_.c_str()); if (doc == nullptr) { throw std::invalid_argument("Cannot find world file"); @@ -100,15 +95,15 @@ EntityManager::id_type RealizingSystem::initSingleton( } key = getProp(top, "startx"); - realizable.startingPos.x() = atoi(reinterpret_cast(key)); + startingPos_.x() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(top, "starty"); - realizable.startingPos.y() = atoi(reinterpret_cast(key)); + startingPos_.y() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(top, "startmap"); - realizable.startingMapId = atoi(reinterpret_cast(key)); + startingMapId_ = atoi(reinterpret_cast(key)); xmlFree(key); for (xmlNodePtr node = top->xmlChildrenNode; @@ -291,78 +286,62 @@ EntityManager::id_type RealizingSystem::initSingleton( mapping.generateBoundaries(map); - realizable.maps.insert(map); - realizable.entityByMapId[mappable.mapId] = map; + entityByMapId_[mappable.mapId] = map; } } xmlFreeDoc(doc); xmlFreeDoc(protoXml); - loadMap(realizable.entityByMapId[realizable.startingMapId]); - - return world; + activateMap(entityByMapId_[startingMapId_]); } -EntityManager::id_type RealizingSystem::getSingleton() const +void RealizingSystem::loadMap(id_type mapEntity) { - std::set result = - game_.getEntityManager().getEntitiesWithComponents< - RealizableComponent>(); - - if (result.empty()) - { - throw std::logic_error("No realizable entity found"); - } else if (result.size() > 1) - { - throw std::logic_error("Multiple realizable entities found"); - } - - return *std::begin(result); + deactivateMap(); + activateMap(mapEntity); } -void RealizingSystem::loadMap(id_type mapEntity) +void RealizingSystem::deactivateMap() { - id_type world = getSingleton(); + id_type oldMap = activeMap_; - auto& realizable = game_.getEntityManager(). - getComponent(world); + auto& oldMappable = game_.getEntityManager(). + getComponent(oldMap); - auto& animating = game_.getSystemManager().getSystem(); - auto& pondering = game_.getSystemManager().getSystem(); + // Deactivate any map objects from the old map. + for (id_type prototype : oldMappable.objects) + { + leaveActiveMap(prototype); + } + // Deactivate players that were on the old map. std::set players = game_.getEntityManager().getEntitiesWithComponents< PlayableComponent>(); - if (realizable.hasActiveMap) + for (id_type player : players) { - id_type oldMap = realizable.activeMap; - - auto& oldMappable = game_.getEntityManager(). - getComponent(oldMap); + auto& playable = game_.getEntityManager(). + getComponent(player); - // Deactivate any map objects from the old map. - for (id_type prototype : oldMappable.objects) + if (playable.mapId == oldMap) { - leaveActiveMap(prototype); + leaveActiveMap(player); } + } +} - // Deactivate players that were on the old map. - for (id_type player : players) - { - auto& playable = game_.getEntityManager(). - getComponent(player); +void RealizingSystem::activateMap(id_type mapEntity) +{ + auto& animating = game_.getSystemManager().getSystem(); + auto& pondering = game_.getSystemManager().getSystem(); - if (playable.mapId == oldMap) - { - leaveActiveMap(player); - } - } - } + std::set players = + game_.getEntityManager().getEntitiesWithComponents< + PlayableComponent>(); - realizable.hasActiveMap = true; - realizable.activeMap = mapEntity; + activeMap_ = mapEntity; auto& mappable = game_.getEntityManager(). getComponent(mapEntity); diff --git a/src/systems/realizing.h b/src/systems/realizing.h index 595c58f..ab5a150 100644 --- a/src/systems/realizing.h +++ b/src/systems/realizing.h @@ -2,29 +2,56 @@ #define REALIZING_H_6853748C #include +#include #include "system.h" +#include "vector.h" class RealizingSystem : public System { public: - RealizingSystem(Game& game) : System(game) - { - } - /** - * Creates the singleton realizable entity and initializes it with the - * provided world definition and map object prototype definition. + * Constructs the realizing system. + * + * Note that this must be constructed after the following system: + * - Mapping + * - Animating + * - Pondering + * - Scripting */ - id_type initSingleton( + RealizingSystem( + Game& game, std::string worldFile, std::string prototypeFile); - /** - * Helper method that returns the entity ID of the (assumed) singleton entity - * with a RealizableComponent. Throws an exception if the number of realizable - * entities is not exactly one. - */ - id_type getSingleton() const; + id_type getActiveMap() const + { + return activeMap_; + } + + int getStartingMapId() const + { + return startingMapId_; + } + + vec2i getStartingPos() const + { + return startingPos_; + } + + id_type getEntityByMapId(size_t mapId) const + { + return entityByMapId_.at(mapId); + } + + id_type getActivePlayer() const + { + return activePlayer_; + } + + void setActivePlayer(id_type entity) + { + activePlayer_ = entity; + } /** * Loads the given map. @@ -41,6 +68,19 @@ public: */ void leaveActiveMap(id_type entity); +private: + + void deactivateMap(); + + void activateMap(id_type mapEntity); + + std::string worldFile_; + std::string prototypeFile_; + int startingMapId_; + vec2i startingPos_; + std::map entityByMapId_; + id_type activeMap_; + id_type activePlayer_; }; #endif /* end of include guard: REALIZING_H_6853748C */ diff --git a/src/systems/scripting.cpp b/src/systems/scripting.cpp index dc1fff5..57c3fd5 100644 --- a/src/systems/scripting.cpp +++ b/src/systems/scripting.cpp @@ -2,7 +2,6 @@ #include "game.h" #include "components/runnable.h" #include "components/ponderable.h" -#include "components/realizable.h" #include "components/transformable.h" #include "components/playable.h" #include "components/mappable.h" @@ -24,9 +23,9 @@ ScriptingSystem::ScriptingSystem(Game& game) : System(game) { id_type entity = game_.getEntityManager().emplaceEntity(); - engine.open_libraries(sol::lib::base, sol::lib::coroutine); + engine_.open_libraries(sol::lib::base, sol::lib::coroutine); - engine.new_usertype( + engine_.new_usertype( "vec2d", sol::constructors(), "x", sol::property( @@ -36,13 +35,13 @@ ScriptingSystem::ScriptingSystem(Game& game) : System(game) [] (vec2d& v) -> double { return v.y(); }, [] (vec2d& v, double y) { v.y() = y; })); - engine.new_usertype( + engine_.new_usertype( "vec2i", sol::constructors(), "x", [] (vec2i& v) -> int& { return v.x(); }, "y", [] (vec2i& v) -> int& { return v.y(); }); - engine.new_usertype( + engine_.new_usertype( "entity", sol::constructors(), "id", &script_entity::id, @@ -66,62 +65,50 @@ ScriptingSystem::ScriptingSystem(Game& game) : System(game) return game_.getEntityManager(). getComponent(entity.id); }, - "realizable", - [&] (script_entity& entity) -> RealizableComponent& { - return game_.getEntityManager(). - getComponent(entity.id); - }, "prototypable", [&] (script_entity& entity) -> PrototypableComponent& { return game_.getEntityManager(). getComponent(entity.id); }); - engine.new_usertype( + engine_.new_usertype( "transformable", "pos", &TransformableComponent::pos); - engine.new_usertype( + engine_.new_usertype( "ponderable", "vel", &PonderableComponent::vel, "accel", &PonderableComponent::accel); - engine.new_usertype( + engine_.new_usertype( "mappable", "mapId", &MappableComponent::mapId); - engine.new_usertype( + engine_.new_usertype( "playable", "checkpointPos", &PlayableComponent::checkpointPos, "checkpointMapId", &PlayableComponent::checkpointMapId, "checkpointMapObject", &PlayableComponent::checkpointMapObject, "checkpointMapObjectIndex", &PlayableComponent::checkpointMapObjectIndex); - engine.new_usertype( - "realizable", - "activeMap", &RealizableComponent::activeMap); - - engine.new_usertype( + engine_.new_usertype( "prototypable", "mapObjectIndex", &PrototypableComponent::mapObjectIndex, "prototypeId", &PrototypableComponent::prototypeId); - engine.new_usertype( + engine_.new_usertype( "realizing", - "singleton", - [&] (RealizingSystem& realizing) -> script_entity { - return realizing.getSingleton(); - }); + "activeMap", sol::property(&RealizingSystem::getActiveMap)); - engine.set_function( + engine_.set_function( "realizing", [&] () { return game_.getSystemManager().getSystem(); }); - engine.script_file("scripts/common.lua"); - engine.script_file("scripts/movplat.lua"); - engine.script_file("scripts/checkpoint.lua"); + engine_.script_file("scripts/common.lua"); + engine_.script_file("scripts/movplat.lua"); + engine_.script_file("scripts/checkpoint.lua"); } void ScriptingSystem::tick(double dt) @@ -177,7 +164,7 @@ EntityManager::id_type ScriptingSystem::runScript( std::unique_ptr( new sol::thread( sol::thread::create( - engine.lua_state()))); + engine_.lua_state()))); runnable.callable = std::unique_ptr( diff --git a/src/systems/scripting.h b/src/systems/scripting.h index d5380f1..e330316 100644 --- a/src/systems/scripting.h +++ b/src/systems/scripting.h @@ -22,7 +22,7 @@ private: template id_type runScript(std::string event, id_type entity, Args&&... args); - sol::state engine; + sol::state engine_; }; #endif /* end of include guard: AUTOMATING_H_E6E5D76E */ -- cgit 1.4.1 From 5e48cf6333aca7af6854d79194f138d57ce0b5e1 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 14 May 2018 18:40:54 -0400 Subject: Specialized treatment of behavior scripts The AutomatableComponent now links to the Runnable entity representing the behavior script. Also reordered the SystemManager and EntityManager members of the Game class such that the EntityManager is destroyed before the SystemManager is. This fixes a bug where the destruction of a component has some affect on the state of a system. Specifically, if the ScriptingSystem (and thus the Lua state) is destroyed before the EntityManager is and there are any Runnable entities, the game will crash when trying to destroy them. --- src/components/automatable.h | 34 +++++++++++++++++++++ src/components/prototypable.h | 11 +++---- src/components/runnable.h | 36 ++++++++++++++++++++++ src/game.h | 2 +- src/systems/realizing.cpp | 33 ++++++-------------- src/systems/scripting.cpp | 70 ++++++++++++++++++++++++++++++++++++------- src/systems/scripting.h | 10 +++++-- 7 files changed, 153 insertions(+), 43 deletions(-) create mode 100644 src/components/automatable.h (limited to 'src/systems/scripting.h') diff --git a/src/components/automatable.h b/src/components/automatable.h new file mode 100644 index 0000000..22d9859 --- /dev/null +++ b/src/components/automatable.h @@ -0,0 +1,34 @@ +#ifndef AUTOMATABLE_H_FACB42A5 +#define AUTOMATABLE_H_FACB42A5 + +#include "component.h" +#include "entity_manager.h" + +class AutomatableComponent : public Component { +public: + + using id_type = EntityManager::id_type; + + /** + * Controls what script will be run as this entity's behavior. It should refer + * to a table in the global namespace of the script engine state, and that + * table should contain a function called "Behavior". + */ + std::string table; + + /** + * Whether or not the behavior script is running. + * + * @managed_by ScriptingSystem + */ + bool running = false; + + /** + * The entity ID of the running script, if there is one. + * + * @managed_by ScriptingSystem + */ + id_type script; +}; + +#endif /* end of include guard: AUTOMATABLE_H_FACB42A5 */ diff --git a/src/components/prototypable.h b/src/components/prototypable.h index 4659e7c..c0dd972 100644 --- a/src/components/prototypable.h +++ b/src/components/prototypable.h @@ -9,14 +9,15 @@ public: using id_type = EntityManager::id_type; + /** + * The index of the object in the map definition. + */ size_t mapObjectIndex; + /** + * The name of the prototype that the object was spawned from. + */ std::string prototypeId; - - bool hasBehavior = false; - bool runningBehavior = false; - - id_type behaviorScript; }; #endif /* end of include guard: PROTOTYPABLE_H_817F2205 */ diff --git a/src/components/runnable.h b/src/components/runnable.h index 1b994fb..956bfdc 100644 --- a/src/components/runnable.h +++ b/src/components/runnable.h @@ -4,12 +4,48 @@ #include "component.h" #include #include +#include "entity_manager.h" class RunnableComponent : public Component { public: + using id_type = EntityManager::id_type; + + /** + * A Lua stack where the entity's script is running. + * + * NOTE: This object is called a thread, but there is no multi-threading going + * on. + * + * @managed_by ScriptingSystem + */ std::unique_ptr runner; + + /** + * An entry point to the script running in the runner thread. + * + * @managed_by ScriptingSystem + */ std::unique_ptr callable; + + /** + * Whether or not this entity represents a behavior script. A behavior script + * usually does not terminate on its own, and can be terminated at will by + * another system, usually when the automatable entity leaves the active map. + * + * @managed_by ScriptingSystem + */ + bool behavior = false; + + /** + * If this is a behavior script, this is the ID of the automatable entity that + * the behavior belongs to. This is required so that the ScriptingSystem can + * notify the automatable entity if the behavior script terminates by itself, + * and that it shouldn't attempt to terminate it. + * + * @managed_by ScriptingSystem + */ + id_type actor; }; #endif /* end of include guard: AUTOMATABLE_H_3D519131 */ diff --git a/src/game.h b/src/game.h index dc256c6..d7fdcd7 100644 --- a/src/game.h +++ b/src/game.h @@ -44,8 +44,8 @@ private: std::mt19937 rng_; Renderer renderer_; - EntityManager entityManager_; SystemManager systemManager_; + EntityManager entityManager_; bool shouldQuit_ = false; }; diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index 7f5aefb..baacf5a 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp @@ -12,6 +12,7 @@ #include "components/ponderable.h" #include "components/transformable.h" #include "components/prototypable.h" +#include "components/automatable.h" #include "systems/mapping.h" #include "systems/animating.h" #include "systems/pondering.h" @@ -223,7 +224,10 @@ RealizingSystem::RealizingSystem( if (prototypeId == "movplat") { - prototypable.hasBehavior = true; + auto& automatable = game_.getEntityManager(). + emplaceComponent(mapObject); + + automatable.table = prototypeId; } else if (prototypeId == "checkpoint") { auto& ponderable = game_.getEntityManager(). @@ -403,19 +407,9 @@ void RealizingSystem::enterActiveMap(id_type entity) ponderable.active = true; } - if (game_.getEntityManager().hasComponent(entity)) + if (game_.getEntityManager().hasComponent(entity)) { - auto& prototypable = game_.getEntityManager(). - getComponent(entity); - - if (prototypable.hasBehavior) - { - auto& scripting = game_.getSystemManager().getSystem(); - - prototypable.hasBehavior = true; - prototypable.runningBehavior = true; - prototypable.behaviorScript = scripting.runBehaviorScript(entity); - } + game_.getSystemManager().getSystem().startBehavior(entity); } } @@ -437,17 +431,8 @@ void RealizingSystem::leaveActiveMap(id_type entity) ponderable.active = false; } - if (game_.getEntityManager().hasComponent(entity)) + if (game_.getEntityManager().hasComponent(entity)) { - auto& prototypable = game_.getEntityManager(). - getComponent(entity); - - if (prototypable.runningBehavior) - { - auto& scripting = game_.getSystemManager().getSystem(); - scripting.killScript(prototypable.behaviorScript); - - prototypable.runningBehavior = false; - } + game_.getSystemManager().getSystem().stopBehavior(entity); } } diff --git a/src/systems/scripting.cpp b/src/systems/scripting.cpp index 57c3fd5..c423558 100644 --- a/src/systems/scripting.cpp +++ b/src/systems/scripting.cpp @@ -6,6 +6,7 @@ #include "components/playable.h" #include "components/mappable.h" #include "components/prototypable.h" +#include "components/automatable.h" #include "systems/realizing.h" #include "vector.h" @@ -133,28 +134,34 @@ void ScriptingSystem::tick(double dt) if (!*runnable.callable) { - game_.getEntityManager().deleteEntity(entity); + killScript(entity); } } } void ScriptingSystem::killScript(id_type entity) { - if (game_.getEntityManager().hasComponent(entity)) + auto& runnable = game_.getEntityManager(). + getComponent(entity); + + if (runnable.behavior) { - game_.getEntityManager().deleteEntity(entity); + auto& automatable = game_.getEntityManager(). + getComponent(runnable.actor); + + automatable.running = false; } + + game_.getEntityManager().deleteEntity(entity); } template -EntityManager::id_type ScriptingSystem::runScript( +sol::optional ScriptingSystem::runScript( + std::string table, std::string event, id_type entity, Args&&... args) { - auto& prototypable = game_.getEntityManager(). - getComponent(entity); - id_type script = game_.getEntityManager().emplaceEntity(); auto& runnable = game_.getEntityManager(). @@ -171,7 +178,7 @@ EntityManager::id_type ScriptingSystem::runScript( new sol::coroutine( runnable.runner->state(). traverse_get( - prototypable.prototypeId, + table, event))); if (!*runnable.callable) @@ -189,17 +196,58 @@ EntityManager::id_type ScriptingSystem::runScript( throw std::runtime_error(e.what()); } - return script; + if (*runnable.callable) + { + return { script }; + } else { + killScript(script); + + return {}; + } +} + +void ScriptingSystem::startBehavior(id_type entity) +{ + auto& automatable = game_.getEntityManager(). + getComponent(entity); + + sol::optional script = + runScript( + automatable.table, + "Behavior", + entity); + + if (script) + { + automatable.script = *script; + automatable.running = true; + + auto& runnable = game_.getEntityManager(). + getComponent(automatable.script); + + runnable.behavior = true; + runnable.actor = entity; + } } -EntityManager::id_type ScriptingSystem::runBehaviorScript(id_type entity) +void ScriptingSystem::stopBehavior(id_type entity) { - return runScript("Behavior", entity); + auto& automatable = game_.getEntityManager(). + getComponent(entity); + + if (automatable.running) + { + killScript(automatable.script); + } } void ScriptingSystem::onTouch(id_type entity, id_type player) { + auto& prototypable = game_.getEntityManager(). + getComponent(entity); + runScript( + prototypable.prototypeId, "OnTouch", entity, script_entity(player)); diff --git a/src/systems/scripting.h b/src/systems/scripting.h index e330316..b119c3f 100644 --- a/src/systems/scripting.h +++ b/src/systems/scripting.h @@ -13,14 +13,20 @@ public: void killScript(id_type entity); - id_type runBehaviorScript(id_type entity); + void startBehavior(id_type entity); + + void stopBehavior(id_type entity); void onTouch(id_type entity, id_type player); private: template - id_type runScript(std::string event, id_type entity, Args&&... args); + sol::optional runScript( + std::string table, + std::string event, + id_type entity, + Args&&... args); sol::state engine_; }; -- cgit 1.4.1