summary refs log tree commit diff stats
path: root/src/systems/scripting.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-05-13 00:50:11 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-05-17 15:39:39 -0400
commit5269e7c09a0b17c8c972c8ad996b04d42dbcd9cb (patch)
tree94a3f4ce0a0e54375cd2f27fb90d7c35295bda2e /src/systems/scripting.cpp
parent59808c86bf0e4d5cf0b6ab3d6ed1d8bdcd303a37 (diff)
downloadtherapy-5269e7c09a0b17c8c972c8ad996b04d42dbcd9cb.tar.gz
therapy-5269e7c09a0b17c8c972c8ad996b04d42dbcd9cb.tar.bz2
therapy-5269e7c09a0b17c8c972c8ad996b04d42dbcd9cb.zip
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.
Diffstat (limited to 'src/systems/scripting.cpp')
-rw-r--r--src/systems/scripting.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/systems/scripting.cpp b/src/systems/scripting.cpp new file mode 100644 index 0000000..dc1fff5 --- /dev/null +++ b/src/systems/scripting.cpp
@@ -0,0 +1,219 @@
1#include "scripting.h"
2#include "game.h"
3#include "components/runnable.h"
4#include "components/ponderable.h"
5#include "components/realizable.h"
6#include "components/transformable.h"
7#include "components/playable.h"
8#include "components/mappable.h"
9#include "components/prototypable.h"
10#include "systems/realizing.h"
11#include "vector.h"
12
13struct script_entity {
14 using id_type = EntityManager::id_type;
15
16 id_type id;
17
18 script_entity(id_type id) : id(id)
19 {
20 }
21};
22
23ScriptingSystem::ScriptingSystem(Game& game) : System(game)
24{
25 id_type entity = game_.getEntityManager().emplaceEntity();
26
27 engine.open_libraries(sol::lib::base, sol::lib::coroutine);
28
29 engine.new_usertype<vec2d>(
30 "vec2d",
31 sol::constructors<vec2d(), vec2d(double, double)>(),
32 "x", sol::property(
33 [] (vec2d& v) -> double { return v.x(); },
34 [] (vec2d& v, double x) { v.x() = x; }),
35 "y", sol::property(
36 [] (vec2d& v) -> double { return v.y(); },
37 [] (vec2d& v, double y) { v.y() = y; }));
38
39 engine.new_usertype<vec2i>(
40 "vec2i",
41 sol::constructors<vec2i(), vec2i(int, int)>(),
42 "x", [] (vec2i& v) -> int& { return v.x(); },
43 "y", [] (vec2i& v) -> int& { return v.y(); });
44
45 engine.new_usertype<script_entity>(
46 "entity",
47 sol::constructors<script_entity(id_type)>(),
48 "id", &script_entity::id,
49 "transformable",
50 [&] (script_entity& entity) -> TransformableComponent& {
51 return game_.getEntityManager().
52 getComponent<TransformableComponent>(entity.id);
53 },
54 "ponderable",
55 [&] (script_entity& entity) -> PonderableComponent& {
56 return game_.getEntityManager().
57 getComponent<PonderableComponent>(entity.id);
58 },
59 "mappable",
60 [&] (script_entity& entity) -> MappableComponent& {
61 return game_.getEntityManager().
62 getComponent<MappableComponent>(entity.id);
63 },
64 "playable",
65 [&] (script_entity& entity) -> PlayableComponent& {
66 return game_.getEntityManager().
67 getComponent<PlayableComponent>(entity.id);
68 },
69 "realizable",
70 [&] (script_entity& entity) -> RealizableComponent& {
71 return game_.getEntityManager().
72 getComponent<RealizableComponent>(entity.id);
73 },
74 "prototypable",
75 [&] (script_entity& entity) -> PrototypableComponent& {
76 return game_.getEntityManager().
77 getComponent<PrototypableComponent>(entity.id);
78 });
79
80 engine.new_usertype<TransformableComponent>(
81 "transformable",
82 "pos", &TransformableComponent::pos);
83
84 engine.new_usertype<PonderableComponent>(
85 "ponderable",
86 "vel", &PonderableComponent::vel,
87 "accel", &PonderableComponent::accel);
88
89 engine.new_usertype<MappableComponent>(
90 "mappable",
91 "mapId", &MappableComponent::mapId);
92
93 engine.new_usertype<PlayableComponent>(
94 "playable",
95 "checkpointPos", &PlayableComponent::checkpointPos,
96 "checkpointMapId", &PlayableComponent::checkpointMapId,
97 "checkpointMapObject", &PlayableComponent::checkpointMapObject,
98 "checkpointMapObjectIndex", &PlayableComponent::checkpointMapObjectIndex);
99
100 engine.new_usertype<RealizableComponent>(
101 "realizable",
102 "activeMap", &RealizableComponent::activeMap);
103
104 engine.new_usertype<PrototypableComponent>(
105 "prototypable",
106 "mapObjectIndex", &PrototypableComponent::mapObjectIndex,
107 "prototypeId", &PrototypableComponent::prototypeId);
108
109 engine.new_usertype<RealizingSystem>(
110 "realizing",
111 "singleton",
112 [&] (RealizingSystem& realizing) -> script_entity {
113 return realizing.getSingleton();
114 });
115
116 engine.set_function(
117 "realizing",
118 [&] () {
119 return game_.getSystemManager().getSystem<RealizingSystem>();
120 });
121
122 engine.script_file("scripts/common.lua");
123 engine.script_file("scripts/movplat.lua");
124 engine.script_file("scripts/checkpoint.lua");
125}
126
127void ScriptingSystem::tick(double dt)
128{
129 auto entities = game_.getEntityManager().getEntitiesWithComponents<
130 RunnableComponent>();
131
132 for (id_type entity : entities)
133 {
134 auto& runnable = game_.getEntityManager().
135 getComponent<RunnableComponent>(entity);
136
137 if (*runnable.callable)
138 {
139 auto result = (*runnable.callable)(dt);
140 if (!result.valid())
141 {
142 sol::error e = result;
143 throw std::runtime_error(e.what());
144 }
145 }
146
147 if (!*runnable.callable)
148 {
149 game_.getEntityManager().deleteEntity(entity);
150 }
151 }
152}
153
154void ScriptingSystem::killScript(id_type entity)
155{
156 if (game_.getEntityManager().hasComponent<RunnableComponent>(entity))
157 {
158 game_.getEntityManager().deleteEntity(entity);
159 }
160}
161
162template <typename... Args>
163EntityManager::id_type ScriptingSystem::runScript(
164 std::string event,
165 id_type entity,
166 Args&&... args)
167{
168 auto& prototypable = game_.getEntityManager().
169 getComponent<PrototypableComponent>(entity);
170
171 id_type script = game_.getEntityManager().emplaceEntity();
172
173 auto& runnable = game_.getEntityManager().
174 emplaceComponent<RunnableComponent>(script);
175
176 runnable.runner =
177 std::unique_ptr<sol::thread>(
178 new sol::thread(
179 sol::thread::create(
180 engine.lua_state())));
181
182 runnable.callable =
183 std::unique_ptr<sol::coroutine>(
184 new sol::coroutine(
185 runnable.runner->state().
186 traverse_get<sol::function>(
187 prototypable.prototypeId,
188 event)));
189
190 if (!*runnable.callable)
191 {
192 throw std::runtime_error("Error running script");
193 }
194
195 auto result = (*runnable.callable)(
196 script_entity(entity),
197 std::forward<Args>(args)...);
198
199 if (!result.valid())
200 {
201 sol::error e = result;
202 throw std::runtime_error(e.what());
203 }
204
205 return script;
206}
207
208EntityManager::id_type ScriptingSystem::runBehaviorScript(id_type entity)
209{
210 return runScript("Behavior", entity);
211}
212
213void ScriptingSystem::onTouch(id_type entity, id_type player)
214{
215 runScript(
216 "OnTouch",
217 entity,
218 script_entity(player));
219}