summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-05-10 19:27:59 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-05-17 15:39:39 -0400
commit4bbfeae42a1245b1b84e8847787d7643e6a6f2cf (patch)
tree8dd65d9ab0cfffd0e79f670c94b035c5eebfa934 /src
parent67b24a8ddd89371cfb944c5b441c852f0edc23b1 (diff)
downloadtherapy-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.h81
-rw-r--r--src/components/realizable.h3
-rw-r--r--src/systems/automating.cpp99
-rw-r--r--src/systems/automating.h3
-rw-r--r--src/systems/realizing.cpp153
-rw-r--r--src/vector.h10
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
9class AutomatableComponent : public Component { 8class AutomatableComponent : public Component {
10public: 9public:
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
9struct 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
7void AutomatingSystem::tick(double dt) 19void 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
52void 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
6class AutomatingSystem : public System { 7class AutomatingSystem : public System {
7public: 8public:
@@ -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
32void 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.
120EntityManager::id_type RealizingSystem::initSingleton( 34EntityManager::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;