#include "scripting.h" #include "game.h" #include "components/runnable.h" #include "components/ponderable.h" #include "components/transformable.h" #include "components/playable.h" #include "components/mappable.h" #include "components/prototypable.h" #include "components/automatable.h" #include "systems/realizing.h" #include "vector.h" #include "muxer.h" struct script_entity { using id_type = EntityManager::id_type; id_type id; script_entity(id_type id) : id(id) { } }; ScriptingSystem::ScriptingSystem(Game& game) : System(game) { id_type entity = game_.getEntityManager().emplaceEntity(); engine_.open_libraries(sol::lib::base, sol::lib::coroutine); engine_.new_usertype( "vec2d", sol::constructors(), "x", sol::property( [] (vec2d& v) -> double { return v.x(); }, [] (vec2d& v, double x) { v.x() = x; }), "y", sol::property( [] (vec2d& v) -> double { return v.y(); }, [] (vec2d& v, double y) { v.y() = y; })); engine_.new_usertype( "vec2i", sol::constructors(), "x", [] (vec2i& v) -> int& { return v.x(); }, "y", [] (vec2i& v) -> int& { return v.y(); }); engine_.new_usertype( "entity", sol::constructors(), "id", &script_entity::id, "transformable", [&] (script_entity& entity) -> TransformableComponent& { return game_.getEntityManager(). getComponent(entity.id); }, "ponderable", [&] (script_entity& entity) -> PonderableComponent& { return game_.getEntityManager(). getComponent(entity.id); }, "mappable", [&] (script_entity& entity) -> MappableComponent& { return game_.getEntityManager(). getComponent(entity.id); }, "playable", [&] (script_entity& entity) -> PlayableComponent& { return game_.getEntityManager(). getComponent(entity.id); }, "prototypable", [&] (script_entity& entity) -> PrototypableComponent& { return game_.getEntityManager(). getComponent(entity.id); }); engine_.new_usertype( "transformable", "pos", &TransformableComponent::pos); engine_.new_usertype( "ponderable", "vel", &PonderableComponent::vel, "accel", &PonderableComponent::accel); engine_.new_usertype( "mappable", "mapId", &MappableComponent::mapId); engine_.new_usertype( "playable", "checkpointPos", &PlayableComponent::checkpointPos, "checkpointMapId", &PlayableComponent::checkpointMapId, "checkpointMapObject", &PlayableComponent::checkpointMapObject, "checkpointMapObjectIndex", &PlayableComponent::checkpointMapObjectIndex); engine_.new_usertype( "prototypable", "mapObjectIndex", &PrototypableComponent::mapObjectIndex, "prototypeId", &PrototypableComponent::prototypeId); engine_.new_usertype( "realizing", "activeMap", sol::property(&RealizingSystem::getActiveMap)); engine_.set_function( "realizing", [&] () -> RealizingSystem& { return game_.getSystemManager().getSystem(); }); engine_.set_function("playSound", playSound); engine_.script_file("scripts/common.lua"); engine_.script_file("scripts/movplat.lua"); engine_.script_file("scripts/checkpoint.lua"); } void ScriptingSystem::tick(double dt) { auto entities = game_.getEntityManager().getEntitiesWithComponents< RunnableComponent>(); for (id_type entity : entities) { auto& runnable = game_.getEntityManager(). getComponent(entity); if (*runnable.callable) { auto result = (*runnable.callable)(dt); if (!result.valid()) { sol::error e = result; throw std::runtime_error(e.what()); } } if (!*runnable.callable) { killScript(entity); } } } void ScriptingSystem::killScript(id_type entity) { auto& runnable = game_.getEntityManager(). getComponent(entity); if (runnable.behavior) { auto& automatable = game_.getEntityManager(). getComponent(runnable.actor); automatable.running = false; } game_.getEntityManager().deleteEntity(entity); } template sol::optional ScriptingSystem::runScript( std::string table, std::string event, id_type entity, Args&&... args) { id_type script = game_.getEntityManager().emplaceEntity(); auto& runnable = game_.getEntityManager(). emplaceComponent(script); runnable.runner = std::unique_ptr( new sol::thread( sol::thread::create( engine_.lua_state()))); runnable.callable = std::unique_ptr( new sol::coroutine( runnable.runner->state(). traverse_get( table, event))); if (!*runnable.callable) { throw std::runtime_error("Error running script"); } auto result = (*runnable.callable)( script_entity(entity), std::forward(args)...); if (!result.valid()) { sol::error e = result; throw std::runtime_error(e.what()); } 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; } } void ScriptingSystem::stopBehavior(id_type 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)); }