diff options
Diffstat (limited to 'src/systems/realizing.cpp')
-rw-r--r-- | src/systems/realizing.cpp | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp new file mode 100644 index 0000000..baacf5a --- /dev/null +++ b/src/systems/realizing.cpp | |||
@@ -0,0 +1,438 @@ | |||
1 | #include "realizing.h" | ||
2 | #include <stdexcept> | ||
3 | #include <libxml/parser.h> | ||
4 | #include <cstring> | ||
5 | #include <map> | ||
6 | #include "game.h" | ||
7 | #include "consts.h" | ||
8 | #include "animation.h" | ||
9 | #include "components/mappable.h" | ||
10 | #include "components/animatable.h" | ||
11 | #include "components/playable.h" | ||
12 | #include "components/ponderable.h" | ||
13 | #include "components/transformable.h" | ||
14 | #include "components/prototypable.h" | ||
15 | #include "components/automatable.h" | ||
16 | #include "systems/mapping.h" | ||
17 | #include "systems/animating.h" | ||
18 | #include "systems/pondering.h" | ||
19 | #include "systems/scripting.h" | ||
20 | |||
21 | inline xmlChar* getProp(xmlNodePtr node, const char* attr) | ||
22 | { | ||
23 | xmlChar* key = xmlGetProp(node, reinterpret_cast<const xmlChar*>(attr)); | ||
24 | if (key == nullptr) | ||
25 | { | ||
26 | throw std::invalid_argument("Error parsing world file"); | ||
27 | } | ||
28 | |||
29 | return key; | ||
30 | } | ||
31 | |||
32 | // TODO: neither the XML doc nor any of the emplaced entities are properly | ||
33 | // destroyed if this constructor throws an exception. | ||
34 | RealizingSystem::RealizingSystem( | ||
35 | Game& game, | ||
36 | std::string worldFile, | ||
37 | std::string prototypeFile) : | ||
38 | System(game), | ||
39 | worldFile_(std::move(worldFile)), | ||
40 | prototypeFile_(std::move(prototypeFile)) | ||
41 | { | ||
42 | auto& mapping = game_.getSystemManager().getSystem<MappingSystem>(); | ||
43 | |||
44 | xmlChar* key = nullptr; | ||
45 | |||
46 | // Create a mapping between prototype names and the XML trees defining them. | ||
47 | xmlDocPtr protoXml = xmlParseFile(prototypeFile_.c_str()); | ||
48 | if (protoXml == nullptr) | ||
49 | { | ||
50 | throw std::invalid_argument("Cannot find prototypes file"); | ||
51 | } | ||
52 | |||
53 | xmlNodePtr protoTop = xmlDocGetRootElement(protoXml); | ||
54 | if (protoTop == nullptr) | ||
55 | { | ||
56 | throw std::invalid_argument("Error parsing prototypes file"); | ||
57 | } | ||
58 | |||
59 | if (xmlStrcmp(protoTop->name, reinterpret_cast<const xmlChar*>("entities"))) | ||
60 | { | ||
61 | throw std::invalid_argument("Error parsing prototypes file"); | ||
62 | } | ||
63 | |||
64 | std::map<std::string, xmlNodePtr> prototypes; | ||
65 | |||
66 | for (xmlNodePtr node = protoTop->xmlChildrenNode; | ||
67 | node != nullptr; | ||
68 | node = node->next) | ||
69 | { | ||
70 | if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("entity"))) | ||
71 | { | ||
72 | key = getProp(node, "id"); | ||
73 | std::string prototypeId = reinterpret_cast<char*>(key); | ||
74 | xmlFree(key); | ||
75 | |||
76 | prototypes[prototypeId] = node; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | // Create entities from the world definition. | ||
81 | xmlDocPtr doc = xmlParseFile(worldFile_.c_str()); | ||
82 | if (doc == nullptr) | ||
83 | { | ||
84 | throw std::invalid_argument("Cannot find world file"); | ||
85 | } | ||
86 | |||
87 | xmlNodePtr top = xmlDocGetRootElement(doc); | ||
88 | if (top == nullptr) | ||
89 | { | ||
90 | throw std::invalid_argument("Error parsing world file"); | ||
91 | } | ||
92 | |||
93 | if (xmlStrcmp(top->name, reinterpret_cast<const xmlChar*>("world"))) | ||
94 | { | ||
95 | throw std::invalid_argument("Error parsing world file"); | ||
96 | } | ||
97 | |||
98 | key = getProp(top, "startx"); | ||
99 | startingPos_.x() = atoi(reinterpret_cast<char*>(key)); | ||
100 | xmlFree(key); | ||
101 | |||
102 | key = getProp(top, "starty"); | ||
103 | startingPos_.y() = atoi(reinterpret_cast<char*>(key)); | ||
104 | xmlFree(key); | ||
105 | |||
106 | key = getProp(top, "startmap"); | ||
107 | startingMapId_ = atoi(reinterpret_cast<char*>(key)); | ||
108 | xmlFree(key); | ||
109 | |||
110 | for (xmlNodePtr node = top->xmlChildrenNode; | ||
111 | node != nullptr; | ||
112 | node = node->next) | ||
113 | { | ||
114 | if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar*>("map"))) | ||
115 | { | ||
116 | id_type map = game_.getEntityManager().emplaceEntity(); | ||
117 | |||
118 | auto& mappable = game_.getEntityManager(). | ||
119 | emplaceComponent<MappableComponent>(map, | ||
120 | Texture("res/tiles.png"), | ||
121 | Texture("res/font.bmp")); | ||
122 | |||
123 | key = getProp(node, "id"); | ||
124 | mappable.mapId = atoi(reinterpret_cast<char*>(key)); | ||
125 | xmlFree(key); | ||
126 | |||
127 | key = getProp(node, "title"); | ||
128 | mappable.title = reinterpret_cast<char*>(key); | ||
129 | xmlFree(key); | ||
130 | |||
131 | for (xmlNodePtr mapNode = node->xmlChildrenNode; | ||
132 | mapNode != nullptr; | ||
133 | mapNode = mapNode->next) | ||
134 | { | ||
135 | if (!xmlStrcmp( | ||
136 | mapNode->name, | ||
137 | reinterpret_cast<const xmlChar*>("environment"))) | ||
138 | { | ||
139 | key = xmlNodeGetContent(mapNode); | ||
140 | if (key == nullptr) | ||
141 | { | ||
142 | throw std::invalid_argument("Error parsing world file"); | ||
143 | } | ||
144 | |||
145 | mappable.tiles.clear(); | ||
146 | mappable.tiles.push_back(atoi(strtok( | ||
147 | reinterpret_cast<char*>(key), | ||
148 | ",\n"))); | ||
149 | |||
150 | for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++) | ||
151 | { | ||
152 | mappable.tiles.push_back(atoi(strtok(nullptr, ",\n"))); | ||
153 | } | ||
154 | |||
155 | xmlFree(key); | ||
156 | } else if (!xmlStrcmp( | ||
157 | mapNode->name, | ||
158 | reinterpret_cast<const xmlChar*>("entity"))) | ||
159 | { | ||
160 | id_type mapObject = game_.getEntityManager().emplaceEntity(); | ||
161 | |||
162 | key = getProp(mapNode, "type"); | ||
163 | std::string prototypeId = reinterpret_cast<char*>(key); | ||
164 | xmlFree(key); | ||
165 | |||
166 | xmlNodePtr prototypeNode = prototypes[prototypeId]; | ||
167 | |||
168 | // Set the coordinates from the object definition. | ||
169 | auto& transformable = game_.getEntityManager(). | ||
170 | emplaceComponent<TransformableComponent>(mapObject); | ||
171 | |||
172 | key = getProp(mapNode, "x"); | ||
173 | transformable.origPos.x() = atoi(reinterpret_cast<char*>(key)); | ||
174 | xmlFree(key); | ||
175 | |||
176 | key = getProp(mapNode, "y"); | ||
177 | transformable.origPos.y() = atoi(reinterpret_cast<char*>(key)); | ||
178 | xmlFree(key); | ||
179 | |||
180 | // Set the sprite and size using the prototype definition. | ||
181 | key = getProp(prototypeNode, "sprite"); | ||
182 | std::string spritePath = reinterpret_cast<char*>(key); | ||
183 | xmlFree(key); | ||
184 | |||
185 | key = getProp(prototypeNode, "width"); | ||
186 | transformable.origSize.w() = atoi(reinterpret_cast<char*>(key)); | ||
187 | xmlFree(key); | ||
188 | |||
189 | key = getProp(prototypeNode, "height"); | ||
190 | transformable.origSize.h() = atoi(reinterpret_cast<char*>(key)); | ||
191 | xmlFree(key); | ||
192 | |||
193 | AnimationSet objectAnim( | ||
194 | spritePath.c_str(), | ||
195 | transformable.origSize.w(), | ||
196 | transformable.origSize.h(), | ||
197 | 1); | ||
198 | |||
199 | objectAnim.emplaceAnimation("static", 0, 1, 1); | ||
200 | |||
201 | auto& animatable = game_.getEntityManager(). | ||
202 | emplaceComponent<AnimatableComponent>( | ||
203 | mapObject, | ||
204 | std::move(objectAnim)); | ||
205 | |||
206 | animatable.origAnimation = "static"; | ||
207 | |||
208 | // Create a physics body. | ||
209 | game_.getSystemManager().getSystem<PonderingSystem>(). | ||
210 | initializeBody(mapObject, PonderableComponent::Type::vacuumed); | ||
211 | |||
212 | |||
213 | |||
214 | |||
215 | |||
216 | auto& prototypable = game_.getEntityManager(). | ||
217 | emplaceComponent<PrototypableComponent>(mapObject); | ||
218 | |||
219 | prototypable.prototypeId = prototypeId; | ||
220 | |||
221 | key = getProp(mapNode, "index"); | ||
222 | prototypable.mapObjectIndex = atoi(reinterpret_cast<char*>(key)); | ||
223 | xmlFree(key); | ||
224 | |||
225 | if (prototypeId == "movplat") | ||
226 | { | ||
227 | auto& automatable = game_.getEntityManager(). | ||
228 | emplaceComponent<AutomatableComponent>(mapObject); | ||
229 | |||
230 | automatable.table = prototypeId; | ||
231 | } else if (prototypeId == "checkpoint") | ||
232 | { | ||
233 | auto& ponderable = game_.getEntityManager(). | ||
234 | getComponent<PonderableComponent>(mapObject); | ||
235 | |||
236 | ponderable.colliderType = PonderableComponent::Collision::event; | ||
237 | } | ||
238 | |||
239 | mappable.objects.push_back(mapObject); | ||
240 | } else if (!xmlStrcmp( | ||
241 | mapNode->name, | ||
242 | reinterpret_cast<const xmlChar*>("adjacent"))) | ||
243 | { | ||
244 | key = getProp(mapNode, "type"); | ||
245 | std::string adjTypeStr(reinterpret_cast<char*>(key)); | ||
246 | xmlFree(key); | ||
247 | |||
248 | MappableComponent::Adjacent::Type adjType; | ||
249 | if (adjTypeStr == "wall") | ||
250 | { | ||
251 | adjType = MappableComponent::Adjacent::Type::wall; | ||
252 | } else if (adjTypeStr == "wrap") | ||
253 | { | ||
254 | adjType = MappableComponent::Adjacent::Type::wrap; | ||
255 | } else if (adjTypeStr == "warp") | ||
256 | { | ||
257 | adjType = MappableComponent::Adjacent::Type::warp; | ||
258 | } else if (adjTypeStr == "reverseWarp") | ||
259 | { | ||
260 | adjType = MappableComponent::Adjacent::Type::reverse; | ||
261 | } else { | ||
262 | throw std::logic_error("Invalid adjacency type"); | ||
263 | } | ||
264 | |||
265 | key = getProp(mapNode, "map"); | ||
266 | size_t adjMapId = atoi(reinterpret_cast<char*>(key)); | ||
267 | xmlFree(key); | ||
268 | |||
269 | key = getProp(mapNode, "dir"); | ||
270 | std::string adjDir(reinterpret_cast<char*>(key)); | ||
271 | xmlFree(key); | ||
272 | |||
273 | if (adjDir == "left") | ||
274 | { | ||
275 | mappable.leftAdjacent = {adjType, adjMapId}; | ||
276 | } else if (adjDir == "right") | ||
277 | { | ||
278 | mappable.rightAdjacent = {adjType, adjMapId}; | ||
279 | } else if (adjDir == "up") | ||
280 | { | ||
281 | mappable.upAdjacent = {adjType, adjMapId}; | ||
282 | } else if (adjDir == "down") | ||
283 | { | ||
284 | mappable.downAdjacent = {adjType, adjMapId}; | ||
285 | } else { | ||
286 | throw std::logic_error("Invalid adjacency direction"); | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | mapping.generateBoundaries(map); | ||
292 | |||
293 | entityByMapId_[mappable.mapId] = map; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | xmlFreeDoc(doc); | ||
298 | xmlFreeDoc(protoXml); | ||
299 | |||
300 | activateMap(entityByMapId_[startingMapId_]); | ||
301 | } | ||
302 | |||
303 | void RealizingSystem::loadMap(id_type mapEntity) | ||
304 | { | ||
305 | deactivateMap(); | ||
306 | activateMap(mapEntity); | ||
307 | } | ||
308 | |||
309 | void RealizingSystem::deactivateMap() | ||
310 | { | ||
311 | id_type oldMap = activeMap_; | ||
312 | |||
313 | auto& oldMappable = game_.getEntityManager(). | ||
314 | getComponent<MappableComponent>(oldMap); | ||
315 | |||
316 | // Deactivate any map objects from the old map. | ||
317 | for (id_type prototype : oldMappable.objects) | ||
318 | { | ||
319 | leaveActiveMap(prototype); | ||
320 | } | ||
321 | |||
322 | // Deactivate players that were on the old map. | ||
323 | std::set<id_type> players = | ||
324 | game_.getEntityManager().getEntitiesWithComponents< | ||
325 | PlayableComponent>(); | ||
326 | |||
327 | for (id_type player : players) | ||
328 | { | ||
329 | auto& playable = game_.getEntityManager(). | ||
330 | getComponent<PlayableComponent>(player); | ||
331 | |||
332 | if (playable.mapId == oldMap) | ||
333 | { | ||
334 | leaveActiveMap(player); | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | void RealizingSystem::activateMap(id_type mapEntity) | ||
340 | { | ||
341 | auto& animating = game_.getSystemManager().getSystem<AnimatingSystem>(); | ||
342 | auto& pondering = game_.getSystemManager().getSystem<PonderingSystem>(); | ||
343 | |||
344 | std::set<id_type> players = | ||
345 | game_.getEntityManager().getEntitiesWithComponents< | ||
346 | PlayableComponent>(); | ||
347 | |||
348 | activeMap_ = mapEntity; | ||
349 | |||
350 | auto& mappable = game_.getEntityManager(). | ||
351 | getComponent<MappableComponent>(mapEntity); | ||
352 | |||
353 | // Initialize the new map's objects. | ||
354 | for (id_type prototype : mappable.objects) | ||
355 | { | ||
356 | if (game_.getEntityManager(). | ||
357 | hasComponent<TransformableComponent>(prototype)) | ||
358 | { | ||
359 | auto& transformable = game_.getEntityManager(). | ||
360 | getComponent<TransformableComponent>(prototype); | ||
361 | |||
362 | transformable.pos = transformable.origPos; | ||
363 | transformable.size = transformable.origSize; | ||
364 | } | ||
365 | |||
366 | if (game_.getEntityManager().hasComponent<AnimatableComponent>(prototype)) | ||
367 | { | ||
368 | animating.initPrototype(prototype); | ||
369 | } | ||
370 | |||
371 | if (game_.getEntityManager().hasComponent<PonderableComponent>(prototype)) | ||
372 | { | ||
373 | pondering.initPrototype(prototype); | ||
374 | } | ||
375 | |||
376 | enterActiveMap(prototype); | ||
377 | } | ||
378 | |||
379 | // Activate any players on the map. | ||
380 | for (id_type player : players) | ||
381 | { | ||
382 | auto& playable = game_.getEntityManager(). | ||
383 | getComponent<PlayableComponent>(player); | ||
384 | |||
385 | if (playable.mapId == mapEntity) | ||
386 | { | ||
387 | enterActiveMap(player); | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | void RealizingSystem::enterActiveMap(id_type entity) | ||
393 | { | ||
394 | if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity)) | ||
395 | { | ||
396 | auto& animatable = game_.getEntityManager(). | ||
397 | getComponent<AnimatableComponent>(entity); | ||
398 | |||
399 | animatable.active = true; | ||
400 | } | ||
401 | |||
402 | if (game_.getEntityManager().hasComponent<PonderableComponent>(entity)) | ||
403 | { | ||
404 | auto& ponderable = game_.getEntityManager(). | ||
405 | getComponent<PonderableComponent>(entity); | ||
406 | |||
407 | ponderable.active = true; | ||
408 | } | ||
409 | |||
410 | if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity)) | ||
411 | { | ||
412 | game_.getSystemManager().getSystem<ScriptingSystem>().startBehavior(entity); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | void RealizingSystem::leaveActiveMap(id_type entity) | ||
417 | { | ||
418 | if (game_.getEntityManager().hasComponent<AnimatableComponent>(entity)) | ||
419 | { | ||
420 | auto& animatable = game_.getEntityManager(). | ||
421 | getComponent<AnimatableComponent>(entity); | ||
422 | |||
423 | animatable.active = false; | ||
424 | } | ||
425 | |||
426 | if (game_.getEntityManager().hasComponent<PonderableComponent>(entity)) | ||
427 | { | ||
428 | auto& ponderable = game_.getEntityManager(). | ||
429 | getComponent<PonderableComponent>(entity); | ||
430 | |||
431 | ponderable.active = false; | ||
432 | } | ||
433 | |||
434 | if (game_.getEntityManager().hasComponent<AutomatableComponent>(entity)) | ||
435 | { | ||
436 | game_.getSystemManager().getSystem<ScriptingSystem>().stopBehavior(entity); | ||
437 | } | ||
438 | } | ||