summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/components/automatable.h96
-rw-r--r--src/game.cpp4
-rw-r--r--src/game.h9
-rw-r--r--src/main.cpp6
-rw-r--r--src/systems/automating.cpp71
-rw-r--r--src/systems/automating.h19
-rw-r--r--src/systems/realizing.cpp174
8 files changed, 377 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index cd652e2..04ca668 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -67,6 +67,7 @@ add_executable(Aromatherapy
67 src/systems/playing.cpp 67 src/systems/playing.cpp
68 src/systems/scheduling.cpp 68 src/systems/scheduling.cpp
69 src/systems/realizing.cpp 69 src/systems/realizing.cpp
70 src/systems/automating.cpp
70 vendor/stb_image.cpp 71 vendor/stb_image.cpp
71) 72)
72 73
diff --git a/src/components/automatable.h b/src/components/automatable.h new file mode 100644 index 0000000..b37945f --- /dev/null +++ b/src/components/automatable.h
@@ -0,0 +1,96 @@
1#ifndef AUTOMATABLE_H_3D519131
2#define AUTOMATABLE_H_3D519131
3
4#include "component.h"
5#include <vector>
6#include <random>
7
8class AutomatableComponent : public Component {
9public:
10
11 /**
12 * Helper class that defines an automatable action.
13 */
14 class Action {
15 public:
16
17 /**
18 * The horizontal and vertical speed, in pixels/sec, that the entity should
19 * move at.
20 */
21 double speedX;
22 double speedY;
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
72 /**
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
87 /**
88 * If this flag is disabled, the entity will be ignored by the automating
89 * system.
90 *
91 * @managed_by RealizingSystem
92 */
93 bool active = false;
94};
95
96#endif /* end of include guard: AUTOMATABLE_H_3D519131 */
diff --git a/src/game.cpp b/src/game.cpp index d10c52c..bf2b10b 100644 --- a/src/game.cpp +++ b/src/game.cpp
@@ -12,6 +12,7 @@
12#include "systems/playing.h" 12#include "systems/playing.h"
13#include "systems/scheduling.h" 13#include "systems/scheduling.h"
14#include "systems/realizing.h" 14#include "systems/realizing.h"
15#include "systems/automating.h"
15#include "animation.h" 16#include "animation.h"
16#include "consts.h" 17#include "consts.h"
17 18
@@ -29,12 +30,13 @@ void key_callback(GLFWwindow* window, int key, int, int action, int)
29 game.systemManager_.input(key, action); 30 game.systemManager_.input(key, action);
30} 31}
31 32
32Game::Game() 33Game::Game(std::mt19937& rng) : rng_(rng)
33{ 34{
34 systemManager_.emplaceSystem<RealizingSystem>(*this); 35 systemManager_.emplaceSystem<RealizingSystem>(*this);
35 systemManager_.emplaceSystem<PlayingSystem>(*this); 36 systemManager_.emplaceSystem<PlayingSystem>(*this);
36 systemManager_.emplaceSystem<SchedulingSystem>(*this); 37 systemManager_.emplaceSystem<SchedulingSystem>(*this);
37 systemManager_.emplaceSystem<ControllingSystem>(*this); 38 systemManager_.emplaceSystem<ControllingSystem>(*this);
39 systemManager_.emplaceSystem<AutomatingSystem>(*this);
38 systemManager_.emplaceSystem<OrientingSystem>(*this); 40 systemManager_.emplaceSystem<OrientingSystem>(*this);
39 systemManager_.emplaceSystem<PonderingSystem>(*this); 41 systemManager_.emplaceSystem<PonderingSystem>(*this);
40 systemManager_.emplaceSystem<MappingSystem>(*this); 42 systemManager_.emplaceSystem<MappingSystem>(*this);
diff --git a/src/game.h b/src/game.h index 92a67d9..dc256c6 100644 --- a/src/game.h +++ b/src/game.h
@@ -1,6 +1,7 @@
1#ifndef GAME_H_1014DDC9 1#ifndef GAME_H_1014DDC9
2#define GAME_H_1014DDC9 2#define GAME_H_1014DDC9
3 3
4#include <random>
4#include "entity_manager.h" 5#include "entity_manager.h"
5#include "system_manager.h" 6#include "system_manager.h"
6#include "renderer/renderer.h" 7#include "renderer/renderer.h"
@@ -8,10 +9,15 @@
8class Game { 9class Game {
9public: 10public:
10 11
11 Game(); 12 Game(std::mt19937& rng);
12 13
13 void execute(); 14 void execute();
14 15
16 inline std::mt19937& getRng()
17 {
18 return rng_;
19 }
20
15 inline Renderer& getRenderer() 21 inline Renderer& getRenderer()
16 { 22 {
17 return renderer_; 23 return renderer_;
@@ -36,6 +42,7 @@ public:
36 42
37private: 43private:
38 44
45 std::mt19937 rng_;
39 Renderer renderer_; 46 Renderer renderer_;
40 EntityManager entityManager_; 47 EntityManager entityManager_;
41 SystemManager systemManager_; 48 SystemManager systemManager_;
diff --git a/src/main.cpp b/src/main.cpp index ddbc15f..d59d0f9 100644 --- a/src/main.cpp +++ b/src/main.cpp
@@ -1,11 +1,15 @@
1#include <random>
1#include "muxer.h" 2#include "muxer.h"
2#include "game.h" 3#include "game.h"
3 4
4int main() 5int main()
5{ 6{
7 std::random_device randomDevice;
8 std::mt19937 rng(randomDevice());
9
6 initMuxer(); 10 initMuxer();
7 11
8 Game game; 12 Game game(rng);
9 game.execute(); 13 game.execute();
10 14
11 destroyMuxer(); 15 destroyMuxer();
diff --git a/src/systems/automating.cpp b/src/systems/automating.cpp new file mode 100644 index 0000000..0d85957 --- /dev/null +++ b/src/systems/automating.cpp
@@ -0,0 +1,71 @@
1#include "automating.h"
2#include "game.h"
3#include "components/automatable.h"
4#include "components/ponderable.h"
5#include "systems/pondering.h"
6
7void AutomatingSystem::tick(double dt)
8{
9 auto entities = game_.getEntityManager().getEntitiesWithComponents<
10 AutomatableComponent,
11 PonderableComponent>();
12
13 for (id_type entity : entities)
14 {
15 auto& automatable = game_.getEntityManager().
16 getComponent<AutomatableComponent>(entity);
17
18 if (!automatable.active)
19 {
20 continue;
21 }
22
23 if (automatable.behaviorRunning &&
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.velX = curAction.speedX;
58 ponderable.velY = curAction.speedY;
59
60 automatable.remaining -= dt;
61 }
62}
63
64void AutomatingSystem::initPrototype(id_type prototype)
65{
66 auto& automatable = game_.getEntityManager().
67 getComponent<AutomatableComponent>(prototype);
68
69 automatable.behaviorRunning = false;
70 automatable.actionRunning = false;
71}
diff --git a/src/systems/automating.h b/src/systems/automating.h new file mode 100644 index 0000000..c78b7cf --- /dev/null +++ b/src/systems/automating.h
@@ -0,0 +1,19 @@
1#ifndef AUTOMATING_H_E6E5D76E
2#define AUTOMATING_H_E6E5D76E
3
4#include "system.h"
5
6class AutomatingSystem : public System {
7public:
8
9 AutomatingSystem(Game& game) : System(game)
10 {
11 }
12
13 void tick(double dt);
14
15 void initPrototype(id_type prototype);
16
17};
18
19#endif /* end of include guard: AUTOMATING_H_E6E5D76E */
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index 3656acb..8e670ac 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp
@@ -12,9 +12,11 @@
12#include "components/playable.h" 12#include "components/playable.h"
13#include "components/ponderable.h" 13#include "components/ponderable.h"
14#include "components/transformable.h" 14#include "components/transformable.h"
15#include "components/automatable.h"
15#include "systems/mapping.h" 16#include "systems/mapping.h"
16#include "systems/animating.h" 17#include "systems/animating.h"
17#include "systems/pondering.h" 18#include "systems/pondering.h"
19#include "systems/automating.h"
18 20
19inline xmlChar* getProp(xmlNodePtr node, const char* attr) 21inline xmlChar* getProp(xmlNodePtr node, const char* attr)
20{ 22{
@@ -27,6 +29,92 @@ inline xmlChar* getProp(xmlNodePtr node, const char* attr)
27 return key; 29 return key;
28} 30}
29 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.speedX = -speed;
97 action.speedY = 0;
98 } else if (direction == "right")
99 {
100 action.speedX = speed;
101 action.speedY = 0;
102 } else if (direction == "up")
103 {
104 action.speedX = 0;
105 action.speedY = -speed;
106 } else if (direction == "down")
107 {
108 action.speedX = 0;
109 action.speedY = speed;
110 }
111
112 action.dur = length / speed;
113
114 behavior.push_back(std::move(action));
115 }
116}
117
30// TODO: neither the XML doc nor any of the emplaced entities are properly 118// TODO: neither the XML doc nor any of the emplaced entities are properly
31// destroyed if this method throws an exception. 119// destroyed if this method throws an exception.
32EntityManager::id_type RealizingSystem::initSingleton( 120EntityManager::id_type RealizingSystem::initSingleton(
@@ -211,6 +299,70 @@ EntityManager::id_type RealizingSystem::initSingleton(
211 game_.getSystemManager().getSystem<PonderingSystem>(). 299 game_.getSystemManager().getSystem<PonderingSystem>().
212 initializeBody(mapObject, PonderableComponent::Type::vacuumed); 300 initializeBody(mapObject, PonderableComponent::Type::vacuumed);
213 301
302 // Look for any object configuration.
303 std::map<std::string, int> items;
304
305 for (xmlNodePtr objectNode = mapNode->xmlChildrenNode;
306 objectNode != nullptr;
307 objectNode = objectNode->next)
308 {
309 if (!xmlStrcmp(
310 objectNode->name,
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
325 // Add any AI behaviors.
326 std::vector<double> behaviorWeights;
327
328 for (xmlNodePtr protoSubNode = prototypeNode->xmlChildrenNode;
329 protoSubNode != nullptr;
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 }
365
214 mappable.objects.push_back(mapObject); 366 mappable.objects.push_back(mapObject);
215 } else if (!xmlStrcmp( 367 } else if (!xmlStrcmp(
216 mapNode->name, 368 mapNode->name,
@@ -304,6 +456,7 @@ void RealizingSystem::loadMap(id_type mapEntity)
304 456
305 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); 457 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
306 auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>(); 458 auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>();
459 auto& automating = game_.getSystemManager().getSystem<AutomatingSystem>();
307 460
308 std::set<id_type> players = 461 std::set<id_type> players =
309 game_.getEntityManager().getEntitiesWithComponents< 462 game_.getEntityManager().getEntitiesWithComponents<
@@ -366,6 +519,11 @@ void RealizingSystem::loadMap(id_type mapEntity)
366 pondering.initPrototype(prototype); 519 pondering.initPrototype(prototype);
367 } 520 }
368 521
522 if (game_.getEntityManager().hasComponent<AutomatableComponent>(prototype))
523 {
524 automating.initPrototype(prototype);
525 }
526
369 enterActiveMap(prototype); 527 enterActiveMap(prototype);
370 } 528 }
371 529
@@ -399,6 +557,14 @@ void RealizingSystem::enterActiveMap(id_type entity)
399 557
400 ponderable.active = true; 558 ponderable.active = true;
401 } 559 }
560
561 if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity))
562 {
563 auto& automatable = game_.getEntityManager().
564 getComponent<AutomatableComponent>(entity);
565
566 automatable.active = true;
567 }
402} 568}
403 569
404void RealizingSystem::leaveActiveMap(id_type entity) 570void RealizingSystem::leaveActiveMap(id_type entity)
@@ -418,4 +584,12 @@ void RealizingSystem::leaveActiveMap(id_type entity)
418 584
419 ponderable.active = false; 585 ponderable.active = false;
420 } 586 }
587
588 if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity))
589 {
590 auto& automatable = game_.getEntityManager().
591 getComponent<AutomatableComponent>(entity);
592
593 automatable.active = false;
594 }
421} 595}