summary refs log tree commit diff stats
path: root/src/systems/realizing.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-04-28 09:22:44 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2018-04-28 09:22:44 -0400
commit8016a7146fec3f6f43ca05723441750e5aae3d4d (patch)
tree0d527c1af80cf9ac34a027f9ee6f1acbb95db9f4 /src/systems/realizing.cpp
parentf782b81ba10c9b7a1e221b16de0aaa7b6c521729 (diff)
downloadtherapy-8016a7146fec3f6f43ca05723441750e5aae3d4d.tar.gz
therapy-8016a7146fec3f6f43ca05723441750e5aae3d4d.tar.bz2
therapy-8016a7146fec3f6f43ca05723441750e5aae3d4d.zip
Restructured the way the world is loaded
The World class was removed and replaced by the RealizingSystem and RealizableComponent. The realizable entity is intended to be a singleton and to represent the world. The Map class was also removed and integrated into the MappableComponent.

These changes are to facilitate implementation of map objects without needing special intermediary objects (including the Map class). Now, map entities are created as soon as the world is created, and map object entities will be as well. They will simply be deactivated while the map is not active. Multiple players are now slightly better supported, which will be important in the future.

This will likely become inefficient as the world becomes bigger, and some sort of sector-loading process will have to be designed. This also reduces the usefulness of EntityManager's entity-searching capabilities (which are not the most efficiently implemented currently anyway), and will likely in the future require some added functionality to better search subsets of entities.

