diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-05-10 19:27:59 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-05-17 15:39:39 -0400 |
commit | 4bbfeae42a1245b1b84e8847787d7643e6a6f2cf (patch) | |
tree | 8dd65d9ab0cfffd0e79f670c94b035c5eebfa934 /src | |
parent | 67b24a8ddd89371cfb944c5b441c852f0edc23b1 (diff) | |
download | therapy-4bbfeae42a1245b1b84e8847787d7643e6a6f2cf.tar.gz therapy-4bbfeae42a1245b1b84e8847787d7643e6a6f2cf.tar.bz2 therapy-4bbfeae42a1245b1b84e8847787d7643e6a6f2cf.zip |
Started integrating Lua as a scripting engine
Currently moving platforms are able to have their movement controlled by a script rather than by XML, which is probably a better implementation and scales better to other things. The scripts, instead of using the components as state, use the stack as state. In this way, they pretend to be multithreaded. For instance, the moving platform calls moveRight and then moveLeft. Both of those functions internally make calls that say to wait until the next tick. When the AutomatingSystem ticks, it continues execution of all scripts (sequentially, of course) until they ask for the next tick again. This is implemented using coroutines.
Diffstat (limited to 'src')
-rw-r--r-- | src/components/automatable.h | 81 | ||||
-rw-r--r-- | src/components/realizable.h | 3 | ||||
-rw-r--r-- | src/systems/automating.cpp | 99 | ||||
-rw-r--r-- | src/systems/automating.h | 3 | ||||
-rw-r--r-- | src/systems/realizing.cpp | 153 | ||||
-rw-r--r-- | src/vector.h | 10 |
6 files changed, 82 insertions, 267 deletions
diff --git a/src/components/automatable.h b/src/components/automatable.h index c1fd1a3..d30340a 100644 --- a/src/components/automatable.h +++ b/src/components/automatable.h | |||
@@ -2,87 +2,16 @@ | |||
2 | #define AUTOMATABLE_H_3D519131 | 2 | #define AUTOMATABLE_H_3D519131 |
3 | 3 | ||
4 | #include "component.h" | 4 | #include "component.h" |
5 | #include <vector> | 5 | #include <sol.hpp> |
6 | #include <random> | 6 | #include <memory> |
7 | #include "vector.h" | ||
8 | 7 | ||
9 | class AutomatableComponent : public Component { | 8 | class AutomatableComponent : public Component { |
10 | public: | 9 | public: |
11 | 10 | ||
12 | /** | 11 | std::unique_ptr<sol::thread> runner; |
13 | * Helper class that defines an automatable action. | 12 | std::unique_ptr<sol::coroutine> behavior; |
14 | */ | ||
15 | class Action { | ||
16 | public: | ||
17 | |||
18 | /** | ||
19 | * The horizontal and vertical speed, in pixels/sec, that the entity should | ||
20 | * move at. | ||
21 | */ | ||
22 | vec2d speed; | ||
23 | |||
24 | /** | ||
25 | * The duration of the action in seconds. | ||
26 | */ | ||
27 | double dur; | ||
28 | }; | ||
29 | |||
30 | /** | ||
31 | * Helper type that defines a behavior that an entity can exhibit, which is a | ||
32 | * list of actions that are stepped through in sequence. | ||
33 | */ | ||
34 | using Behavior = std::vector<Action>; | ||
35 | |||
36 | /** | ||
37 | * A group of behaviors that the entity can exhibit, which are picked at | ||
38 | * random at the start of automation and whenever a behavior completes. | ||
39 | * | ||
40 | * @managed_by RealizingSystem | ||
41 | */ | ||
42 | std::vector<Behavior> behaviors; | ||
43 | |||
44 | /** | ||
45 | * A random distribution over the above behaviors. | ||
46 | * | ||
47 | * @managed_by RealizingSystem | ||
48 | */ | ||
49 | std::discrete_distribution<size_t> behaviorDist; | ||
50 | |||
51 | /** | ||
52 | * A flag indicating whether a behavior is currently executing. | ||
53 | * | ||
54 | * @managed_by AutomatingSystem | ||
55 | */ | ||
56 | bool behaviorRunning = false; | ||
57 | |||
58 | /** | ||
59 | * A flag indicating whether an action is currently executing. | ||
60 | * | ||
61 | * @managed_by AutomatingSystem | ||
62 | */ | ||
63 | bool actionRunning = false; | ||
64 | |||
65 | /** | ||
66 | * The index of the currently executing behavior, if there is one. | ||
67 | * | ||
68 | * @managed_by AutomatingSystem | ||
69 | */ | ||
70 | size_t currentBehavior; | ||
71 | 13 | ||
72 | /** | 14 | sol::environment origBehavior; |
73 | * The index of the currently executing action, if there is one. | ||
74 | * | ||
75 | * @managed_by AutomatingSystem | ||
76 | */ | ||
77 | size_t currentAction; | ||
78 | |||
79 | /** | ||
80 | * The amount of time remaining, in seconds, of the currently executing | ||
81 | * action. | ||
82 | * | ||
83 | * @managed_by AutomatingSystem | ||
84 | */ | ||
85 | double remaining; | ||
86 | 15 | ||
87 | /** | 16 | /** |
88 | * If this flag is disabled, the entity will be ignored by the automating | 17 | * If this flag is disabled, the entity will be ignored by the automating |
diff --git a/src/components/realizable.h b/src/components/realizable.h index b749aeb..bc834a2 100644 --- a/src/components/realizable.h +++ b/src/components/realizable.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "component.h" | 4 | #include "component.h" |
5 | #include <set> | 5 | #include <set> |
6 | #include <map> | 6 | #include <map> |
7 | #include <sol.hpp> | ||
7 | #include "entity_manager.h" | 8 | #include "entity_manager.h" |
8 | #include "vector.h" | 9 | #include "vector.h" |
9 | 10 | ||
@@ -69,6 +70,8 @@ public: | |||
69 | * The entity ID of the currently active player. | 70 | * The entity ID of the currently active player. |
70 | */ | 71 | */ |
71 | id_type activePlayer; | 72 | id_type activePlayer; |
73 | |||
74 | sol::state scriptEngine; | ||
72 | }; | 75 | }; |
73 | 76 | ||
74 | #endif /* end of include guard: REALIZABLE_H_36D8D71E */ | 77 | #endif /* end of include guard: REALIZABLE_H_36D8D71E */ |
diff --git a/src/systems/automating.cpp b/src/systems/automating.cpp index 61b97d9..6cec3bf 100644 --- a/src/systems/automating.cpp +++ b/src/systems/automating.cpp | |||
@@ -2,13 +2,24 @@ | |||
2 | #include "game.h" | 2 | #include "game.h" |
3 | #include "components/automatable.h" | 3 | #include "components/automatable.h" |
4 | #include "components/ponderable.h" | 4 | #include "components/ponderable.h" |
5 | #include "systems/pondering.h" | 5 | #include "components/realizable.h" |
6 | #include "systems/realizing.h" | ||
7 | #include "vector.h" | ||
8 | |||
9 | struct script_entity { | ||
10 | using id_type = EntityManager::id_type; | ||
11 | |||
12 | id_type id; | ||
13 | |||
14 | script_entity(id_type id) : id(id) | ||
15 | { | ||
16 | } | ||
17 | }; | ||
6 | 18 | ||
7 | void AutomatingSystem::tick(double dt) | 19 | void AutomatingSystem::tick(double dt) |
8 | { | 20 | { |
9 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | 21 | auto entities = game_.getEntityManager().getEntitiesWithComponents< |
10 | AutomatableComponent, | 22 | AutomatableComponent>(); |
11 | PonderableComponent>(); | ||
12 | 23 | ||
13 | for (id_type entity : entities) | 24 | for (id_type entity : entities) |
14 | { | 25 | { |
@@ -20,43 +31,7 @@ void AutomatingSystem::tick(double dt) | |||
20 | continue; | 31 | continue; |
21 | } | 32 | } |
22 | 33 | ||
23 | if (automatable.behaviorRunning && | 34 | (*automatable.behavior)(dt); |
24 | (automatable.remaining <= 0.0)) | ||
25 | { | ||
26 | automatable.currentAction++; | ||
27 | automatable.actionRunning = false; | ||
28 | |||
29 | if (automatable.currentAction == | ||
30 | automatable.behaviors[automatable.currentBehavior].size()) | ||
31 | { | ||
32 | automatable.behaviorRunning = false; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | if (!automatable.behaviorRunning) | ||
37 | { | ||
38 | automatable.currentBehavior = automatable.behaviorDist(game_.getRng()); | ||
39 | automatable.currentAction = 0; | ||
40 | automatable.behaviorRunning = true; | ||
41 | } | ||
42 | |||
43 | AutomatableComponent::Action& curAction = | ||
44 | automatable.behaviors | ||
45 | [automatable.currentBehavior] | ||
46 | [automatable.currentAction]; | ||
47 | |||
48 | if (!automatable.actionRunning) | ||
49 | { | ||
50 | automatable.remaining = curAction.dur; | ||
51 | automatable.actionRunning = true; | ||
52 | } | ||
53 | |||
54 | auto& ponderable = game_.getEntityManager(). | ||
55 | getComponent<PonderableComponent>(entity); | ||
56 | |||
57 | ponderable.vel = curAction.speed; | ||
58 | |||
59 | automatable.remaining -= dt; | ||
60 | } | 35 | } |
61 | } | 36 | } |
62 | 37 | ||
@@ -65,6 +40,46 @@ void AutomatingSystem::initPrototype(id_type prototype) | |||
65 | auto& automatable = game_.getEntityManager(). | 40 | auto& automatable = game_.getEntityManager(). |
66 | getComponent<AutomatableComponent>(prototype); | 41 | getComponent<AutomatableComponent>(prototype); |
67 | 42 | ||
68 | automatable.behaviorRunning = false; | 43 | auto& realizable = game_.getEntityManager(). |
69 | automatable.actionRunning = false; | 44 | getComponent<RealizableComponent>( |
45 | game_.getSystemManager().getSystem<RealizingSystem>().getSingleton()); | ||
46 | automatable.behavior.reset(); | ||
47 | automatable.runner = std::unique_ptr<sol::thread>(new sol::thread(sol::thread::create(realizable.scriptEngine.lua_state()))); | ||
48 | automatable.behavior = std::unique_ptr<sol::coroutine>(new sol::coroutine(automatable.runner->state()["run"])); | ||
49 | (*automatable.behavior)(script_entity(prototype)); | ||
50 | } | ||
51 | |||
52 | void AutomatingSystem::initScriptEngine(sol::state& scriptEngine) | ||
53 | { | ||
54 | scriptEngine.open_libraries(sol::lib::base, sol::lib::coroutine); | ||
55 | scriptEngine.new_usertype<vec2d>( | ||
56 | "vec2d", | ||
57 | sol::constructors<vec2d(), vec2d(double, double)>(), | ||
58 | "x", sol::property( | ||
59 | [] (vec2d& v) -> double { return v.x(); }, | ||
60 | [] (vec2d& v, double x) { v.x() = x; }), | ||
61 | "y", sol::property( | ||
62 | [] (vec2d& v) -> double { return v.y(); }, | ||
63 | [] (vec2d& v, double y) { v.y() = y; })); | ||
64 | |||
65 | scriptEngine.new_usertype<vec2i>( | ||
66 | "vec2i", | ||
67 | sol::constructors<vec2i(), vec2i(int, int)>(), | ||
68 | "x", [] (vec2i& v) -> int& { return v.x(); }, | ||
69 | "y", [] (vec2i& v) -> int& { return v.y(); }); | ||
70 | |||
71 | scriptEngine.new_usertype<script_entity>( | ||
72 | "entity", | ||
73 | sol::constructors<script_entity(id_type)>(), | ||
74 | "id", &script_entity::id, | ||
75 | "ponderable", | ||
76 | [&] (script_entity& entity) -> PonderableComponent& { | ||
77 | return game_.getEntityManager(). | ||
78 | getComponent<PonderableComponent>(entity.id); | ||
79 | }); | ||
80 | |||
81 | scriptEngine.new_usertype<PonderableComponent>( | ||
82 | "ponderable", | ||
83 | "vel", &PonderableComponent::vel, | ||
84 | "accel", &PonderableComponent::accel); | ||
70 | } | 85 | } |
diff --git a/src/systems/automating.h b/src/systems/automating.h index c78b7cf..117b622 100644 --- a/src/systems/automating.h +++ b/src/systems/automating.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define AUTOMATING_H_E6E5D76E | 2 | #define AUTOMATING_H_E6E5D76E |
3 | 3 | ||
4 | #include "system.h" | 4 | #include "system.h" |
5 | #include <sol.hpp> | ||
5 | 6 | ||
6 | class AutomatingSystem : public System { | 7 | class AutomatingSystem : public System { |
7 | public: | 8 | public: |
@@ -14,6 +15,8 @@ public: | |||
14 | 15 | ||
15 | void initPrototype(id_type prototype); | 16 | void initPrototype(id_type prototype); |
16 | 17 | ||
18 | void initScriptEngine(sol::state& scriptEngine); | ||
19 | |||
17 | }; | 20 | }; |
18 | 21 | ||
19 | #endif /* end of include guard: AUTOMATING_H_E6E5D76E */ | 22 | #endif /* end of include guard: AUTOMATING_H_E6E5D76E */ |
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index f9285ad..28e2279 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp | |||
@@ -29,92 +29,6 @@ inline xmlChar* getProp(xmlNodePtr node, const char* attr) | |||
29 | return key; | 29 | return key; |
30 | } | 30 | } |
31 | 31 | ||
32 | void parseAI( | ||
33 | xmlNodePtr node, | ||
34 | std::vector<AutomatableComponent::Action>& behavior, | ||
35 | const std::map<std::string, int>& items) | ||
36 | { | ||
37 | xmlChar* key = nullptr; | ||
38 | |||
39 | if (!xmlStrcmp( | ||
40 | node->name, | ||
41 | reinterpret_cast<const xmlChar*>("switch"))) | ||
42 | { | ||
43 | key = getProp(node, "item"); | ||
44 | std::string switchItem = reinterpret_cast<char*>(key); | ||
45 | xmlFree(key); | ||
46 | |||
47 | for (xmlNodePtr switchNode = node->xmlChildrenNode; | ||
48 | switchNode != nullptr; | ||
49 | switchNode = switchNode->next) | ||
50 | { | ||
51 | if (!xmlStrcmp( | ||
52 | switchNode->name, | ||
53 | reinterpret_cast<const xmlChar*>("case"))) | ||
54 | { | ||
55 | key = getProp(switchNode, "value"); | ||
56 | int caseValue = atoi(reinterpret_cast<char*>(key)); | ||
57 | xmlFree(key); | ||
58 | |||
59 | if (items.at(switchItem) == caseValue) | ||
60 | { | ||
61 | for (xmlNodePtr caseNode = switchNode->xmlChildrenNode; | ||
62 | caseNode != nullptr; | ||
63 | caseNode = caseNode->next) | ||
64 | { | ||
65 | parseAI( | ||
66 | caseNode, | ||
67 | behavior, | ||
68 | items); | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | } else if (!xmlStrcmp( | ||
74 | node->name, | ||
75 | reinterpret_cast<const xmlChar*>("move"))) | ||
76 | { | ||
77 | key = getProp(node, "direction"); | ||
78 | std::string direction = reinterpret_cast<char*>(key); | ||
79 | xmlFree(key); | ||
80 | |||
81 | key = getProp(node, "length-var"); | ||
82 | std::string lengthVar = reinterpret_cast<char*>(key); | ||
83 | xmlFree(key); | ||
84 | |||
85 | key = getProp(node, "speed-var"); | ||
86 | std::string speedVar = reinterpret_cast<char*>(key); | ||
87 | xmlFree(key); | ||
88 | |||
89 | double length = items.at(lengthVar); | ||
90 | double speed = items.at(speedVar); | ||
91 | |||
92 | AutomatableComponent::Action action; | ||
93 | |||
94 | if (direction == "left") | ||
95 | { | ||
96 | action.speed.x() = -speed; | ||
97 | action.speed.y() = 0; | ||
98 | } else if (direction == "right") | ||
99 | { | ||
100 | action.speed.x() = speed; | ||
101 | action.speed.y() = 0; | ||
102 | } else if (direction == "up") | ||
103 | { | ||
104 | action.speed.x() = 0; | ||
105 | action.speed.y() = -speed; | ||
106 | } else if (direction == "down") | ||
107 | { | ||
108 | action.speed.x() = 0; | ||
109 | action.speed.y() = speed; | ||
110 | } | ||
111 | |||
112 | action.dur = length / speed; | ||
113 | |||
114 | behavior.push_back(std::move(action)); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | // TODO: neither the XML doc nor any of the emplaced entities are properly | 32 | // TODO: neither the XML doc nor any of the emplaced entities are properly |
119 | // destroyed if this method throws an exception. | 33 | // destroyed if this method throws an exception. |
120 | EntityManager::id_type RealizingSystem::initSingleton( | 34 | EntityManager::id_type RealizingSystem::initSingleton( |
@@ -126,6 +40,9 @@ EntityManager::id_type RealizingSystem::initSingleton( | |||
126 | auto& realizable = game_.getEntityManager(). | 40 | auto& realizable = game_.getEntityManager(). |
127 | emplaceComponent<RealizableComponent>(world); | 41 | emplaceComponent<RealizableComponent>(world); |
128 | 42 | ||
43 | game_.getSystemManager().getSystem<AutomatingSystem>(). | ||
44 | initScriptEngine(realizable.scriptEngine); | ||
45 | |||
129 | realizable.worldFile = worldFile; | 46 | realizable.worldFile = worldFile; |
130 | realizable.prototypeFile = prototypeFile; | 47 | realizable.prototypeFile = prototypeFile; |
131 | 48 | ||
@@ -299,68 +216,14 @@ EntityManager::id_type RealizingSystem::initSingleton( | |||
299 | game_.getSystemManager().getSystem<PonderingSystem>(). | 216 | game_.getSystemManager().getSystem<PonderingSystem>(). |
300 | initializeBody(mapObject, PonderableComponent::Type::vacuumed); | 217 | initializeBody(mapObject, PonderableComponent::Type::vacuumed); |
301 | 218 | ||
302 | // Look for any object configuration. | 219 | if (prototypeId == "movplat") |
303 | std::map<std::string, int> items; | ||
304 | |||
305 | for (xmlNodePtr objectNode = mapNode->xmlChildrenNode; | ||
306 | objectNode != nullptr; | ||
307 | objectNode = objectNode->next) | ||
308 | { | 220 | { |
309 | if (!xmlStrcmp( | 221 | auto& automatable = game_.getEntityManager(). |
310 | objectNode->name, | 222 | emplaceComponent<AutomatableComponent>(mapObject); |
311 | reinterpret_cast<const xmlChar*>("item"))) | ||
312 | { | ||
313 | key = getProp(objectNode, "id"); | ||
314 | std::string itemName = reinterpret_cast<char*>(key); | ||
315 | xmlFree(key); | ||
316 | |||
317 | key = xmlNodeGetContent(objectNode); | ||
318 | int itemVal = atoi(reinterpret_cast<char*>(key)); | ||
319 | xmlFree(key); | ||
320 | |||
321 | items[itemName] = itemVal; | ||
322 | } | ||
323 | } | ||
324 | 223 | ||
325 | // Add any AI behaviors. | ||
326 | std::vector<double> behaviorWeights; | ||
327 | 224 | ||
328 | for (xmlNodePtr protoSubNode = prototypeNode->xmlChildrenNode; | 225 | realizable.scriptEngine.script_file( |
329 | protoSubNode != nullptr; | 226 | "res/platform.lua");//, |
330 | protoSubNode = protoSubNode->next) | ||
331 | { | ||
332 | if (!xmlStrcmp( | ||
333 | protoSubNode->name, | ||
334 | reinterpret_cast<const xmlChar*>("ai"))) | ||
335 | { | ||
336 | if (!game_.getEntityManager(). | ||
337 | hasComponent<AutomatableComponent>(mapObject)) | ||
338 | { | ||
339 | game_.getEntityManager(). | ||
340 | emplaceComponent<AutomatableComponent>(mapObject); | ||
341 | } | ||
342 | |||
343 | auto& automatable = game_.getEntityManager(). | ||
344 | getComponent<AutomatableComponent>(mapObject); | ||
345 | |||
346 | key = getProp(protoSubNode, "chance"); | ||
347 | behaviorWeights.push_back(atof(reinterpret_cast<char*>(key))); | ||
348 | xmlFree(key); | ||
349 | |||
350 | std::vector<AutomatableComponent::Action> behavior; | ||
351 | |||
352 | for (xmlNodePtr aiNode = protoSubNode->xmlChildrenNode; | ||
353 | aiNode != nullptr; | ||
354 | aiNode = aiNode->next) | ||
355 | { | ||
356 | parseAI( | ||
357 | aiNode, | ||
358 | behavior, | ||
359 | items); | ||
360 | } | ||
361 | |||
362 | automatable.behaviors.push_back(std::move(behavior)); | ||
363 | } | ||
364 | } | 227 | } |
365 | 228 | ||
366 | mappable.objects.push_back(mapObject); | 229 | mappable.objects.push_back(mapObject); |
diff --git a/src/vector.h b/src/vector.h index 3abd98a..9355dd5 100644 --- a/src/vector.h +++ b/src/vector.h | |||
@@ -7,9 +7,11 @@ public: | |||
7 | 7 | ||
8 | T coords[2]; | 8 | T coords[2]; |
9 | 9 | ||
10 | vec2() = default; | 10 | vec2() : coords{0, 0} |
11 | { | ||
12 | } | ||
11 | 13 | ||
12 | vec2(double x, double y) : coords{x, y} | 14 | vec2(T x, T y) : coords{x, y} |
13 | { | 15 | { |
14 | } | 16 | } |
15 | 17 | ||
@@ -90,12 +92,12 @@ public: | |||
90 | return vec2(-x(), -y()); | 92 | return vec2(-x(), -y()); |
91 | } | 93 | } |
92 | 94 | ||
93 | vec2 operator*(double s) const | 95 | vec2 operator*(T s) const |
94 | { | 96 | { |
95 | return vec2(x() * s, y() * s); | 97 | return vec2(x() * s, y() * s); |
96 | } | 98 | } |
97 | 99 | ||
98 | vec2& operator*=(double s) | 100 | vec2& operator*=(T s) |
99 | { | 101 | { |
100 | x() *= s; | 102 | x() *= s; |
101 | y() *= s; | 103 | y() *= s; |