summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2015-03-19 16:15:47 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2015-03-19 16:15:47 -0400
commit4b4125e234cb727c70822e0a1fce0688c357741e (patch)
tree5b446ebe6f0454304d4202368bd18fba3f629d3d /src
parent281bdf956a646fd8c9944e9a44f867c984792216 (diff)
downloadtherapy-4b4125e234cb727c70822e0a1fce0688c357741e.tar.gz
therapy-4b4125e234cb727c70822e0a1fce0688c357741e.tar.bz2
therapy-4b4125e234cb727c70822e0a1fce0688c357741e.zip
Implemented a simple AI
Diffstat (limited to 'src')
-rw-r--r--src/components/ai.cpp142
-rw-r--r--src/components/ai.h73
-rw-r--r--src/components/map_collision.cpp15
-rw-r--r--src/components/physics_body.cpp8
-rw-r--r--src/components/player_physics.cpp8
-rw-r--r--src/entity.h5
-rw-r--r--src/entityfactory.cpp229
-rw-r--r--src/entityfactory.h3
-rw-r--r--src/map.cpp2
-rw-r--r--src/map.h1
-rw-r--r--src/world.cpp18
11 files changed, 412 insertions, 92 deletions
diff --git a/src/components/ai.cpp b/src/components/ai.cpp new file mode 100644 index 0000000..9f8c764 --- /dev/null +++ b/src/components/ai.cpp
@@ -0,0 +1,142 @@
1#include "ai.h"
2#include <cstdlib>
3#include "entity.h"
4
5void AIActionContainer::addAction(std::shared_ptr<AIAction> action)
6{
7 actions.push_back(action);
8}
9
10void AIActionContainer::start(Game& game, Entity& entity)
11{
12 currentAction = begin(actions);
13
14 if (currentAction != end(actions))
15 {
16 (*currentAction)->start(game, entity);
17 }
18}
19
20void AIActionContainer::perform(Game& game, Entity& entity, double dt)
21{
22 if (!isDone())
23 {
24 (*currentAction)->perform(game, entity, dt);
25
26 if ((*currentAction)->isDone())
27 {
28 currentAction++;
29
30 if (!isDone())
31 {
32 (*currentAction)->start(game, entity);
33 }
34 }
35 }
36}
37
38bool AIActionContainer::isDone() const
39{
40 return currentAction == end(actions);
41}
42
43AI::AI(int chance)
44{
45 this->chance = chance;
46}
47
48int AI::getChance() const
49{
50 return chance;
51}
52
53AI& AIComponent::emplaceAI(int chance)
54{
55 maxChance += chance;
56 ais.emplace_back(chance);
57
58 return ais.back();
59}
60
61void AIComponent::tick(Game& game, Entity& entity, double dt)
62{
63 if (currentAI == nullptr)
64 {
65 int toChoose = rand() % maxChance;
66 for (auto& ai : ais)
67 {
68 if (toChoose < ai.getChance())
69 {
70 currentAI = &ai;
71 break;
72 } else {
73 toChoose -= ai.getChance();
74 }
75 }
76
77 if (currentAI != nullptr)
78 {
79 currentAI->start(game, entity);
80 }
81 }
82
83 if (currentAI != nullptr)
84 {
85 currentAI->perform(game, entity, dt);
86
87 if (currentAI->isDone())
88 {
89 currentAI = nullptr;
90 }
91 }
92}
93
94MoveAIAction::MoveAIAction(Direction dir, int len, int speed)
95{
96 this->dir = dir;
97 this->len = len;
98 this->speed = speed;
99}
100
101void MoveAIAction::start(Game& game, Entity& entity)
102{
103 remaining = len;
104}
105
106void MoveAIAction::perform(Game&, Entity& entity, double dt)
107{
108 double dist = dt * speed;
109 remaining -= dist;
110
111 switch (dir)
112 {
113 case Direction::Left:
114 {
115 entity.position.first -= dist;
116 break;
117 }
118
119 case Direction::Right:
120 {
121 entity.position.first += dist;
122 break;
123 }
124
125 case Direction::Up:
126 {
127 entity.position.second -= dist;
128 break;
129 }
130
131 case Direction::Down:
132 {
133 entity.position.second += dist;
134 break;
135 }
136 }
137}
138
139bool MoveAIAction::isDone() const
140{
141 return remaining <= 0.0;
142}
diff --git a/src/components/ai.h b/src/components/ai.h new file mode 100644 index 0000000..840283b --- /dev/null +++ b/src/components/ai.h
@@ -0,0 +1,73 @@
1#ifndef AI_H
2#define AI_H
3
4#include <list>
5#include <map>
6#include <string>
7#include <memory>
8
9#include "entity.h"
10
11class AIAction {
12 public:
13 virtual void start(Game& game, Entity& entity) = 0;
14 virtual void perform(Game& game, Entity& entity, double dt) = 0;
15 virtual bool isDone() const = 0;
16};
17
18class AIActionContainer {
19 public:
20 void addAction(std::shared_ptr<AIAction> action);
21 virtual void start(Game& game, Entity& entity);
22 virtual void perform(Game& game, Entity& entity, double dt);
23 virtual bool isDone() const;
24
25 private:
26 std::list<std::shared_ptr<AIAction>> actions;
27 std::list<std::shared_ptr<AIAction>>::iterator currentAction {end(actions)};
28};
29
30class AI : public AIActionContainer {
31 public:
32 AI(int chance);
33
34 int getChance() const;
35
36 private:
37 int chance;
38};
39
40class AIComponent : public Component {
41 public:
42 AI& emplaceAI(int chance);
43 void tick(Game& game, Entity& entity, double dt);
44
45 private:
46 int maxChance = 0;
47 std::list<AI> ais;
48 AI* currentAI = nullptr;
49};
50
51class MoveAIAction : public AIAction {
52 public:
53 enum class Direction {
54 Left,
55 Right,
56 Up,
57 Down
58 };
59
60 MoveAIAction(Direction dir, int len, int speed);
61
62 void start(Game& game, Entity& entity);
63 void perform(Game& game, Entity& entity, double dt);
64 bool isDone() const;
65
66 private:
67 Direction dir;
68 int len;
69 int speed;
70 double remaining;
71};
72
73#endif /* end of include guard: AI_H */
diff --git a/src/components/map_collision.cpp b/src/components/map_collision.cpp index 83ad33d..432fea6 100644 --- a/src/components/map_collision.cpp +++ b/src/components/map_collision.cpp
@@ -156,15 +156,24 @@ void MapCollisionComponent::processCollision(Game& game, Entity& collider, Colli
156 if (dir == Direction::left) 156 if (dir == Direction::left)
157 { 157 {
158 collider.position.first = collision.axis; 158 collider.position.first = collision.axis;
159 collider.send(game, Message::Type::stopMovingHorizontally); 159
160 Message msg(Message::Type::setHorizontalVelocity);
161 msg.velocity = 0.0;
162 collider.send(game, msg);
160 } else if (dir == Direction::right) 163 } else if (dir == Direction::right)
161 { 164 {
162 collider.position.first = collision.axis - collider.size.first; 165 collider.position.first = collision.axis - collider.size.first;
163 collider.send(game, Message::Type::stopMovingHorizontally); 166
167 Message msg(Message::Type::setHorizontalVelocity);
168 msg.velocity = 0.0;
169 collider.send(game, msg);
164 } else if (dir == Direction::up) 170 } else if (dir == Direction::up)
165 { 171 {
166 collider.position.second = collision.axis; 172 collider.position.second = collision.axis;
167 collider.send(game, Message::Type::stopMovingVertically); 173
174 Message msg(Message::Type::setVerticalVelocity);
175 msg.velocity = 0.0;
176 collider.send(game, msg);
168 } else if (dir == Direction::down) 177 } else if (dir == Direction::down)
169 { 178 {
170 collider.position.second = collision.axis - collider.size.second; 179 collider.position.second = collision.axis - collider.size.second;
diff --git a/src/components/physics_body.cpp b/src/components/physics_body.cpp index acbdc5d..97394d1 100644 --- a/src/components/physics_body.cpp +++ b/src/components/physics_body.cpp
@@ -13,12 +13,12 @@ void PhysicsBodyComponent::receive(Game&, Entity&, const Message& msg)
13 } else if (msg.type == Message::Type::stopWalking) 13 } else if (msg.type == Message::Type::stopWalking)
14 { 14 {
15 velocity.first = 0.0; 15 velocity.first = 0.0;
16 } else if (msg.type == Message::Type::stopMovingHorizontally) 16 } else if (msg.type == Message::Type::setHorizontalVelocity)
17 { 17 {
18 velocity.first = 0.0; 18 velocity.first = msg.velocity;
19 } else if (msg.type == Message::Type::stopMovingVertically) 19 } else if (msg.type == Message::Type::setVerticalVelocity)
20 { 20 {
21 velocity.second = 0.0; 21 velocity.second = msg.velocity;
22 } 22 }
23} 23}
24 24
diff --git a/src/components/player_physics.cpp b/src/components/player_physics.cpp index 1d14f35..40e9948 100644 --- a/src/components/player_physics.cpp +++ b/src/components/player_physics.cpp
@@ -29,12 +29,12 @@ void PlayerPhysicsComponent::receive(Game&, Entity& entity, const Message& msg)
29 { 29 {
30 velocity.first = 0.0; 30 velocity.first = 0.0;
31 direction = 0; 31 direction = 0;
32 } else if (msg.type == Message::Type::stopMovingHorizontally) 32 } else if (msg.type == Message::Type::setHorizontalVelocity)
33 { 33 {
34 velocity.first = 0.0; 34 velocity.first = msg.velocity;
35 } else if (msg.type == Message::Type::stopMovingVertically) 35 } else if (msg.type == Message::Type::setVerticalVelocity)
36 { 36 {
37 velocity.second = 0.0; 37 velocity.second = msg.velocity;
38 } else if (msg.type == Message::Type::hitTheGround) 38 } else if (msg.type == Message::Type::hitTheGround)
39 { 39 {
40 if (isFalling) 40 if (isFalling)
diff --git a/src/entity.h b/src/entity.h index d09dbe5..7f09f2d 100644 --- a/src/entity.h +++ b/src/entity.h
@@ -15,8 +15,8 @@ class Message {
15 walkLeft, 15 walkLeft,
16 walkRight, 16 walkRight,
17 stopWalking, 17 stopWalking,
18 stopMovingHorizontally, 18 setHorizontalVelocity,
19 stopMovingVertically, 19 setVerticalVelocity,
20 collision, 20 collision,
21 jump, 21 jump,
22 stopJump, 22 stopJump,
@@ -33,6 +33,7 @@ class Message {
33 Type type; 33 Type type;
34 Entity* collisionEntity; 34 Entity* collisionEntity;
35 int dropAxis; 35 int dropAxis;
36 double velocity;
36}; 37};
37 38
38class Entity { 39class Entity {
diff --git a/src/entityfactory.cpp b/src/entityfactory.cpp index 6fb86ca..b80fe99 100644 --- a/src/entityfactory.cpp +++ b/src/entityfactory.cpp
@@ -3,103 +3,180 @@
3#include "muxer.h" 3#include "muxer.h"
4#include <cstdio> 4#include <cstdio>
5#include <map> 5#include <map>
6#include <list>
6#include "components/static_image.h" 7#include "components/static_image.h"
7#include "components/simple_collider.h" 8#include "components/simple_collider.h"
8#include "components/physics_body.h" 9#include "components/physics_body.h"
10#include "components/ai.h"
9#include "game.h" 11#include "game.h"
10 12
11struct EntityData { 13void parseEntityAIData(AI& ai, xmlNodePtr node, const std::map<std::string, int>& items)
12 char* sprite;
13 char* action;
14 bool hasPhysics;
15 int width;
16 int height;
17};
18
19static std::map<std::string, EntityData> factories;
20
21std::shared_ptr<Entity> EntityFactory::createNamedEntity(const std::string name)
22{ 14{
23 auto it = factories.find(name); 15 xmlChar* key;
24 EntityData data = factories[name];
25 if (it == factories.end())
26 {
27 xmlDocPtr doc = xmlParseFile(("entities/" + name + ".xml").c_str());
28 if (doc == nullptr)
29 {
30 fprintf(stderr, "Error reading entity %s\n", name.c_str());
31 exit(-1);
32 }
33
34 xmlNodePtr top = xmlDocGetRootElement(doc);
35 if (top == nullptr)
36 {
37 fprintf(stderr, "Empty entity %s\n", name.c_str());
38 exit(-1);
39 }
40
41 if (xmlStrcmp(top->name, (const xmlChar*) "entity-def"))
42 {
43 fprintf(stderr, "Invalid entity definition %s\n", name.c_str());
44 exit(-1);
45 }
46 16
47 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next) 17 for (xmlNodePtr aiNode = node->xmlChildrenNode; aiNode != NULL; aiNode = aiNode->next)
18 {
19 if (!xmlStrcmp(aiNode->name, (xmlChar*) "move"))
48 { 20 {
49 if (!xmlStrcmp(node->name, (const xmlChar*) "sprite")) 21 MoveAIAction::Direction dir;
22 int len;
23 int speed;
24
25 key = xmlGetProp(aiNode, (xmlChar*) "direction");
26 if (key == 0) exit(2);
27 if (!xmlStrcmp(key, (xmlChar*) "left"))
50 { 28 {
51 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); 29 dir = MoveAIAction::Direction::Left;
52 data.sprite = (char*) calloc(xmlStrlen(key)+1, sizeof(char)); 30 } else if (!xmlStrcmp(key, (xmlChar*) "right"))
53 strcpy(data.sprite, (char*) key);
54 xmlFree(key);
55 } else if (!xmlStrcmp(node->name, (const xmlChar*) "action"))
56 { 31 {
57 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); 32 dir = MoveAIAction::Direction::Right;
58 data.action = (char*) calloc(xmlStrlen(key)+1, sizeof(char)); 33 } else if (!xmlStrcmp(key, (xmlChar*) "up"))
59 strcpy(data.action, (char*) key);
60 xmlFree(key);
61 } else if (!xmlStrcmp(node->name, (const xmlChar*) "size"))
62 { 34 {
63 xmlChar* key = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); 35 dir = MoveAIAction::Direction::Up;
64 data.hasPhysics = true; 36 } else if (!xmlStrcmp(key, (xmlChar*) "down"))
65 sscanf((char*) key, "%d,%d", &data.width, &data.height); 37 {
66 xmlFree(key); 38 dir = MoveAIAction::Direction::Down;
39 } else {
40 exit(2);
41 }
42 xmlFree(key);
43
44 key = xmlGetProp(aiNode, (xmlChar*) "length");
45 if (key != 0)
46 {
47 len = atoi((char*) key);
48 } else {
49 key = xmlGetProp(aiNode, (xmlChar*) "length-var");
50 if (key == 0) exit(2);
51 std::string varName = (char*) key;
52 len = items.at(varName);
53 }
54 xmlFree(key);
55
56 key = xmlGetProp(aiNode, (xmlChar*) "speed");
57 if (key != 0)
58 {
59 speed = atoi((char*) key);
60 } else {
61 key = xmlGetProp(aiNode, (xmlChar*) "speed-var");
62 if (key == 0) exit(2);
63 std::string varName = (char*) key;
64 speed = items.at(varName);
65 }
66 xmlFree(key);
67
68 ai.addAction(std::make_shared<MoveAIAction>(dir, len, speed));
69 } else if (!xmlStrcmp(aiNode->name, (xmlChar*) "switch"))
70 {
71 key = xmlGetProp(aiNode, (xmlChar*) "item");
72 if (key == 0) exit(2);
73 std::string switchItem = (char*) key;
74 xmlFree(key);
75
76 for (xmlNodePtr switchNode = aiNode->xmlChildrenNode; switchNode != NULL; switchNode = switchNode->next)
77 {
78 if (!xmlStrcmp(switchNode->name, (xmlChar*) "case"))
79 {
80 key = xmlGetProp(switchNode, (xmlChar*) "value");
81 if (key == 0) exit(2);
82 int caseValue = atoi((char*) key);
83 xmlFree(key);
84
85 if (items.at(switchItem) == caseValue)
86 {
87 parseEntityAIData(ai, switchNode, items);
88 }
89 }
67 } 90 }
68 } 91 }
69
70 xmlFreeDoc(doc);
71
72 factories[name] = data;
73 } 92 }
74 93}
75 auto entity = std::make_shared<Entity>(); 94
76 95std::shared_ptr<Entity> EntityFactory::createNamedEntity(const std::string name, const std::map<std::string, int>& items)
77 if (data.sprite) 96{
97 xmlDocPtr doc = xmlParseFile("res/entities.xml");
98 if (doc == nullptr)
78 { 99 {
79 auto component = std::make_shared<StaticImageComponent>(data.sprite); 100 fprintf(stderr, "Error reading entities\n");
80 entity->addComponent(component); 101 exit(-1);
81 } 102 }
82 103
83 if (data.action) 104 xmlNodePtr top = xmlDocGetRootElement(doc);
105 if (top == nullptr)
84 { 106 {
85 if (!strcmp(data.action, "save")) 107 fprintf(stderr, "Empty entities file\n");
86 { 108 exit(-1);
87 auto component = std::make_shared<SimpleColliderComponent>([&] (Game& game, Entity&) { 109 }
88 playSound("res/Pickup_Coin23.wav", 0.25); 110
89 111 if (xmlStrcmp(top->name, (const xmlChar*) "entities"))
90 game.saveGame(); 112 {
91 }); 113 fprintf(stderr, "Invalid entities definition\n");
92 entity->addComponent(component); 114 exit(-1);
93 }
94 } 115 }
95 116
96 if (data.hasPhysics) 117 auto entity = std::make_shared<Entity>();
118
119 xmlChar* key;
120 for (xmlNodePtr node = top->xmlChildrenNode; node != NULL; node = node->next)
97 { 121 {
98 auto component = std::make_shared<PhysicsBodyComponent>(); 122 if (!xmlStrcmp(node->name, (xmlChar*) "entity"))
99 entity->addComponent(component); 123 {
100 124 key = xmlGetProp(node, (xmlChar*) "id");
101 entity->size = std::make_pair(data.width, data.height); 125 if (key == 0) exit(-1);
126 std::string entityID = (char*) key;
127 xmlFree(key);
128
129 if (entityID == name)
130 {
131 key = xmlGetProp(node, (xmlChar*) "sprite");
132 if (key == 0) exit(-1);
133 auto spriteComponent = std::make_shared<StaticImageComponent>((char*) key);
134 entity->addComponent(spriteComponent);
135 xmlFree(key);
136
137 auto physicsComponent = std::make_shared<PhysicsBodyComponent>();
138 entity->addComponent(physicsComponent);
139
140 key = xmlGetProp(node, (xmlChar*) "width");
141 if (key == 0) exit(-1);
142 entity->size.first = atoi((char*) key);
143 xmlFree(key);
144
145 key = xmlGetProp(node, (xmlChar*) "height");
146 if (key == 0) exit(-1);
147 entity->size.second = atoi((char*) key);
148 xmlFree(key);
149
150 bool addAI = false;
151 auto aiComponent = std::make_shared<AIComponent>();
152
153 for (xmlNodePtr entityNode = node->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
154 {
155 if (!xmlStrcmp(entityNode->name, (xmlChar*) "ai"))
156 {
157 addAI = true;
158
159 xmlChar* chanceKey = xmlGetProp(entityNode, (xmlChar*) "chance");
160 if (chanceKey == 0) exit(2);
161 int chance = atoi((char*) chanceKey);
162 xmlFree(chanceKey);
163
164 AI& ai = aiComponent->emplaceAI(chance);
165 parseEntityAIData(ai, entityNode, items);
166 }
167 }
168
169 if (addAI)
170 {
171 entity->addComponent(aiComponent);
172 }
173
174 break;
175 }
176 }
102 } 177 }
178
179 xmlFreeDoc(doc);
103 180
104 return entity; 181 return entity;
105} 182}
diff --git a/src/entityfactory.h b/src/entityfactory.h index 870d6d5..56f7216 100644 --- a/src/entityfactory.h +++ b/src/entityfactory.h
@@ -2,13 +2,14 @@
2#define ENTITYFACTORY_H 2#define ENTITYFACTORY_H
3 3
4#include <string> 4#include <string>
5#include <map>
5 6
6class Entity; 7class Entity;
7class Map; 8class Map;
8 9
9class EntityFactory { 10class EntityFactory {
10 public: 11 public:
11 static std::shared_ptr<Entity> createNamedEntity(const std::string name); 12 static std::shared_ptr<Entity> createNamedEntity(const std::string name, const std::map<std::string, int>& items);
12}; 13};
13 14
14#endif 15#endif
diff --git a/src/map.cpp b/src/map.cpp index c4f31d1..e3725b2 100644 --- a/src/map.cpp +++ b/src/map.cpp
@@ -69,7 +69,7 @@ void Map::createEntities(std::list<std::shared_ptr<Entity>>& entities) const
69{ 69{
70 for (auto data : this->entities) 70 for (auto data : this->entities)
71 { 71 {
72 auto entity = EntityFactory::createNamedEntity(data.name); 72 auto entity = EntityFactory::createNamedEntity(data.name, data.items);
73 entity->position = data.position; 73 entity->position = data.position;
74 74
75 entities.push_back(entity); 75 entities.push_back(entity);
diff --git a/src/map.h b/src/map.h index 4e661ab..22333aa 100644 --- a/src/map.h +++ b/src/map.h
@@ -34,6 +34,7 @@ class Map {
34 struct EntityData { 34 struct EntityData {
35 std::string name; 35 std::string name;
36 std::pair<int, int> position; 36 std::pair<int, int> position;
37 std::map<std::string, int> items;
37 }; 38 };
38 39
39 struct Adjacent { 40 struct Adjacent {
diff --git a/src/world.cpp b/src/world.cpp index 17288fa..0c61c47 100644 --- a/src/world.cpp +++ b/src/world.cpp
@@ -57,7 +57,7 @@ World::World(const char* filename)
57 { 57 {
58 if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment")) 58 if (!xmlStrcmp(mapNode->name, (const xmlChar*) "environment"))
59 { 59 {
60 xmlChar* key = xmlNodeListGetString(doc, mapNode->xmlChildrenNode, 1); 60 xmlChar* key = xmlNodeGetContent(mapNode);
61 int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int)); 61 int* mapdata = (int*) malloc(MAP_WIDTH*MAP_HEIGHT*sizeof(int));
62 mapdata[0] = atoi(strtok((char*) key, ",\n")); 62 mapdata[0] = atoi(strtok((char*) key, ",\n"));
63 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++) 63 for (int i=1; i<(MAP_WIDTH*MAP_HEIGHT); i++)
@@ -85,6 +85,22 @@ World::World(const char* filename)
85 data.position.second = atoi((char*) yKey); 85 data.position.second = atoi((char*) yKey);
86 xmlFree(yKey); 86 xmlFree(yKey);
87 87
88 for (xmlNodePtr entityNode = mapNode->xmlChildrenNode; entityNode != NULL; entityNode = entityNode->next)
89 {
90 if (!xmlStrcmp(entityNode->name, (xmlChar*) "item"))
91 {
92 xmlChar* itemIdKey = xmlGetProp(entityNode, (const xmlChar*) "id");
93 if (itemIdKey == 0) exit(2);
94 std::string itemId = (char*) itemIdKey;
95 xmlFree(itemIdKey);
96
97 xmlChar* itemIdVal = xmlNodeGetContent(entityNode);
98 if (itemIdVal == 0) exit(2);
99 data.items[itemId] = atoi((char*) itemIdVal);
100 xmlFree(itemIdVal);
101 }
102 }
103
88 map.addEntity(data); 104 map.addEntity(data);
89 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "adjacent")) 105 } else if (!xmlStrcmp(mapNode->name, (const xmlChar*) "adjacent"))
90 { 106 {