A lot of the components were also rewritten to use bare member variables instead of accessor methods, as they never had special functionality and just took up space. These components were also documented.
Diffstat (limited to 'src/systems/realizing.cpp')
-rw-r--r--src/systems/realizing.cpp321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp new file mode 100644 index 0000000..09c38f3 --- /dev/null +++ b/src/systems/realizing.cpp
@@ -0,0 +1,321 @@
1#include "realizing.h"
2#include <stdexcept>
3#include <libxml/parser.h>
4#include <cstring>
5#include "game.h"
6#include "consts.h"
7#include "components/realizable.h"
8#include "components/mappable.h"
9#include "components/animatable.h"
10#include "components/playable.h"
11#include "components/ponderable.h"
12#include "components/transformable.h"
13#include "systems/mapping.h"
14#include "systems/animating.h"
15#include "systems/pondering.h"
16
17inline xmlChar* getProp(xmlNodePtr node, const char* attr)
18{
19 xmlChar* key = xmlGetProp(node, reinterpret_cast<const xmlChar*>(attr));
20 if (key == nullptr)
21 {
22 throw std::invalid_argument("Error parsing world file");
23 }
24
25 return key;
26}
27
28// TODO: neither the XML doc nor any of the emplaced entities are properly
29// destroyed if this method throws an exception.
30EntityManager::id_type RealizingSystem::initSingleton(std::string filename)
31{
32 id_type world = game_.getEntityManager().emplaceEntity();
33
34 auto& realizable = game_.getEntityManager().
35 emplaceComponent<RealizableComponent>(world);
36
37 auto& mapping = game_.getSystemManager().getSystem<MappingSystem>();
38
39 xmlDocPtr doc = xmlParseFile(filename.c_str());
40 if (doc == nullptr)
41 {
42 throw std::invalid_argument("Cannot find world file");
43 }
44
45 xmlNodePtr top = xmlDocGetRootElement(doc);
46 if (top == nullptr)
47 {
48 throw std::invalid_argument("Error parsing world file");
49 }
50
51 if (xmlStrcmp(top->name, reinterpret_cast<const xmlChar*>("world")))
52 {
53 throw std::invalid_argument("Error parsing world file");
54 }
55
56 xmlChar* key = nullptr;
57
58 key = getProp(top, "startx");
59 realizable.startingX = atoi(reinterpret_cast<char*>(key));
60 xmlFree(key);
61
62 key = getProp(top, "starty");
63 realizable.startingY = atoi(reinterpret_cast<char*>(key));
64 xmlFree(key);
65
66 key = getProp(top, "startmap");
67 realizable.startingMapId = atoi(reinterpret_cast<char*>(key));
68 xmlFree(key);
69
70 for (xmlNodePtr node = top->xmlChildrenNode;
71 node != nullptr;
72 node = node->next)
73 {
74 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("map")))
75 {
76 id_type map = game_.getEntityManager().emplaceEntity();
77
78 auto& mappable = game_.getEntityManager().
79 emplaceComponent<MappableComponent>(map,
80 Texture("res/tiles.png"),
81 Texture("res/font.bmp"));
82
83 key = getProp(node, "id");
84 mappable.mapId = atoi(reinterpret_cast<char*>(key));
85 xmlFree(key);
86
87 key = getProp(node, "title");
88 mappable.title = reinterpret_cast<char*>(key);
89 xmlFree(key);
90
91 for (xmlNodePtr mapNode = node->xmlChildrenNode;
92 mapNode != nullptr;
93 mapNode = mapNode->next)
94 {
95 if (!xmlStrcmp(
96 mapNode->name,
97 reinterpret_cast<const xmlChar*>("environment")))
98 {
99 key = xmlNodeGetContent(mapNode);
100 if (key == nullptr)
101 {
102 throw std::invalid_argument("Error parsing world file");
103 }
104
105 mappable.tiles.clear();
106 mappable.tiles.push_back(atoi(strtok(
107 reinterpret_cast<char*>(key),
108 ",\n")));
109
110 for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++)
111 {
112 mappable.tiles.push_back(atoi(strtok(nullptr, ",\n")));
113 }
114
115 xmlFree(key);
116 } else if (!xmlStrcmp(
117 mapNode->name,
118 reinterpret_cast<const xmlChar*>("adjacent")))
119 {
120 key = getProp(mapNode, "type");
121 std::string adjTypeStr(reinterpret_cast<char*>(key));
122 xmlFree(key);
123
124 MappableComponent::Adjacent::Type adjType;
125 if (adjTypeStr == "wall")
126 {
127 adjType = MappableComponent::Adjacent::Type::wall;
128 } else if (adjTypeStr == "wrap")
129 {
130 adjType = MappableComponent::Adjacent::Type::wrap;
131 } else if (adjTypeStr == "warp")
132 {
133 adjType = MappableComponent::Adjacent::Type::warp;
134 } else if (adjTypeStr == "reverseWarp")
135 {
136 adjType = MappableComponent::Adjacent::Type::reverse;
137 } else {
138 throw std::logic_error("Invalid adjacency type");
139 }
140
141 key = getProp(mapNode, "map");
142 size_t adjMapId = atoi(reinterpret_cast<char*>(key));
143 xmlFree(key);
144
145 key = getProp(mapNode, "dir");
146 std::string adjDir(reinterpret_cast<char*>(key));
147 xmlFree(key);
148
149 if (adjDir == "left")
150 {
151 mappable.leftAdjacent = {adjType, adjMapId};
152 } else if (adjDir == "right")
153 {
154 mappable.rightAdjacent = {adjType, adjMapId};
155 } else if (adjDir == "up")
156 {
157 mappable.upAdjacent = {adjType, adjMapId};
158 } else if (adjDir == "down")
159 {
160 mappable.downAdjacent = {adjType, adjMapId};
161 } else {
162 throw std::logic_error("Invalid adjacency direction");
163 }
164 }
165 }
166
167 mapping.generateBoundaries(map);
168
169 realizable.maps.insert(map);
170 realizable.entityByMapId[mappable.mapId] = map;
171 }
172 }
173
174 xmlFreeDoc(doc);
175
176 loadMap(realizable.entityByMapId[realizable.startingMapId]);
177
178 return world;
179}
180
181EntityManager::id_type RealizingSystem::getSingleton() const
182{
183 std::set<id_type> result =
184 game_.getEntityManager().getEntitiesWithComponents<
185 RealizableComponent>();
186
187 if (result.empty())
188 {
189 throw std::logic_error("No realizable entity found");
190 } else if (result.size() > 1)
191 {
192 throw std::logic_error("Multiple realizable entities found");
193 }
194
195 return *std::begin(result);
196}
197
198void RealizingSystem::loadMap(id_type mapEntity)
199{
200 id_type world = getSingleton();
201
202 auto& realizable = game_.getEntityManager().
203 getComponent<RealizableComponent>(world);
204
205 auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>();
206 auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>();
207
208 std::set<id_type> players =
209 game_.getEntityManager().getEntitiesWithComponents<
210 PlayableComponent>();
211
212 if (realizable.hasActiveMap)
213 {
214 id_type oldMap = realizable.activeMap;
215
216 auto& oldMappable = game_.getEntityManager().
217 getComponent<MappableComponent>(oldMap);
218
219 // Deactivate any map objects from the old map.
220 for (id_type prototype : oldMappable.objects)
221 {
222 leaveActiveMap(prototype);
223 }
224
225 // Deactivate players that were on the old map.
226 for (id_type player : players)
227 {
228 auto& playable = game_.getEntityManager().
229 getComponent<PlayableComponent>(player);
230
231 if (playable.mapId == oldMap)
232 {
233 leaveActiveMap(player);
234 }
235 }
236 }
237
238 realizable.hasActiveMap = true;
239 realizable.activeMap = mapEntity;
240
241 auto& mappable = game_.getEntityManager().
242 getComponent<MappableComponent>(mapEntity);
243
244 // Initialize the new map's objects.
245 for (id_type prototype : mappable.objects)
246 {
247 if (game_.getEntityManager().
248 hasComponent<TransformableComponent>(prototype))
249 {
250 auto& transformable = game_.getEntityManager().
251 getComponent<TransformableComponent>(prototype);
252
253 transformable.x = transformable.origX;
254 transformable.y = transformable.origY;
255 transformable.w = transformable.origW;
256 transformable.h = transformable.origH;
257 }
258
259 if (game_.getEntityManager().hasComponent<AnimatableComponent>(prototype))
260 {
261 animating.initPrototype(prototype);
262 }
263
264 if (game_.getEntityManager().hasComponent<PonderableComponent>(prototype))
265 {
266 pondering.initPrototype(prototype);
267 }
268
269 enterActiveMap(prototype);
270 }
271
272 // Activate any players on the map.
273 for (id_type player : players)
274 {
275 auto& playable = game_.getEntityManager().
276 getComponent<PlayableComponent>(player);
277
278 if (playable.mapId == mapEntity)
279 {
280 enterActiveMap(player);
281 }
282 }
283}
284
285void RealizingSystem::enterActiveMap(id_type entity)
286{
287 if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity))
288 {
289 auto& animatable = game_.getEntityManager().
290 getComponent<AnimatableComponent>(entity);
291
292 animatable.active = true;
293 }
294
295 if (game_.getEntityManager().hasComponent<PonderableComponent>(entity))
296 {
297 auto& ponderable = game_.getEntityManager().
298 getComponent<PonderableComponent>(entity);
299
300 ponderable.active = true;
301 }
302}
303
304void RealizingSystem::leaveActiveMap(id_type entity)
305{
306 if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity))
307 {
308 auto& animatable = game_.getEntityManager().
309 getComponent<AnimatableComponent>(entity);
310
311 animatable.active = false;
312 }
313
314 if (game_.getEntityManager().hasComponent<PonderableComponent>(entity))
315 {
316 auto& ponderable = game_.getEntityManager().
317 getComponent<PonderableComponent>(entity);
318
319 ponderable.active = false;
320 }
321}