wall_brush.cpp

main 831 lines · 23.5 KB Raw
Alex Alex Commit Map Editor 18/10/2026 23:40
1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Remere's Map Editor is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#include "main.h"
19
20#include "wall_brush.h"
21
22#include "gui.h"
23#include "items.h"
24#include "basemap.h"
25
26uint32_t WallBrush::full_border_types[16];
27uint32_t WallBrush::half_border_types[16];
28
29WallBrush::WallBrush() :
30 redirect_to(nullptr) {
31 ////
32}
33
34WallBrush::~WallBrush() {
35 ////
36}
37
38bool WallBrush::load(pugi::xml_node node, wxArrayString& warnings) {
39 pugi::xml_attribute attribute;
40 if ((attribute = node.attribute("lookid"))) {
41 look_id = attribute.as_ushort();
42 }
43
44 if ((attribute = node.attribute("server_lookid"))) {
45 look_id = g_items[attribute.as_ushort()].clientID;
46 }
47
48 for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
49 const std::string& childName = as_lower_str(childNode.name());
50 if (childName == "wall") {
51 const std::string& typeString = childNode.attribute("type").as_string();
52 if (typeString.empty()) {
53 warnings.push_back("Could not read type tag of wall node\n");
54 continue;
55 }
56
57 uint32_t alignment;
58 if (typeString == "vertical") {
59 alignment = WALL_VERTICAL;
60 } else if (typeString == "horizontal") {
61 alignment = WALL_HORIZONTAL;
62 } else if (typeString == "corner") {
63 alignment = WALL_NORTHWEST_DIAGONAL;
64 } else if (typeString == "pole") {
65 alignment = WALL_POLE;
66 } else if (typeString == "south end") {
67 alignment = WALL_SOUTH_END;
68 } else if (typeString == "east end") {
69 alignment = WALL_EAST_END;
70 } else if (typeString == "north end") {
71 alignment = WALL_NORTH_END;
72 } else if (typeString == "west end") {
73 alignment = WALL_WEST_END;
74 } else if (typeString == "south T") {
75 alignment = WALL_SOUTH_T;
76 } else if (typeString == "east T") {
77 alignment = WALL_EAST_T;
78 } else if (typeString == "west T") {
79 alignment = WALL_WEST_T;
80 } else if (typeString == "north T") {
81 alignment = WALL_NORTH_T;
82 } else if (typeString == "northwest diagonal") {
83 alignment = WALL_NORTHWEST_DIAGONAL;
84 } else if (typeString == "northeast diagonal") {
85 alignment = WALL_NORTHEAST_DIAGONAL;
86 } else if (typeString == "southwest diagonal") {
87 alignment = WALL_SOUTHWEST_DIAGONAL;
88 } else if (typeString == "southeast diagonal") {
89 alignment = WALL_SOUTHEAST_DIAGONAL;
90 } else if (typeString == "intersection") {
91 alignment = WALL_INTERSECTION;
92 } else if (typeString == "untouchable") {
93 alignment = WALL_UNTOUCHABLE;
94 } else {
95 warnings.push_back("Unknown wall alignment '" + wxstr(typeString) + "'\n");
96 continue;
97 }
98
99 for (pugi::xml_node subChildNode = childNode.first_child(); subChildNode; subChildNode = subChildNode.next_sibling()) {
100 const std::string& subChildName = as_lower_str(subChildNode.name());
101 if (subChildName == "item") {
102 uint16_t id = subChildNode.attribute("id").as_ushort();
103 if (id == 0) {
104 warnings.push_back("Could not read id tag of item node\n");
105 break;
106 }
107
108 ItemType& it = g_items[id];
109 if (it.id == 0) {
110 warnings.push_back("There is no itemtype with id " + std::to_string(id));
111 return false;
112 } else if (it.brush && it.brush != this) {
113 warnings.push_back("Itemtype id " + std::to_string(id) + " already has a brush");
114 return false;
115 }
116
117 it.isWall = true;
118 it.brush = this;
119 it.border_alignment = ::BorderType(alignment);
120
121 WallType wt;
122 wt.id = id;
123
124 wall_items[alignment].total_chance += subChildNode.attribute("chance").as_int();
125 wt.chance = wall_items[alignment].total_chance;
126
127 wall_items[alignment].items.push_back(wt);
128 } else if (subChildName == "door") {
129 uint16_t id = subChildNode.attribute("id").as_ushort();
130 if (id == 0) {
131 warnings.push_back("Could not read id tag of door node\n");
132 break;
133 }
134
135 const std::string& type = subChildNode.attribute("type").as_string();
136 if (type.empty()) {
137 warnings.push_back("Could not read type tag of door node\n");
138 continue;
139 }
140
141 bool isOpen;
142 pugi::xml_attribute openAttribute = subChildNode.attribute("open");
143 if (openAttribute) {
144 isOpen = openAttribute.as_bool();
145 } else {
146 isOpen = true;
147 if (type != "window" && type != "any window" && type != "hatch window") {
148 warnings.push_back("Could not read open tag of door node\n");
149 break;
150 }
151 }
152
153 // "locked" checkbox - spawn locked doors
154 bool isLocked;
155 pugi::xml_attribute lockedAttribute = subChildNode.attribute("locked");
156 if (lockedAttribute) {
157 isLocked = lockedAttribute.as_bool();
158 } else {
159 isLocked = false;
160 }
161
162 ItemType& it = g_items[id];
163 if (it.id == 0) {
164 warnings.push_back("There is no itemtype with id " + std::to_string(id));
165 return false;
166 } else if (it.brush && it.brush != this) {
167 warnings.push_back("Itemtype id " + std::to_string(id) + " already has a brush");
168 return false;
169 }
170
171 it.isWall = true;
172 it.brush = this;
173 it.isBrushDoor = true;
174 it.wall_hate_me = subChildNode.attribute("hate").as_bool();
175 it.isOpen = isOpen;
176 it.isLocked = isLocked;
177 it.border_alignment = ::BorderType(alignment);
178
179 DoorType dt;
180 dt.locked = isLocked;
181
182 bool all_windows = false;
183 bool all_doors = false;
184 if (type == "normal") {
185 dt.type = WALL_DOOR_NORMAL;
186 } else if (type == "normal_alt") {
187 dt.type = WALL_DOOR_NORMAL_ALT;
188 } else if (type == "locked") {
189 dt.type = WALL_DOOR_LOCKED;
190 } else if (type == "quest") {
191 dt.type = WALL_DOOR_QUEST;
192 } else if (type == "magic") {
193 dt.type = WALL_DOOR_MAGIC;
194 } else if (type == "archway") {
195 dt.type = WALL_ARCHWAY;
196 } else if (type == "window") {
197 dt.type = WALL_WINDOW;
198 } else if (type == "hatch_window" || type == "hatch window") {
199 dt.type = WALL_HATCH_WINDOW;
200 } else if (type == "any door") {
201 all_doors = true;
202 } else if (type == "any window") {
203 all_windows = true;
204 } else if (type == "any") {
205 all_windows = true;
206 all_doors = true;
207 } else {
208 warnings.push_back("Unknown door type '" + wxstr(type) + "'\n");
209 break;
210 }
211
212 dt.id = id;
213
214 if (all_doors) {
215 dt.type = WALL_DOOR_NORMAL;
216 door_items[alignment].push_back(dt);
217 dt.type = WALL_DOOR_NORMAL_ALT;
218 door_items[alignment].push_back(dt);
219 dt.type = WALL_DOOR_LOCKED;
220 door_items[alignment].push_back(dt);
221 dt.type = WALL_DOOR_QUEST;
222 door_items[alignment].push_back(dt);
223 dt.type = WALL_DOOR_MAGIC;
224 door_items[alignment].push_back(dt);
225 dt.type = WALL_ARCHWAY;
226 door_items[alignment].push_back(dt);
227 }
228
229 if (all_windows) {
230 dt.type = WALL_WINDOW;
231 door_items[alignment].push_back(dt);
232 dt.type = WALL_HATCH_WINDOW;
233 door_items[alignment].push_back(dt);
234 }
235
236 if (!all_doors && !all_windows) {
237 door_items[alignment].push_back(dt);
238 }
239 }
240 }
241 } else if (childName == "friend") {
242 const std::string& name = childNode.attribute("name").as_string();
243 if (name.empty()) {
244 continue;
245 }
246
247 if (name == "all") {
248 // friends.push_back(-1);
249 } else {
250 Brush* brush = g_brushes.getBrush(name);
251 if (brush) {
252 friends.push_back(brush->getID());
253 } else {
254 warnings.push_back("Brush '" + wxstr(name) + "' is not defined.");
255 continue;
256 }
257
258 if (childNode.attribute("redirect").as_bool()) {
259 if (!brush->isWall()) {
260 warnings.push_back("Wall brush redirect link: '" + wxstr(name) + "' is not a wall brush.");
261 } else if (!redirect_to) {
262 redirect_to = brush->asWall();
263 } else {
264 warnings.push_back("Wall brush '" + wxstr(getName()) + "' has more than one redirect link.");
265 }
266 }
267 }
268 }
269 }
270 return true;
271}
272
273void WallBrush::undraw(BaseMap* map, Tile* tile) {
274 tile->cleanWalls(this);
275}
276
277void WallBrush::draw(BaseMap* map, Tile* tile, void* parameter) {
278 ASSERT(tile);
279 bool b = (parameter ? *reinterpret_cast<bool*>(parameter) : false);
280 if (b) {
281 // Find a matching wall item on this tile, and shift the id
282 for (ItemVector::iterator item_iter = tile->items.begin(); item_iter != tile->items.end(); ++item_iter) {
283 Item* item = *item_iter;
284 if (item->isWall()) {
285 WallBrush* wb = item->getWallBrush();
286 if (wb == this) {
287 // Ok, shift alignment
288 BorderType alignment = item->getWallAlignment();
289 uint16_t id = 0;
290 WallBrush* try_brush = this;
291
292 // Add protection against infinite redirect loops
293 int redirect_count = 0;
294 const int max_redirects = 20; // Reasonable limit for redirect chains
295
296 while (true) {
297 if (id != 0) {
298 break;
299 }
300 if (try_brush == nullptr || redirect_count >= max_redirects) {
301 return;
302 }
303
304 for (int i = alignment + 1; i != alignment; ++i) {
305 if (i == 16) {
306 i = 0;
307 }
308 WallNode& wn = try_brush->wall_items[i];
309 if (wn.total_chance <= 0) {
310 continue;
311 }
312 int chance = random(1, wn.total_chance);
313 for (std::vector<WallType>::const_iterator it = wn.items.begin(); it != wn.items.end(); ++it) {
314 if (chance <= it->chance) {
315 id = it->id;
316 break;
317 }
318 }
319 if (id != 0) {
320 break;
321 }
322 }
323
324 try_brush = try_brush->redirect_to;
325 redirect_count++;
326 if (try_brush == this) {
327 break;
328 }
329 }
330 if (id != 0) {
331 item->setID(id);
332 }
333 return;
334 }
335 }
336 }
337 }
338
339 tile->cleanWalls(this);
340
341 // Just find a valid item and place it, the bordering algorithm will change it to the proper shape.
342 uint16_t id = 0;
343 WallBrush* try_brush = this;
344
345 // Add protection against infinite redirect loops
346 int redirect_count = 0;
347 const int max_redirects = 20; // Reasonable limit for redirect chains
348
349 while (true) {
350 if (id != 0) {
351 break;
352 }
353 if (try_brush == nullptr || redirect_count >= max_redirects) {
354 return;
355 }
356
357 for (int i = 0; i < 16; ++i) {
358 WallNode& wn = try_brush->wall_items[i];
359 if (wn.total_chance <= 0) {
360 continue;
361 }
362 int chance = random(1, wn.total_chance);
363 for (std::vector<WallType>::const_iterator it = wn.items.begin(); it != wn.items.end(); ++it) {
364 if (chance <= it->chance) {
365 id = it->id;
366 break;
367 }
368 }
369 if (id != 0) {
370 break;
371 }
372 }
373
374 try_brush = try_brush->redirect_to;
375 redirect_count++;
376 if (try_brush == this) {
377 break;
378 }
379 }
380
381 Item* new_item = Item::Create(id);
382 if (new_item && g_gui.IsCurrentActionIDEnabled()) {
383 new_item->setActionID(g_gui.GetCurrentActionID());
384 }
385 tile->addWallItem(new_item);
386}
387
388bool hasMatchingWallBrushAtTile(BaseMap* map, WallBrush* wall_brush, uint32_t x, uint32_t y, uint32_t z) {
389 Tile* t = map->getTile(x, y, z);
390 if (!t) {
391 return false;
392 }
393
394 ItemVector::const_iterator it = t->items.begin();
395 for (; it != t->items.end(); ++it) {
396 Item* item = *it;
397 if (item->isWall()) {
398 WallBrush* wb = item->getWallBrush();
399 if (wb == wall_brush) {
400 return !g_items[item->getID()].wall_hate_me;
401 } else if (wall_brush->friendOf(wb) || wb->friendOf(wall_brush)) {
402 return !g_items[item->getID()].wall_hate_me;
403 }
404 }
405 }
406
407 return false;
408}
409
410void WallBrush::doWalls(BaseMap* map, Tile* tile) {
411 ASSERT(tile);
412
413 // For quicker reference
414 unsigned int x = tile->getPosition().x;
415 unsigned int y = tile->getPosition().y;
416 unsigned int z = tile->getPosition().z;
417
418 // Advance the vector to the beginning of the walls
419 ItemVector::iterator it = tile->items.begin();
420 for (; it != tile->items.end() && (*it)->isBorder(); ++it)
421 ;
422
423 ItemVector items_to_add;
424
425 // Add iteration guard to prevent infinite loops
426 int max_iterations = tile->items.size() * 2 + 100; // Reasonable upper bound
427 int iteration_count = 0;
428
429 while (it != tile->items.end() && iteration_count < max_iterations) {
430 ++iteration_count;
431
432 Item* wall = *it;
433 if (!wall->isWall()) {
434 ++it;
435 continue;
436 }
437 WallBrush* wall_brush = wall->getWallBrush();
438 // Skip if either the wall has no brush
439 if (!wall_brush) {
440 ++it;
441 continue;
442 }
443 // or if it's a decoration brush.
444 if (wall_brush->isWallDecoration()) {
445 items_to_add.push_back(wall);
446 it = tile->items.erase(it);
447 continue;
448 }
449 bool neighbours[4];
450
451 if (x == 0) {
452 if (y == 0) {
453 neighbours[0] = false;
454 neighbours[1] = false;
455 neighbours[2] = hasMatchingWallBrushAtTile(map, wall_brush, x + 1, y, z);
456 neighbours[3] = hasMatchingWallBrushAtTile(map, wall_brush, x, y + 1, z);
457 } else {
458 neighbours[0] = hasMatchingWallBrushAtTile(map, wall_brush, x, y - 1, z);
459 neighbours[1] = false;
460 neighbours[2] = hasMatchingWallBrushAtTile(map, wall_brush, x + 1, y, z);
461 neighbours[3] = hasMatchingWallBrushAtTile(map, wall_brush, x, y + 1, z);
462 }
463 } else if (y == 0) {
464 neighbours[0] = false;
465 neighbours[1] = hasMatchingWallBrushAtTile(map, wall_brush, x - 1, y, z);
466 neighbours[2] = hasMatchingWallBrushAtTile(map, wall_brush, x + 1, y, z);
467 neighbours[3] = hasMatchingWallBrushAtTile(map, wall_brush, x, y + 1, z);
468 } else {
469 neighbours[0] = hasMatchingWallBrushAtTile(map, wall_brush, x, y - 1, z);
470 neighbours[1] = hasMatchingWallBrushAtTile(map, wall_brush, x - 1, y, z);
471 neighbours[2] = hasMatchingWallBrushAtTile(map, wall_brush, x + 1, y, z);
472 neighbours[3] = hasMatchingWallBrushAtTile(map, wall_brush, x, y + 1, z);
473 }
474
475 uint32_t tiledata = 0;
476 for (int i = 0; i < 4; i++) {
477 if (neighbours[i]) {
478 // Same wall as this one, calculate what border
479 tiledata |= 1 << i;
480 }
481 }
482
483 bool exit = false;
484 for (int i = 0; i < 2 && !exit; ++i) { // Repeat twice, but exit early if needed
485 ::BorderType bt;
486 if (i == 0) {
487 bt = ::BorderType(full_border_types[tiledata]);
488 } else {
489 bt = ::BorderType(half_border_types[tiledata]);
490 }
491
492 if (wall->getWallAlignment() == WALL_UNTOUCHABLE) {
493 items_to_add.push_back(wall);
494 it = tile->items.erase(it);
495 exit = true;
496 } else if (wall->getWallAlignment() == bt) {
497 // Do nothing, the tile already has a wall like this
498 // However, wall decorations associated with this wall might need to change...
499 items_to_add.push_back(wall);
500 it = tile->items.erase(it);
501 exit = true;
502
503 // Add guard for decoration processing loop
504 int decoration_count = 0;
505 int max_decorations = 10; // Reasonable limit for decorations per wall
506
507 while (it != tile->items.end() && decoration_count < max_decorations) {
508 // If we have a decoration ontop of us, we need to change it's alignment aswell!
509
510 Item* wall_decoration = *it;
511 ASSERT(wall_decoration);
512 WallBrush* brush = wall_decoration->getWallBrush();
513 if (brush && brush->isWallDecoration()) {
514 decoration_count++;
515
516 // We don't know if we have changed alignment
517 if (wall_decoration->getWallAlignment() == bt) {
518 // Same, no need to change...
519 items_to_add.push_back(wall_decoration);
520 it = tile->items.erase(it);
521 continue;
522 }
523 // Not the same alignment, create newd item with correct alignment
524 uint16_t id = 0;
525 WallNode& wn = brush->wall_items[int(bt)];
526 if (wn.total_chance <= 0) {
527 if (wn.items.size() == 0) {
528 ++it;
529 continue;
530 } else {
531 id = wn.items.front().id;
532 }
533 } else {
534 int chance = random(1, wn.total_chance);
535 for (std::vector<WallType>::const_iterator witer = wn.items.begin();
536 witer != wn.items.end();
537 ++witer) {
538 if (chance <= witer->chance) {
539 id = witer->id;
540 break;
541 }
542 }
543 }
544 if (id != 0) {
545 Item* new_wall = Item::Create(id);
546 if (wall_decoration->isSelected()) {
547 new_wall->select();
548 }
549 items_to_add.push_back(new_wall);
550 }
551 ++it;
552 } else {
553 break;
554 }
555 }
556 } else {
557 // Randomize a newd wall of the proper alignment
558 uint16_t id = 0;
559 WallBrush* try_brush = wall_brush;
560
561 // Add protection against infinite redirect loops
562 int redirect_count = 0;
563 const int max_redirects = 20; // Reasonable limit for redirect chains
564
565 while (true) {
566 if (try_brush == nullptr || redirect_count >= max_redirects) {
567 break;
568 }
569 if (id != 0) {
570 break;
571 }
572
573 WallNode& wn = try_brush->wall_items[int(bt)];
574 if (wn.total_chance <= 0) {
575 if (wn.items.size() == 0) {
576 try_brush = try_brush->redirect_to;
577 redirect_count++;
578 if (try_brush == wall_brush) {
579 break; // To prevent infinite loop
580 }
581 continue;
582 } else {
583 id = wn.items.front().id;
584 }
585 } else {
586 int chance = random(1, wn.total_chance);
587 for (std::vector<WallType>::const_iterator node_iter = wn.items.begin();
588 node_iter != wn.items.end();
589 ++node_iter) {
590 if (chance <= node_iter->chance) {
591 id = node_iter->id;
592 break;
593 }
594 }
595 }
596 // Propagate down the chain
597 try_brush = try_brush->redirect_to;
598 redirect_count++;
599 if (try_brush == wall_brush) {
600 break; // To prevent infinite loop
601 }
602 }
603 if ((try_brush == nullptr || redirect_count >= max_redirects) && id == 0) {
604 if (i == 1) {
605 ++it;
606 }
607 continue;
608 } else {
609 // If there is such an item, add it to the tile
610 Item* new_wall = Item::Create(id);
611 if (wall->isSelected()) {
612 new_wall->select();
613 }
614 items_to_add.push_back(new_wall);
615 exit = true;
616 ++it;
617 }
618
619 // Add guard for decoration processing loop here too
620 int decoration_count = 0;
621 int max_decorations = 10; // Reasonable limit for decorations per wall
622
623 // Increment and check for end
624 while (it != tile->items.end() && decoration_count < max_decorations) {
625 // If we have a decoration ontop of us, we need to change it's alignment aswell!
626 Item* wall_decoration = *it;
627 WallBrush* brush = wall_decoration->getWallBrush();
628 if (brush && brush->isWallDecoration()) {
629 decoration_count++;
630
631 // We know we have changed alignment, so no need to check for it again.
632 uint16_t id = 0;
633 WallNode& wn = brush->wall_items[int(bt)];
634 if (wn.total_chance <= 0) {
635 if (wn.items.size() == 0) {
636 ++it;
637 continue;
638 } else {
639 id = wn.items.front().id;
640 }
641 } else {
642 int chance = random(1, wn.total_chance);
643 for (std::vector<WallType>::const_iterator node_iter = wn.items.begin();
644 node_iter != wn.items.end();
645 ++node_iter) {
646 if (chance <= node_iter->chance) {
647 id = node_iter->id;
648 break;
649 }
650 }
651 }
652 if (id != 0) {
653 Item* new_wall = Item::Create(id);
654 if (wall_decoration->isSelected()) {
655 new_wall->select();
656 }
657 items_to_add.push_back(new_wall);
658 }
659 ++it;
660 } else {
661 ++it;
662 break;
663 }
664 }
665 }
666 }
667
668 // Safety check: if we haven't moved the iterator at all, force advancement
669 if (!exit && it != tile->items.end()) {
670 ++it;
671 }
672 }
673
674 tile->cleanWalls();
675 for (ItemVector::const_iterator it = items_to_add.begin(); it != items_to_add.end(); ++it) {
676 tile->addWallItem(*it);
677 }
678}
679
680bool WallBrush::hasWall(Item* item) {
681 ASSERT(item->isWall());
682 ::BorderType bt = item->getWallAlignment();
683
684 WallBrush* test_wall = this;
685
686 // Add protection against infinite redirect loops
687 int redirect_count = 0;
688 const int max_redirects = 20; // Reasonable limit for redirect chains
689
690 while (test_wall != nullptr && redirect_count < max_redirects) {
691 for (std::vector<WallType>::const_iterator it = test_wall->wall_items[int(bt)].items.begin(); it != test_wall->wall_items[int(bt)].items.end(); ++it) {
692 if (it->id == item->getID()) {
693 return true;
694 }
695 }
696 for (std::vector<DoorType>::const_iterator it = test_wall->door_items[int(bt)].begin(); it != test_wall->door_items[int(bt)].end(); ++it) {
697 if (it->id == item->getID()) {
698 return true;
699 }
700 }
701
702 test_wall = test_wall->redirect_to;
703 redirect_count++;
704 if (test_wall == this) {
705 return false; // Prevent infinite loop
706 }
707 }
708 return false;
709}
710
711::DoorType WallBrush::getDoorTypeFromID(uint16_t id) {
712 for (int index = 0; index < 16; ++index) {
713 for (std::vector<DoorType>::const_iterator iter = door_items[index].begin(); iter != door_items[index].end(); ++iter) {
714 if (iter->id == id) {
715 return iter->type;
716 }
717 }
718 }
719 return WALL_UNDEFINED;
720}
721
722//=============================================================================
723// Wall Decoration brush
724
725WallDecorationBrush::WallDecorationBrush() {
726 ////
727}
728
729WallDecorationBrush::~WallDecorationBrush() {
730 ////
731}
732
733void WallDecorationBrush::draw(BaseMap* map, Tile* tile, void* parameter) {
734 ASSERT(tile);
735
736 ItemVector::iterator iter = tile->items.begin();
737
738 bool prefLocked = g_gui.HasDoorLocked();
739
740 tile->cleanWalls(this);
741 while (iter != tile->items.end()) {
742 Item* item = *iter;
743 if (item->isBorder()) {
744 ++iter;
745 continue;
746 }
747
748 if (item->isWall()) {
749 // Now we found something interesting.
750
751 // Is it just a decoration, like what we're trying to add?
752 WallBrush* brush = item->getWallBrush();
753 if (brush && brush->isWallDecoration()) {
754 // It is, discard and advance!
755 ++iter;
756 continue;
757 }
758
759 // We first need to figure out the alignment of this item (wall)
760 BorderType wall_alignment = item->getWallAlignment();
761
762 // Now we need to figure out if we got an item that mights suffice to place on this tile..
763
764 int id = 0;
765 if (item->isBrushDoor()) {
766 // If it's a door
767 ::DoorType doortype = brush->getDoorTypeFromID(item->getID());
768 uint16_t discarded_id = 0;
769 bool close_match = false;
770 bool open = item->isOpen();
771
772 for (std::vector<WallBrush::DoorType>::iterator door_iter = door_items[wall_alignment].begin();
773 door_iter != door_items[wall_alignment].end();
774 ++door_iter) {
775 WallBrush::DoorType& dt = *door_iter;
776 if (dt.type == doortype) {
777 ASSERT(dt.id);
778 ItemType& it = g_items[dt.id];
779 ASSERT(it.id != 0);
780
781 if (it.isOpen == open) {
782 if (open || dt.locked == prefLocked) {
783 id = dt.id;
784 break;
785 } else {
786 discarded_id = dt.id;
787 close_match = true;
788 }
789 } else {
790 discarded_id = dt.id;
791 close_match = true;
792 }
793 if (!close_match && discarded_id == 0) {
794 discarded_id = dt.id;
795 }
796 }
797 }
798 if (id == 0) {
799 id = discarded_id;
800 if (id == 0) {
801 ++iter;
802 continue;
803 }
804 }
805 } else {
806 // If it's a normal wall...
807 if (wall_items[wall_alignment].total_chance <= 0) {
808 // No fitting item, exit
809 ++iter;
810 continue;
811 }
812 int chance = random(1, wall_items[wall_alignment].total_chance);
813 for (auto it = wall_items[wall_alignment].items.begin(); it != wall_items[wall_alignment].items.end(); ++it) {
814 if (chance <= it->chance) {
815 id = it->id;
816 break;
817 }
818 }
819 }
820 // If we found an invalid id we should've already exited the loop
821 ASSERT(id);
822
823 // Add a matching item above this item.
824 Item* item = Item::Create(id);
825 ++iter;
826 iter = tile->items.insert(iter, item);
827 }
828 ++iter;
829 }
830}
831
Top