1 #include "Debug_Pages.h"
2 #include "Animation_Data.h"
3 #include "Environment.h"
4 #include "Font.h"
5 #include "Structure.h"
6 #include "TileDef.h"
7 #include "WorldDef.h"
8 #include "Smooth.h"
9 #include "WorldMan.h"
10 #include "Lighting.h"
11 #include "RenderWorld.h"
12 #include "Overhead.h"
13 #include "AI.h"
14 #include "Animation_Control.h"
15 #include "Isometric_Utils.h"
16 #include "Font_Control.h"
17 #include "Message.h"
18 #include "Tile_Cache.h"
19 #include "SaveLoadMap.h"
20 #include "Random.h"
21 #include "Render_Fun.h"
22 #include "GameSettings.h"
23 #include "MemMan.h"
24
25 #include <string_theory/format>
26 #include <string_theory/string>
27 #include <numeric>
28 #include <stdexcept>
29
30
31 // LEVEL NODE MANIPLULATION FUNCTIONS
CreateLevelNode(void)32 static LEVELNODE* CreateLevelNode(void)
33 {
34 LEVELNODE* const Node = new LEVELNODE{};
35 Node->ubShadeLevel = LightGetAmbient();
36 Node->ubNaturalShadeLevel = LightGetAmbient();
37 Node->pSoldier = NULL;
38 Node->pNext = NULL;
39 Node->sRelativeX = 0;
40 Node->sRelativeY = 0;
41 return Node;
42 }
43
44
CountLevelNodes(int (& guiLNCount)[9])45 static void CountLevelNodes(int (&guiLNCount)[9])
46 {
47 FOR_EACH_WORLD_TILE(pME)
48 {
49 // start at 1 to skip land head ptr; 0 stores total
50 for (UINT32 uiLoop2 = 1; uiLoop2 < 9; uiLoop2++)
51 {
52 for (const LEVELNODE* pLN = pME->pLevelNodes[uiLoop2]; pLN != NULL; pLN = pLN->pNext)
53 {
54 guiLNCount[uiLoop2]++;
55 }
56 }
57 }
58 guiLNCount[0] = std::accumulate(guiLNCount + 1, guiLNCount + 9, 0);
59 }
60
61
DebugLevelNodePage(void)62 void DebugLevelNodePage(void)
63 {
64 int guiLNCount[9] {};
65 const ST::string levelString[]
66 {
67 ST_LITERAL("Land"),
68 ST_LITERAL("Object"),
69 ST_LITERAL("Struct"),
70 ST_LITERAL("Shadow"),
71 ST_LITERAL("Merc"),
72 ST_LITERAL("Roof"),
73 ST_LITERAL("Onroof"),
74 ST_LITERAL("Topmost")
75 };
76
77 MPageHeader("DEBUG LEVELNODES PAGE 1 OF 1");
78 INT32 y = DEBUG_PAGE_START_Y;
79 INT32 h = DEBUG_PAGE_LINE_HEIGHT;
80 CountLevelNodes(guiLNCount);
81
82 for (UINT32 uiLoop = 1; uiLoop < 9; uiLoop++)
83 {
84 MPrintStat(DEBUG_PAGE_FIRST_COLUMN, y += h, levelString[uiLoop - 1], guiLNCount[uiLoop]);
85 }
86 MPrint(DEBUG_PAGE_FIRST_COLUMN, y += h, ST::format("{} land nodes in excess of world max (25600)", guiLNCount[1] - WORLD_MAX));
87 MPrint(DEBUG_PAGE_FIRST_COLUMN, y += h, ST::format("Total # levelnodes {}, {} bytes each", guiLNCount[0], sizeof(LEVELNODE)));
88 MPrint(DEBUG_PAGE_FIRST_COLUMN, y += h, ST::format("Total memory for levelnodes {}", guiLNCount[0] * sizeof(LEVELNODE)));
89 }
90
91
FindTypeInLayer(LEVELNODE * const start_node,UINT32 const type)92 static LEVELNODE* FindTypeInLayer(LEVELNODE* const start_node, UINT32 const type)
93 {
94 // Look through all objects and Search for type
95 for (LEVELNODE* i = start_node; i; i = i->pNext)
96 {
97 UINT16 const idx = i->usIndex;
98 if (idx == NO_TILE || idx >= NUMBEROFTILES) continue;
99 if (GetTileType(idx) != type) continue;
100 return i;
101 }
102 return 0;
103 }
104
105
106 // First for object layer
107 // #################################################################
108
AddObjectToTail(const UINT32 iMapIndex,const UINT16 usIndex)109 LEVELNODE* AddObjectToTail(const UINT32 iMapIndex, const UINT16 usIndex)
110 {
111 LEVELNODE* const n = CreateLevelNode();
112 n->usIndex = usIndex;
113
114 // Append node to list
115 LEVELNODE** anchor = &gpWorldLevelData[iMapIndex].pObjectHead;
116 while (*anchor != NULL) anchor = &(*anchor)->pNext;
117 *anchor = n;
118
119 ResetSpecificLayerOptimizing(TILES_DYNAMIC_OBJECTS);
120 return n;
121 }
122
123
AddObjectToHead(const UINT32 iMapIndex,const UINT16 usIndex)124 LEVELNODE* AddObjectToHead(const UINT32 iMapIndex, const UINT16 usIndex)
125 {
126 LEVELNODE* const n = CreateLevelNode();
127 n->usIndex = usIndex;
128
129 LEVELNODE** const head = &gpWorldLevelData[iMapIndex].pObjectHead;
130 n->pNext = *head;
131 *head = n;
132
133 ResetSpecificLayerOptimizing(TILES_DYNAMIC_OBJECTS);
134 AddObjectToMapTempFile(iMapIndex, usIndex);
135 return n;
136 }
137
138
RemoveObject(UINT32 iMapIndex,UINT16 usIndex)139 BOOLEAN RemoveObject(UINT32 iMapIndex, UINT16 usIndex)
140 {
141 // Look through all objects and remove index if found
142 LEVELNODE* pOldObject = NULL;
143 for (LEVELNODE* pObject = gpWorldLevelData[iMapIndex].pObjectHead; pObject != NULL; pObject = pObject->pNext)
144 {
145 if (pObject->usIndex == usIndex)
146 {
147 // OK, set links
148 // Check for head or tail
149 if (pOldObject == NULL)
150 {
151 // It's the head
152 gpWorldLevelData[iMapIndex].pObjectHead = pObject->pNext;
153 }
154 else
155 {
156 pOldObject->pNext = pObject->pNext;
157 }
158
159 CheckForAndDeleteTileCacheStructInfo(pObject, usIndex);
160
161 delete pObject;
162
163 //Add the index to the maps temp file so we can remove it after reloading the map
164 AddRemoveObjectToMapTempFile(iMapIndex, usIndex);
165
166 return TRUE;
167 }
168
169 pOldObject = pObject;
170 }
171
172 // Could not find it
173 return FALSE;
174 }
175
176
TypeRangeExistsInObjectLayer(UINT32 const iMapIndex,UINT32 const fStartType,UINT32 const fEndType)177 LEVELNODE* TypeRangeExistsInObjectLayer(UINT32 const iMapIndex, UINT32 const fStartType, UINT32 const fEndType)
178 {
179 // Look through all objects and Search for type
180 for (LEVELNODE* pObject = gpWorldLevelData[iMapIndex].pObjectHead; pObject != NULL; pObject = pObject->pNext)
181 {
182 if (pObject->usIndex == NO_TILE || pObject->usIndex >= NUMBEROFTILES) continue;
183
184 UINT32 const fTileType = GetTileType(pObject->usIndex);
185 if (fTileType < fStartType || fEndType < fTileType) continue;
186
187 return pObject;
188 }
189
190 // Could not find it
191 return 0;
192 }
193
194
FindTypeInObjectLayer(UINT32 const map_idx,UINT32 const type)195 LEVELNODE* FindTypeInObjectLayer(UINT32 const map_idx, UINT32 const type)
196 {
197 return FindTypeInLayer(gpWorldLevelData[map_idx].pObjectHead, type);
198 }
199
200
RemoveAllObjectsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)201 BOOLEAN RemoveAllObjectsOfTypeRange( UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType )
202 {
203 BOOLEAN fRetVal = FALSE;
204
205 // Look through all objects and Search for type
206 for (LEVELNODE* pObject = gpWorldLevelData[iMapIndex].pObjectHead; pObject != NULL;)
207 {
208 LEVELNODE* Next = pObject->pNext;
209
210 if (pObject->usIndex != NO_TILE && pObject->usIndex < NUMBEROFTILES)
211 {
212 const UINT32 fTileType = GetTileType(pObject->usIndex);
213 if (fTileType >= fStartType && fTileType <= fEndType)
214 {
215 RemoveObject(iMapIndex, pObject->usIndex);
216 fRetVal = TRUE;
217 }
218 }
219
220 pObject = Next;
221 }
222 return fRetVal;
223 }
224
225
226 // #######################################################
227 // Land Piece Layer
228 // #######################################################
229
AddLandToTail(const UINT32 iMapIndex,const UINT16 usIndex)230 LEVELNODE* AddLandToTail(const UINT32 iMapIndex, const UINT16 usIndex)
231 {
232 LEVELNODE* const n = CreateLevelNode();
233 n->usIndex = usIndex;
234
235 // Append node to list
236 LEVELNODE* prev = NULL;
237 LEVELNODE** anchor = &gpWorldLevelData[iMapIndex].pLandHead;
238 while (*anchor != NULL)
239 {
240 prev = *anchor;
241 anchor = &prev->pNext;
242 }
243 *anchor = n;
244 n->pPrevNode = prev;
245
246 ResetSpecificLayerOptimizing(TILES_DYNAMIC_LAND);
247 return n;
248 }
249
250
AddLandToHead(const UINT32 iMapIndex,const UINT16 usIndex)251 void AddLandToHead(const UINT32 iMapIndex, const UINT16 usIndex)
252 {
253 LEVELNODE* const n = CreateLevelNode();
254 n->usIndex = usIndex;
255
256 LEVELNODE** const head = &gpWorldLevelData[iMapIndex].pLandHead;
257 if (*head != NULL) (*head)->pPrevNode = n;
258 n->pNext = *head;
259 n->pPrevNode = NULL;
260 *head = n;
261
262 if (usIndex < NUMBEROFTILES && gTileDatabase[usIndex].ubFullTile)
263 {
264 gpWorldLevelData[iMapIndex].pLandStart = n;
265 }
266
267 ResetSpecificLayerOptimizing(TILES_DYNAMIC_LAND);
268 }
269
270
271 static BOOLEAN AdjustForFullTile(UINT32 iMapIndex);
272 static void RemoveLandEx(UINT32 iMapIndex, UINT16 usIndex);
273
274
RemoveLand(UINT32 const map_idx,UINT16 const idx)275 void RemoveLand(UINT32 const map_idx, UINT16 const idx)
276 {
277 RemoveLandEx(map_idx, idx);
278 AdjustForFullTile(map_idx);
279 }
280
281
RemoveLandEx(UINT32 iMapIndex,UINT16 usIndex)282 static void RemoveLandEx(UINT32 iMapIndex, UINT16 usIndex)
283 {
284 // Look through all Lands and remove index if found
285 for (LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead; pLand != NULL; pLand = pLand->pNext)
286 {
287 if (pLand->usIndex == usIndex)
288 {
289 // Check for head
290 if (pLand->pPrevNode == NULL)
291 {
292 // It's the head
293 gpWorldLevelData[iMapIndex].pLandHead = pLand->pNext;
294 }
295 else
296 {
297 pLand->pPrevNode->pNext = pLand->pNext;
298 }
299
300 // Check for tail
301 if (pLand->pNext != NULL)
302 {
303 pLand->pNext->pPrevNode = pLand->pPrevNode;
304 }
305
306 delete pLand;
307 break;
308 }
309 }
310 }
311
312
AdjustForFullTile(UINT32 iMapIndex)313 static BOOLEAN AdjustForFullTile(UINT32 iMapIndex)
314 {
315 for (LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead; pLand != NULL; pLand = pLand->pNext)
316 {
317 if (pLand->usIndex < NUMBEROFTILES)
318 {
319 // If this is a full tile, set new full tile
320 if (gTileDatabase[pLand->usIndex].ubFullTile)
321 {
322 gpWorldLevelData[iMapIndex].pLandStart = pLand;
323 return TRUE;
324 }
325 }
326 }
327
328 // Could not find a full tile
329 // Set to tail, and convert it to a full tile!
330 // Add a land piece to tail from basic land
331
332 UINT16 NewIndex = Random(10);
333
334 // Adjust for type
335 NewIndex += gTileTypeStartIndex[gCurrentBackground];
336
337 LEVELNODE* pNewNode = AddLandToTail(iMapIndex, NewIndex);
338 gpWorldLevelData[iMapIndex].pLandStart = pNewNode;
339
340 return FALSE;
341 }
342
343
ReplaceLandIndex(UINT32 const iMapIndex,UINT16 const usOldIndex,UINT16 const usNewIndex)344 void ReplaceLandIndex(UINT32 const iMapIndex, UINT16 const usOldIndex, UINT16 const usNewIndex)
345 {
346 // Look through all Lands and remove index if found
347 for (LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead; pLand != NULL; pLand = pLand->pNext)
348 {
349 if (pLand->usIndex == usOldIndex)
350 {
351 // OK, set new index value
352 pLand->usIndex = usNewIndex;
353 AdjustForFullTile(iMapIndex);
354 return;
355 }
356 }
357
358 // Could not find it
359 throw std::logic_error("Tried to replace non-existent land index");
360 }
361
362
FindTypeInLandLayer(UINT32 const map_idx,UINT32 const type)363 LEVELNODE* FindTypeInLandLayer(UINT32 const map_idx, UINT32 const type)
364 {
365 return FindTypeInLayer(gpWorldLevelData[map_idx].pLandHead, type);
366 }
367
368
TypeRangeExistsInLandLayer(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)369 BOOLEAN TypeRangeExistsInLandLayer(UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType)
370 {
371 // Look through all objects and Search for type
372 for (const LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead; pLand != NULL; )
373 {
374 if (pLand->usIndex != NO_TILE)
375 {
376 const UINT32 fTileType = GetTileType(pLand->usIndex);
377
378 pLand = pLand->pNext; // XXX TODO0009 if pLand->usIndex == NO_TILE this is an endless loop
379
380 if (fTileType >= fStartType && fTileType <= fEndType)
381 {
382 return TRUE;
383 }
384 }
385 }
386
387 // Could not find it
388 return FALSE;
389 }
390
391
RemoveAllLandsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)392 BOOLEAN RemoveAllLandsOfTypeRange( UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType )
393 {
394 const LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead;
395 BOOLEAN fRetVal = FALSE;
396
397 // Look through all objects and Search for type
398 while (pLand != NULL)
399 {
400 if (pLand->usIndex != NO_TILE)
401 {
402 const LEVELNODE* Next = pLand->pNext;
403
404 const UINT32 fTileType = GetTileType(pLand->usIndex);
405 if (fTileType >= fStartType && fTileType <= fEndType)
406 {
407 // Remove Item
408 RemoveLand(iMapIndex, pLand->usIndex);
409 fRetVal = TRUE;
410 }
411
412 pLand = Next; // XXX TODO0009 if pLand->usIndex == NO_TILE this is an endless loop
413 }
414 }
415 return fRetVal;
416 }
417
418
DeleteAllLandLayers(UINT32 iMapIndex)419 void DeleteAllLandLayers(UINT32 iMapIndex)
420 {
421 const LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead;
422
423 while (pLand != NULL)
424 {
425 const LEVELNODE* pOldLand = pLand;
426 pLand = pLand->pNext;
427 RemoveLandEx(iMapIndex, pOldLand->usIndex);
428 }
429
430 // Set world data values
431 gpWorldLevelData[iMapIndex].pLandHead = NULL;
432 gpWorldLevelData[iMapIndex].pLandStart = NULL;
433 }
434
435
InsertLandIndexAtLevel(const UINT32 iMapIndex,const UINT16 usIndex,const UINT8 ubLevel)436 void InsertLandIndexAtLevel(const UINT32 iMapIndex, const UINT16 usIndex, const UINT8 ubLevel)
437 {
438 // If we want to insert at head;
439 if (ubLevel == 0)
440 {
441 AddLandToHead(iMapIndex, usIndex);
442 return;
443 }
444
445 // Move to index before insertion
446 LEVELNODE* pLand = gpWorldLevelData[iMapIndex].pLandHead;
447 for (UINT8 level = 0;; ++level)
448 {
449 if (!pLand) throw std::logic_error("Tried to insert land index at invalid level");
450
451 if (level == ubLevel - 1) break;
452
453 pLand = pLand->pNext;
454 }
455
456 LEVELNODE* const n = CreateLevelNode();
457 n->usIndex = usIndex;
458
459 // Set links, according to position!
460 n->pPrevNode = pLand;
461 n->pNext = pLand->pNext;
462 pLand->pNext = n;
463
464 // Check for tail
465 if (n->pNext != NULL) n->pNext->pPrevNode = n;
466
467 AdjustForFullTile(iMapIndex);
468
469 ResetSpecificLayerOptimizing(TILES_DYNAMIC_LAND);
470 }
471
472
RemoveHigherLandLevels(UINT32 const map_idx,UINT32 const src_type,std::vector<UINT32> & out_higher_types)473 void RemoveHigherLandLevels(UINT32 const map_idx, UINT32 const src_type, std::vector<UINT32>& out_higher_types)
474 {
475 out_higher_types.clear();
476
477 // Get tail
478 LEVELNODE* tail = 0;
479 for (LEVELNODE* i = gpWorldLevelData[map_idx].pLandHead; i; i = i->pNext)
480 {
481 tail = i;
482 }
483
484 UINT8 const src_log_height = GetTileTypeLogicalHeight(src_type);
485 for (LEVELNODE* i = tail; i;)
486 {
487 LEVELNODE const& l = *i;
488 i = i->pPrevNode;
489
490 UINT32 const tile_type = GetTileType(l.usIndex);
491 if (GetTileTypeLogicalHeight(tile_type) <= src_log_height) continue;
492
493 RemoveLand(map_idx, l.usIndex);
494
495 out_higher_types.push_back(tile_type);
496 }
497
498 AdjustForFullTile(map_idx);
499 }
500
501
AddNodeToWorld(UINT32 const iMapIndex,UINT16 const usIndex,INT8 const level)502 static LEVELNODE* AddNodeToWorld(UINT32 const iMapIndex, UINT16 const usIndex, INT8 const level)
503 {
504 LEVELNODE* const n = CreateLevelNode();
505 n->usIndex = usIndex;
506
507 if (usIndex >= NUMBEROFTILES) return n;
508
509 const DB_STRUCTURE_REF* const sr = gTileDatabase[usIndex].pDBStructureRef;
510 if (!sr) return n;
511
512 if (AddStructureToWorld(iMapIndex, level, sr, n)) return n;
513
514 delete n;
515 throw FailedToAddNode();
516 }
517
518
519 // Struct layer
520 // #################################################################
521
AddStructToTailCommon(UINT32 const map_idx,UINT16 const idx,LEVELNODE * const n)522 static LEVELNODE* AddStructToTailCommon(UINT32 const map_idx, UINT16 const idx, LEVELNODE* const n)
523 {
524 MAP_ELEMENT& me = gpWorldLevelData[map_idx];
525 // Append node to list
526 LEVELNODE** anchor = &me.pStructHead;
527 while (*anchor) anchor = &(*anchor)->pNext;
528 *anchor = n;
529
530 if (idx < NUMBEROFTILES)
531 {
532 TILE_ELEMENT const& te = gTileDatabase[idx];
533 // Check flags for tiledat and set a shadow if we have a buddy
534 if (!GridNoIndoors(map_idx) && te.uiFlags & HAS_SHADOW_BUDDY && te.sBuddyNum != -1)
535 {
536 LEVELNODE* const n = AddShadowToHead(map_idx, te.sBuddyNum);
537 n->uiFlags |= LEVELNODE_BUDDYSHADOW;
538 }
539
540 // Check for special flag to stop burn-through on same-tile structs
541 if (DB_STRUCTURE_REF const* const sr = te.pDBStructureRef)
542 {
543 // If we are NOT a wall and NOT multi-tiles, set mapelement flag
544 if (!FindStructure(map_idx, STRUCTURE_WALLSTUFF) && sr->pDBStructure->ubNumberOfTiles == 1) // XXX TODO0015
545 {
546 me.ubExtFlags[0] |= MAPELEMENT_EXT_NOBURN_STRUCT;
547 }
548 else
549 {
550 me.ubExtFlags[0] &= ~MAPELEMENT_EXT_NOBURN_STRUCT;
551 }
552 }
553 }
554
555 AddStructToMapTempFile(map_idx, idx);
556
557 ResetSpecificLayerOptimizing(TILES_DYNAMIC_STRUCTURES);
558 return n;
559 }
560
561
AddStructToTail(UINT32 const map_idx,UINT16 const idx)562 LEVELNODE* AddStructToTail(UINT32 const map_idx, UINT16 const idx)
563 {
564 LEVELNODE* const n = AddNodeToWorld(map_idx, idx, 0);
565 return AddStructToTailCommon(map_idx, idx, n);
566 }
567
568
ForceStructToTail(UINT32 const map_idx,UINT16 const idx)569 LEVELNODE* ForceStructToTail(UINT32 const map_idx, UINT16 const idx)
570 {
571 LEVELNODE* const n = CreateLevelNode();
572 n->usIndex = idx;
573 return AddStructToTailCommon(map_idx, idx, n);
574 }
575
576
AddStructToHead(UINT32 const map_idx,UINT16 const idx)577 void AddStructToHead(UINT32 const map_idx, UINT16 const idx)
578 {
579 LEVELNODE* const n = AddNodeToWorld(map_idx, idx, 0);
580
581 MAP_ELEMENT& me = gpWorldLevelData[map_idx];
582 // Prepend node to list
583 LEVELNODE** const head = &me.pStructHead;
584 n->pNext = *head;
585 *head = n;
586
587 if (idx < NUMBEROFTILES)
588 {
589 TILE_ELEMENT const& te = gTileDatabase[idx];
590 // Check flags for tiledat and set a shadow if we have a buddy
591 if (!GridNoIndoors(map_idx) && te.uiFlags & HAS_SHADOW_BUDDY && te.sBuddyNum != -1)
592 {
593 LEVELNODE* const n = AddShadowToHead(map_idx, te.sBuddyNum);
594 n->uiFlags |= LEVELNODE_BUDDYSHADOW;
595 }
596
597 // Check for special flag to stop burn-through on same-tile structs
598 if (DB_STRUCTURE_REF const* const sr = te.pDBStructureRef)
599 {
600 // If we are NOT a wall and NOT multi-tiles, set mapelement flag
601 if (FindStructure(map_idx, STRUCTURE_WALLSTUFF) && sr->pDBStructure->ubNumberOfTiles == 1) // XXX TODO0015
602 {
603 me.ubExtFlags[0] |= MAPELEMENT_EXT_NOBURN_STRUCT;
604 }
605 else
606 {
607 me.ubExtFlags[0] &= ~MAPELEMENT_EXT_NOBURN_STRUCT;
608 }
609 }
610 }
611
612 AddStructToMapTempFile(map_idx, idx);
613
614 ResetSpecificLayerOptimizing(TILES_DYNAMIC_STRUCTURES);
615 }
616
617
InsertStructIndex(const UINT32 iMapIndex,const UINT16 usIndex,const UINT8 ubLevel)618 static void InsertStructIndex(const UINT32 iMapIndex, const UINT16 usIndex, const UINT8 ubLevel)
619 {
620 // If we want to insert at head
621 if (ubLevel == 0)
622 {
623 AddStructToHead(iMapIndex, usIndex);
624 return;
625 }
626
627 // Move to index before insertion
628 LEVELNODE* pStruct = gpWorldLevelData[iMapIndex].pStructHead;
629 for (UINT8 level = 0;; ++level)
630 {
631 if (!pStruct) throw std::logic_error("Tried to insert struct at invalid level");
632
633 if (level == ubLevel - 1) break;
634
635 pStruct = pStruct->pNext;
636 }
637
638 LEVELNODE* const n = AddNodeToWorld(iMapIndex, usIndex, 0);
639
640 // Set links, according to position!
641 n->pNext = pStruct->pNext;
642 pStruct->pNext = n;
643
644 ResetSpecificLayerOptimizing(TILES_DYNAMIC_STRUCTURES);
645 }
646
647
648 static BOOLEAN RemoveShadow(UINT32 iMapIndex, UINT16 usIndex);
649
650
RemoveShadowBuddy(UINT32 iMapIndex,UINT16 usIndex)651 static void RemoveShadowBuddy(UINT32 iMapIndex, UINT16 usIndex)
652 {
653 if (usIndex >= NUMBEROFTILES) return;
654 if (GridNoIndoors(iMapIndex)) return;
655
656 const TILE_ELEMENT* const te = &gTileDatabase[usIndex];
657 if (!(te->uiFlags & HAS_SHADOW_BUDDY)) return;
658 if (te->sBuddyNum == -1) return;
659
660 RemoveShadow(iMapIndex, te->sBuddyNum);
661 }
662
663
ForceRemoveStructFromTail(UINT32 const iMapIndex)664 void ForceRemoveStructFromTail(UINT32 const iMapIndex)
665 {
666 LEVELNODE* pPrevStruct = NULL;
667
668 // GOTO TAIL
669 for (LEVELNODE* pStruct = gpWorldLevelData[iMapIndex].pStructHead; pStruct != NULL; pStruct = pStruct->pNext)
670 {
671 // AT THE TAIL
672 if (pStruct->pNext == NULL)
673 {
674 if (pPrevStruct != NULL)
675 {
676 pPrevStruct->pNext = pStruct->pNext;
677 }
678 else
679 {
680 gpWorldLevelData[iMapIndex].pStructHead = pPrevStruct;
681 }
682
683 UINT16 usIndex = pStruct->usIndex;
684
685 // XXX TODO000A It rather seems like a memory leak not to DeleteStructureFromWorld() here. See InternalRemoveStruct()
686
687 //If we have to, make sure to remove this node when we reload the map from a saved game
688 RemoveStructFromMapTempFile(iMapIndex, usIndex);
689
690 delete pStruct;
691
692 RemoveShadowBuddy(iMapIndex, usIndex);
693 return;
694 }
695
696 pPrevStruct = pStruct;
697 }
698 }
699
700
InternalRemoveStruct(UINT32 const map_idx,LEVELNODE ** const anchor)701 static void InternalRemoveStruct(UINT32 const map_idx, LEVELNODE** const anchor)
702 {
703 LEVELNODE* const removee = *anchor;
704 *anchor = removee->pNext;
705
706 // Delete memory assosiated with item
707 DeleteStructureFromWorld(removee->pStructureData);
708
709 UINT16 const idx = removee->usIndex;
710
711 // If we have to, make sure to remove this node when we reload the map from a saved game
712 RemoveStructFromMapTempFile(map_idx, idx);
713
714 RemoveShadowBuddy(map_idx, idx);
715 delete removee;
716 }
717
718
RemoveStruct(UINT32 const map_idx,UINT16 const idx)719 void RemoveStruct(UINT32 const map_idx, UINT16 const idx)
720 {
721 // Look through all structs and remove index if found
722 for (LEVELNODE** anchor = &gpWorldLevelData[map_idx].pStructHead;; anchor = &(*anchor)->pNext)
723 {
724 LEVELNODE* const i = *anchor;
725 if (!i) return; // XXX exception?
726 if (i->usIndex != idx) continue;
727 InternalRemoveStruct(map_idx, anchor);
728 return;
729 }
730 }
731
732
RemoveStructFromLevelNode(UINT32 const map_idx,LEVELNODE * const n)733 void RemoveStructFromLevelNode(UINT32 const map_idx, LEVELNODE* const n)
734 {
735 // Look through all structs and remove index if found
736 for (LEVELNODE** anchor = &gpWorldLevelData[map_idx].pStructHead;; anchor = &(*anchor)->pNext)
737 {
738 LEVELNODE* const i = *anchor;
739 if (!i) return; // XXX exception?
740 if (i != n) continue;
741 InternalRemoveStruct(map_idx, anchor);
742 return;
743 }
744 }
745
746
RemoveAllStructsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)747 BOOLEAN RemoveAllStructsOfTypeRange(UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType)
748 {
749 BOOLEAN fRetVal = FALSE;
750
751 // Look through all structs and Search for type
752 for (const LEVELNODE* pStruct = gpWorldLevelData[iMapIndex].pStructHead; pStruct != NULL;)
753 {
754 if (pStruct->uiFlags & LEVELNODE_CACHEDANITILE)
755 {
756 pStruct = pStruct->pNext;
757 continue;
758 }
759
760 if (pStruct->usIndex != NO_TILE)
761 {
762 const UINT32 fTileType = GetTileType(pStruct->usIndex);
763
764 // Advance to next
765 const LEVELNODE* pOldStruct = pStruct;
766 pStruct = pStruct->pNext; // XXX TODO0009 if pStruct->usIndex == NO_TILE this is an endless loop
767
768 if (fTileType >= fStartType && fTileType <= fEndType)
769 {
770 UINT16 usIndex = pOldStruct->usIndex;
771 if (usIndex < NUMBEROFTILES)
772 {
773 RemoveStruct(iMapIndex, usIndex);
774 fRetVal = TRUE;
775 RemoveShadowBuddy(iMapIndex, usIndex);
776 }
777 }
778 }
779 }
780 return fRetVal;
781 }
782
783
784 //Kris: This was a serious problem. When saving the map and then reloading it, the structure
785 // information was invalid if you changed the types, etc. This is the bulletproof way.
ReplaceStructIndex(UINT32 iMapIndex,UINT16 usOldIndex,UINT16 usNewIndex)786 BOOLEAN ReplaceStructIndex(UINT32 iMapIndex, UINT16 usOldIndex, UINT16 usNewIndex)
787 {
788 RemoveStruct(iMapIndex, usOldIndex);
789 AddWallToStructLayer(iMapIndex, usNewIndex, FALSE);
790 return TRUE;
791 // LEVELNODE *pStruct = NULL;
792 // pStruct = gpWorldLevelData[ iMapIndex ].pStructHead;
793 // Look through all Structs and remove index if found
794 // while( pStruct != NULL )
795 // {
796 // if ( pStruct->usIndex == usOldIndex )
797 // {
798 // // OK, set new index value
799 // pStruct->usIndex = usNewIndex;
800 // AdjustForFullTile( iMapIndex );
801 // return( TRUE );
802 // }
803 // // Advance
804 // pStruct = pStruct->pNext;
805 // }
806 // // Could not find it, return FALSE
807 // return( FALSE );
808 }
809
810
811 // When adding, put in order such that it's drawn before any walls of a
812 // lesser orientation value
AddWallToStructLayer(INT32 const map_idx,UINT16 const idx,bool const replace)813 bool AddWallToStructLayer(INT32 const map_idx, UINT16 const idx, bool const replace)
814 {
815 // Get orientation of piece we want to add
816 UINT16 const wall_orientation = GetWallOrientation(idx);
817
818 // Look through all objects and Search for orientation
819 bool insert_found = false;
820 bool roof_found = false;
821 UINT8 roof_level = 0;
822 UINT8 level = 0;
823 for (LEVELNODE* i = gpWorldLevelData[map_idx].pStructHead; i; ++level, i = i->pNext)
824 {
825 if (i->uiFlags & LEVELNODE_CACHEDANITILE) continue;
826 UINT16 const check_wall_orient = GetWallOrientation(i->usIndex);
827
828 /* Kris: If placing a new wall which is at right angles to the current wall,
829 * then we insert it. */
830 if (check_wall_orient > wall_orientation)
831 {
832 if (((wall_orientation == INSIDE_TOP_RIGHT || wall_orientation == OUTSIDE_TOP_RIGHT) &&
833 (check_wall_orient == INSIDE_TOP_LEFT || check_wall_orient == OUTSIDE_TOP_LEFT)) ||
834 ((wall_orientation == INSIDE_TOP_LEFT || wall_orientation == OUTSIDE_TOP_LEFT) &&
835 (check_wall_orient == INSIDE_TOP_RIGHT || check_wall_orient == OUTSIDE_TOP_RIGHT)))
836 {
837 insert_found = true;
838 }
839 }
840
841 UINT32 const check_type = GetTileType(i->usIndex);
842 if (FIRSTROOF <= check_type && check_type <= LASTROOF)
843 {
844 roof_found = true;
845 roof_level = level;
846 }
847
848 /* Kris: We want to check for walls being parallel to each other. If so,
849 * then we we want to replace it. This is because of an existing problem
850 * with say, INSIDE_TOP_LEFT and OUTSIDE_TOP_LEFT walls coexisting. */
851 if (((wall_orientation == INSIDE_TOP_RIGHT || wall_orientation == OUTSIDE_TOP_RIGHT) &&
852 (check_wall_orient == INSIDE_TOP_RIGHT || check_wall_orient == OUTSIDE_TOP_RIGHT)) ||
853 ((wall_orientation == INSIDE_TOP_LEFT || wall_orientation == OUTSIDE_TOP_LEFT) &&
854 (check_wall_orient == INSIDE_TOP_LEFT || check_wall_orient == OUTSIDE_TOP_LEFT)))
855 {
856 // Same, if replace, replace here
857 return replace ? ReplaceStructIndex(map_idx, i->usIndex, idx) : false;
858 }
859 }
860
861 // Check if we found an insert position, otherwise set to head
862 if (insert_found)
863 {
864 AddStructToHead(map_idx, idx);
865 }
866 else if (roof_found) // Make sure it's ALWAYS after the roof (if any)
867 {
868 InsertStructIndex(map_idx, idx, roof_level);
869 }
870 else
871 {
872 AddStructToTail(map_idx, idx);
873 }
874
875 ResetSpecificLayerOptimizing(TILES_DYNAMIC_STRUCTURES);
876 return true;
877 }
878
879
IndexExistsInLayer(LEVELNODE const * n,UINT16 const tile_index)880 static bool IndexExistsInLayer(LEVELNODE const* n, UINT16 const tile_index)
881 {
882 for (; n; n = n->pNext)
883 {
884 if (n->usIndex == tile_index) return true;
885 }
886 return false;
887 }
888
889
IndexExistsInStructLayer(GridNo const grid_no,UINT16 const tile_index)890 BOOLEAN IndexExistsInStructLayer(GridNo const grid_no, UINT16 const tile_index)
891 {
892 return IndexExistsInLayer(gpWorldLevelData[grid_no].pStructHead, tile_index);
893 }
894
895
HideStructOfGivenType(UINT32 const iMapIndex,UINT32 const fType,BOOLEAN const fHide)896 void HideStructOfGivenType(UINT32 const iMapIndex, UINT32 const fType, BOOLEAN const fHide)
897 {
898 if (fHide)
899 {
900 SetRoofIndexFlagsFromTypeRange(iMapIndex, fType, fType, LEVELNODE_HIDDEN);
901 }
902 else
903 {
904 // ONLY UNHIDE IF NOT REAVEALED ALREADY
905 if (!(gpWorldLevelData[iMapIndex].uiFlags & MAPELEMENT_REVEALED))
906 {
907 RemoveRoofIndexFlagsFromTypeRange(iMapIndex, fType, fType, LEVELNODE_HIDDEN);
908 }
909 }
910 }
911
912
913 // Shadow layer
914 // #################################################################
915
AddShadowToTail(UINT32 const iMapIndex,UINT16 const usIndex)916 void AddShadowToTail(UINT32 const iMapIndex, UINT16 const usIndex)
917 {
918 LEVELNODE* const n = CreateLevelNode();
919 n->usIndex = usIndex;
920
921 // Append node to list
922 LEVELNODE** anchor = &gpWorldLevelData[iMapIndex].pShadowHead;
923 while (*anchor != NULL) anchor = &(*anchor)->pNext;
924 *anchor = n;
925
926 ResetSpecificLayerOptimizing(TILES_DYNAMIC_SHADOWS);
927 }
928
929
930 //Kris: identical shadows can exist in the same gridno, though it makes no sense
931 // because it actually renders the shadows darker than the others. This is an
932 // undesirable effect with walls and buildings so I added this function to make
933 // sure there isn't already a shadow before placing it.
AddExclusiveShadow(UINT32 iMapIndex,UINT16 usIndex)934 void AddExclusiveShadow(UINT32 iMapIndex, UINT16 usIndex)
935 {
936 for (LEVELNODE* pShadow = gpWorldLevelData[iMapIndex].pShadowHead; pShadow; pShadow = pShadow->pNext)
937 {
938 if (pShadow->usIndex == usIndex)
939 return;
940 }
941 AddShadowToHead(iMapIndex, usIndex);
942 }
943
944
AddShadowToHead(const UINT32 iMapIndex,const UINT16 usIndex)945 LEVELNODE* AddShadowToHead(const UINT32 iMapIndex, const UINT16 usIndex)
946 {
947 LEVELNODE* const n = CreateLevelNode();
948 n->usIndex = usIndex;
949
950 // Prepend node to list
951 LEVELNODE** const head = &gpWorldLevelData[iMapIndex].pShadowHead;
952 n->pNext = *head;
953 *head = n;
954
955 ResetSpecificLayerOptimizing(TILES_DYNAMIC_SHADOWS);
956 return n;
957 }
958
959
RemoveShadow(UINT32 iMapIndex,UINT16 usIndex)960 static BOOLEAN RemoveShadow(UINT32 iMapIndex, UINT16 usIndex)
961 {
962 // Look through all shadows and remove index if found
963 LEVELNODE* pOldShadow = NULL;
964 for (LEVELNODE* pShadow = gpWorldLevelData[iMapIndex].pShadowHead; pShadow != NULL; pShadow = pShadow->pNext)
965 {
966 if (pShadow->usIndex == usIndex)
967 {
968 // OK, set links
969 // Check for head
970 if (pOldShadow == NULL)
971 {
972 // It's the head
973 gpWorldLevelData[iMapIndex].pShadowHead = pShadow->pNext;
974 }
975 else
976 {
977 pOldShadow->pNext = pShadow->pNext;
978 }
979
980 delete pShadow;
981 return TRUE;
982 }
983
984 pOldShadow = pShadow;
985 }
986
987 // Could not find it
988 return FALSE;
989 }
990
991
RemoveShadowFromLevelNode(UINT32 iMapIndex,LEVELNODE * pNode)992 BOOLEAN RemoveShadowFromLevelNode(UINT32 iMapIndex, LEVELNODE* pNode)
993 {
994 LEVELNODE* pOldShadow = NULL;
995 for (LEVELNODE* pShadow = gpWorldLevelData[iMapIndex].pShadowHead; pShadow != NULL; pShadow = pShadow->pNext)
996 {
997 if (pShadow == pNode)
998 {
999 // OK, set links
1000 // Check for head
1001 if (pOldShadow == NULL)
1002 {
1003 // It's the head
1004 gpWorldLevelData[iMapIndex].pShadowHead = pShadow->pNext;
1005 }
1006 else
1007 {
1008 pOldShadow->pNext = pShadow->pNext;
1009 }
1010
1011 delete pShadow;
1012 return TRUE;
1013 }
1014
1015 pOldShadow = pShadow;
1016 }
1017
1018 // Could not find it
1019 return FALSE;
1020 }
1021
1022
RemoveAllShadowsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)1023 BOOLEAN RemoveAllShadowsOfTypeRange(UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType)
1024 {
1025 BOOLEAN fRetVal = FALSE;
1026
1027 // Look through all shadows and Search for type
1028 for (const LEVELNODE* pShadow = gpWorldLevelData[iMapIndex].pShadowHead; pShadow != NULL;)
1029 {
1030 if (pShadow->usIndex != NO_TILE)
1031 {
1032 const UINT32 fTileType = GetTileType(pShadow->usIndex);
1033
1034 // Advance to next
1035 const LEVELNODE* pOldShadow = pShadow;
1036 pShadow = pShadow->pNext;
1037
1038 if (fTileType >= fStartType && fTileType <= fEndType)
1039 {
1040 RemoveShadow(iMapIndex, pOldShadow->usIndex);
1041 fRetVal = TRUE;
1042 }
1043 }
1044 }
1045 return fRetVal;
1046 }
1047
1048
RemoveAllShadows(UINT32 iMapIndex)1049 BOOLEAN RemoveAllShadows( UINT32 iMapIndex )
1050 {
1051 BOOLEAN fRetVal = FALSE;
1052
1053 for (LEVELNODE* pShadow = gpWorldLevelData[iMapIndex].pShadowHead; pShadow != NULL;)
1054 {
1055 if (pShadow->usIndex != NO_TILE)
1056 {
1057 // Advance to next
1058 const LEVELNODE* pOldShadow = pShadow;
1059 pShadow = pShadow->pNext;
1060
1061 RemoveShadow(iMapIndex, pOldShadow->usIndex);
1062 fRetVal = TRUE;
1063 }
1064 }
1065 return fRetVal;
1066 }
1067
1068
1069 // Merc layer
1070 // #################################################################
1071
1072
1073 static void AddMercStructureInfo(INT16 sGridNo, SOLDIERTYPE* pSoldier);
1074
1075
AddMercToHead(UINT32 const iMapIndex,SOLDIERTYPE & s,BOOLEAN const fAddStructInfo)1076 LEVELNODE* AddMercToHead(UINT32 const iMapIndex, SOLDIERTYPE& s, BOOLEAN const fAddStructInfo)
1077 {
1078 LEVELNODE* pMerc = gpWorldLevelData[iMapIndex].pMercHead;
1079
1080 LEVELNODE* pNextMerc = CreateLevelNode();
1081 pNextMerc->pNext = pMerc;
1082 pNextMerc->pSoldier = &s;
1083 pNextMerc->uiFlags |= LEVELNODE_SOLDIER;
1084
1085 // Add structure info if we want
1086 if (fAddStructInfo)
1087 {
1088 // Set soldier's levelnode
1089 s.pLevelNode = pNextMerc;
1090 AddMercStructureInfo(iMapIndex, &s);
1091 }
1092
1093 gpWorldLevelData[iMapIndex].pMercHead = pNextMerc;
1094
1095 ResetSpecificLayerOptimizing(TILES_DYNAMIC_MERCS | TILES_DYNAMIC_STRUCT_MERCS | TILES_DYNAMIC_HIGHMERCS);
1096 return pNextMerc;
1097 }
1098
1099
AddMercStructureInfo(INT16 sGridNo,SOLDIERTYPE * pSoldier)1100 static void AddMercStructureInfo(INT16 sGridNo, SOLDIERTYPE* pSoldier)
1101 {
1102 UINT16 const usAnimSurface = GetSoldierAnimationSurface(pSoldier);
1103 AddMercStructureInfoFromAnimSurface(sGridNo, pSoldier, usAnimSurface, pSoldier->usAnimState);
1104 }
1105
1106
AddMercStructureInfoFromAnimSurface(const INT16 sGridNo,SOLDIERTYPE * const s,const UINT16 usAnimSurface,const UINT16 usAnimState)1107 BOOLEAN AddMercStructureInfoFromAnimSurface(const INT16 sGridNo, SOLDIERTYPE* const s, const UINT16 usAnimSurface, const UINT16 usAnimState)
1108 {
1109 s->uiStatusFlags &= ~SOLDIER_MULTITILE;
1110
1111 LEVELNODE* const n = s->pLevelNode;
1112 if (n == NULL || usAnimSurface == INVALID_ANIMATION_SURFACE) return FALSE;
1113
1114 // Remove existing structs
1115 DeleteStructureFromWorld(n->pStructureData);
1116 n->pStructureData = NULL;
1117
1118 const STRUCTURE_FILE_REF* const sfr = GetAnimationStructureRef(s, usAnimSurface, usAnimState);
1119 if (sfr == NULL) return TRUE; // XXX why TRUE?
1120
1121 const DB_STRUCTURE_REF* const sr =
1122 s->ubBodyType == QUEENMONSTER ? // Queen uses only one direction
1123 &sfr->pDBStructureRef[0] :
1124 &sfr->pDBStructureRef[OneCDirection(s->bDirection)];
1125
1126 bool const success = AddStructureToWorld(sGridNo, s->bLevel, sr, n);
1127 if (!success)
1128 {
1129 SLOGD("add struct info for merc %d (%s), at %d direction %d failed",
1130 s->ubID, s->name.c_str(), sGridNo, s->bDirection);
1131 }
1132
1133 // Turn on if we are multi-tiled
1134 if (sr->pDBStructure->ubNumberOfTiles > 1) s->uiStatusFlags |= SOLDIER_MULTITILE;
1135
1136 return success;
1137 }
1138
1139
OKToAddMercToWorld(SOLDIERTYPE * pSoldier,INT8 bDirection)1140 BOOLEAN OKToAddMercToWorld( SOLDIERTYPE *pSoldier, INT8 bDirection )
1141 {
1142 //if (pSoldier->uiStatusFlags & SOLDIER_MULTITILE)
1143 {
1144 // Get surface data
1145 UINT16 const usAnimSurface = GetSoldierAnimationSurface(pSoldier);
1146 if (usAnimSurface == INVALID_ANIMATION_SURFACE)
1147 {
1148 return FALSE;
1149 }
1150
1151 // Now check if we have multi-tile info!
1152 const STRUCTURE_FILE_REF* const pStructFileRef = GetAnimationStructureRef(pSoldier, usAnimSurface, pSoldier->usAnimState);
1153 if (pStructFileRef != NULL)
1154 {
1155 //Try adding struct to this location, if we can it's good!
1156 UINT16 usOKToAddStructID;
1157 if (pSoldier->pLevelNode && pSoldier->pLevelNode->pStructureData != NULL)
1158 {
1159 usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID;
1160 }
1161 else
1162 {
1163 usOKToAddStructID = INVALID_STRUCTURE_ID;
1164 }
1165
1166 if (!OkayToAddStructureToWorld(pSoldier->sGridNo, pSoldier->bLevel, &pStructFileRef->pDBStructureRef[OneCDirection(bDirection)], usOKToAddStructID))
1167 {
1168 return FALSE;
1169 }
1170 }
1171 }
1172
1173 return TRUE;
1174 }
1175
1176
UpdateMercStructureInfo(SOLDIERTYPE * pSoldier)1177 BOOLEAN UpdateMercStructureInfo(SOLDIERTYPE *pSoldier)
1178 {
1179 if (pSoldier->pLevelNode == NULL)
1180 {
1181 return FALSE;
1182 }
1183
1184 AddMercStructureInfo(pSoldier->sGridNo, pSoldier);
1185 return TRUE;
1186 }
1187
1188
RemoveMerc(UINT32 const map_idx,SOLDIERTYPE & s,bool const placeholder)1189 void RemoveMerc(UINT32 const map_idx, SOLDIERTYPE& s, bool const placeholder)
1190 {
1191 if (map_idx == NOWHERE) return; // XXX exception?
1192
1193 for (LEVELNODE** anchor = &gpWorldLevelData[map_idx].pMercHead;; anchor = &(*anchor)->pNext)
1194 {
1195 LEVELNODE* const merc = *anchor;
1196 if (!merc) break;
1197
1198 if (merc->pSoldier != &s) continue;
1199 if (placeholder ^ ((merc->uiFlags & LEVELNODE_MERCPLACEHOLDER) != 0)) continue;
1200
1201 *anchor = merc->pNext;
1202
1203 if (!placeholder)
1204 {
1205 s.pLevelNode = 0;
1206 DeleteStructureFromWorld(merc->pStructureData);
1207 }
1208
1209 delete merc;
1210 break;
1211 }
1212 // XXX exception?
1213 }
1214
1215
1216 // Roof layer
1217 // #################################################################
1218
AddRoof(const UINT32 iMapIndex,const UINT16 usIndex)1219 static LEVELNODE* AddRoof(const UINT32 iMapIndex, const UINT16 usIndex)
1220 {
1221 LEVELNODE* const n = AddNodeToWorld(iMapIndex, usIndex, 1);
1222 ResetSpecificLayerOptimizing(TILES_DYNAMIC_ROOF);
1223 return n;
1224 }
1225
1226
AddRoofToTail(const UINT32 iMapIndex,const UINT16 usIndex)1227 LEVELNODE* AddRoofToTail(const UINT32 iMapIndex, const UINT16 usIndex)
1228 {
1229 LEVELNODE* const n = AddRoof(iMapIndex, usIndex);
1230
1231 // Append node to list
1232 LEVELNODE** anchor = &gpWorldLevelData[iMapIndex].pRoofHead;
1233 while (*anchor != NULL) anchor = &(*anchor)->pNext;
1234 *anchor = n;
1235
1236 return n;
1237 }
1238
1239
AddRoofToHead(const UINT32 iMapIndex,const UINT16 usIndex)1240 LEVELNODE* AddRoofToHead(const UINT32 iMapIndex, const UINT16 usIndex)
1241 {
1242 LEVELNODE* const n = AddRoof(iMapIndex, usIndex);
1243
1244 // Prepend node to list
1245 LEVELNODE** const head = &gpWorldLevelData[iMapIndex].pRoofHead;
1246 n->pNext = *head;
1247 *head = n;
1248
1249 return n;
1250 }
1251
1252
RemoveRoof(UINT32 iMapIndex,UINT16 usIndex)1253 BOOLEAN RemoveRoof(UINT32 iMapIndex, UINT16 usIndex)
1254 {
1255 // Look through all Roofs and remove index if found
1256 LEVELNODE* pOldRoof = NULL;
1257 for (LEVELNODE* pRoof = gpWorldLevelData[iMapIndex].pRoofHead; pRoof != NULL; pRoof = pRoof->pNext)
1258 {
1259 if (pRoof->usIndex == usIndex)
1260 {
1261 // OK, set links
1262 // Check for head
1263 if (pOldRoof == NULL)
1264 {
1265 // It's the head
1266 gpWorldLevelData[iMapIndex].pRoofHead = pRoof->pNext;
1267 }
1268 else
1269 {
1270 pOldRoof->pNext = pRoof->pNext;
1271 }
1272
1273 DeleteStructureFromWorld(pRoof->pStructureData);
1274 delete pRoof;
1275 return TRUE;
1276 }
1277
1278 pOldRoof = pRoof;
1279 }
1280
1281 // Could not find it
1282 return FALSE;
1283 }
1284
1285
FindTypeInRoofLayer(UINT32 const map_idx,UINT32 const type)1286 LEVELNODE* FindTypeInRoofLayer(UINT32 const map_idx, UINT32 const type)
1287 {
1288 return FindTypeInLayer(gpWorldLevelData[map_idx].pRoofHead, type);
1289 }
1290
1291
TypeRangeExistsInRoofLayer(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)1292 LEVELNODE* TypeRangeExistsInRoofLayer(UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType)
1293 {
1294 // Look through all objects and Search for type
1295 for (LEVELNODE* pRoof = gpWorldLevelData[iMapIndex].pRoofHead; pRoof;)
1296 {
1297 if (pRoof->usIndex != NO_TILE)
1298 {
1299 const UINT32 fTileType = GetTileType(pRoof->usIndex);
1300 if (fStartType <= fTileType && fTileType <= fEndType)
1301 {
1302 return pRoof;
1303 }
1304 pRoof = pRoof->pNext; // XXX TODO0009 if pRoof->usIndex == NO_TILE this is an endless loop
1305 }
1306 }
1307
1308 // Could not find it
1309 return 0;
1310 }
1311
1312
IndexExistsInRoofLayer(INT16 const sGridNo,UINT16 const usIndex)1313 BOOLEAN IndexExistsInRoofLayer(INT16 const sGridNo, UINT16 const usIndex)
1314 {
1315 return IndexExistsInLayer(gpWorldLevelData[sGridNo].pRoofHead, usIndex);
1316 }
1317
1318
RemoveAllRoofsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)1319 BOOLEAN RemoveAllRoofsOfTypeRange(UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType)
1320 {
1321 BOOLEAN fRetVal = FALSE;
1322
1323 // Look through all Roofs and Search for type
1324 for (const LEVELNODE* pRoof = gpWorldLevelData[iMapIndex].pRoofHead; pRoof != NULL;)
1325 {
1326 if (pRoof->usIndex != NO_TILE)
1327 {
1328 const UINT32 fTileType = GetTileType(pRoof->usIndex);
1329
1330 // Advance to next
1331 const LEVELNODE* pOldRoof = pRoof;
1332 pRoof = pRoof->pNext; // XXX TODO0009 if pRoof->usIndex == NO_TILE this is an endless loop
1333
1334 if (fTileType >= fStartType && fTileType <= fEndType)
1335 {
1336 RemoveRoof(iMapIndex, pOldRoof->usIndex);
1337 fRetVal = TRUE;
1338 }
1339 }
1340 }
1341
1342 // Could not find it
1343 return fRetVal;
1344 }
1345
1346
RemoveRoofIndexFlagsFromTypeRange(UINT32 const iMapIndex,UINT32 const fStartType,UINT32 const fEndType,LevelnodeFlags const uiFlags)1347 void RemoveRoofIndexFlagsFromTypeRange(UINT32 const iMapIndex, UINT32 const fStartType, UINT32 const fEndType, LevelnodeFlags const uiFlags)
1348 {
1349 // Look through all Roofs and Search for type
1350 for (LEVELNODE* pRoof = gpWorldLevelData[iMapIndex].pRoofHead; pRoof != NULL;)
1351 {
1352 if (pRoof->usIndex != NO_TILE)
1353 {
1354 const UINT32 fTileType = GetTileType(pRoof->usIndex);
1355 if (fTileType >= fStartType && fTileType <= fEndType)
1356 {
1357 pRoof->uiFlags &= ~uiFlags;
1358 }
1359 pRoof = pRoof->pNext; // XXX TODO0009 if pRoof->usIndex == NO_TILE this is an endless loop
1360 }
1361 }
1362 }
1363
1364
SetRoofIndexFlagsFromTypeRange(UINT32 const iMapIndex,UINT32 const fStartType,UINT32 const fEndType,LevelnodeFlags const uiFlags)1365 void SetRoofIndexFlagsFromTypeRange(UINT32 const iMapIndex, UINT32 const fStartType, UINT32 const fEndType, LevelnodeFlags const uiFlags)
1366 {
1367 // Look through all Roofs and Search for type
1368 for (LEVELNODE* pRoof = gpWorldLevelData[iMapIndex].pRoofHead; pRoof != NULL;)
1369 {
1370 if (pRoof->usIndex != NO_TILE)
1371 {
1372 const UINT32 fTileType = GetTileType(pRoof->usIndex);
1373 if (fTileType >= fStartType && fTileType <= fEndType)
1374 {
1375 pRoof->uiFlags |= uiFlags;
1376 }
1377 pRoof = pRoof->pNext; // XXX TODO0009 if pRoof->usIndex == NO_TILE this is an endless loop
1378 }
1379 }
1380 }
1381
1382
1383 // OnRoof layer
1384 // #################################################################
1385
AddOnRoof(const UINT32 iMapIndex,const UINT16 usIndex)1386 static LEVELNODE* AddOnRoof(const UINT32 iMapIndex, const UINT16 usIndex)
1387 {
1388 LEVELNODE* const n = AddNodeToWorld(iMapIndex, usIndex, 1);
1389 ResetSpecificLayerOptimizing(TILES_DYNAMIC_ONROOF);
1390 return n;
1391 }
1392
1393
AddOnRoofToTail(const UINT32 iMapIndex,const UINT16 usIndex)1394 LEVELNODE* AddOnRoofToTail(const UINT32 iMapIndex, const UINT16 usIndex)
1395 {
1396 LEVELNODE* const n = AddOnRoof(iMapIndex, usIndex);
1397
1398 // Append the node to the list
1399 LEVELNODE** anchor = &gpWorldLevelData[iMapIndex].pOnRoofHead;
1400 while (*anchor != NULL) anchor = &(*anchor)->pNext;
1401 *anchor = n;
1402
1403 return n;
1404 }
1405
1406
AddOnRoofToHead(const UINT32 iMapIndex,const UINT16 usIndex)1407 LEVELNODE* AddOnRoofToHead(const UINT32 iMapIndex, const UINT16 usIndex)
1408 {
1409 LEVELNODE* const n = AddOnRoof(iMapIndex, usIndex);
1410
1411 // Prepend the node to the list
1412 LEVELNODE** const head = &gpWorldLevelData[iMapIndex].pOnRoofHead;
1413 n->pNext = *head;
1414 *head = n;
1415
1416 return n;
1417 }
1418
1419
RemoveOnRoof(UINT32 iMapIndex,UINT16 usIndex)1420 BOOLEAN RemoveOnRoof(UINT32 iMapIndex, UINT16 usIndex)
1421 {
1422 LEVELNODE* pOldOnRoof = NULL;
1423
1424 // Look through all OnRoofs and remove index if found
1425 for (LEVELNODE* pOnRoof = gpWorldLevelData[iMapIndex].pOnRoofHead; pOnRoof != NULL; pOnRoof = pOnRoof->pNext)
1426 {
1427 if (pOnRoof->usIndex == usIndex)
1428 {
1429 // OK, set links
1430 // Check for head
1431 if (pOldOnRoof == NULL)
1432 {
1433 // It's the head
1434 gpWorldLevelData[iMapIndex].pOnRoofHead = pOnRoof->pNext;
1435 }
1436 else
1437 {
1438 pOldOnRoof->pNext = pOnRoof->pNext;
1439 }
1440
1441 delete pOnRoof;
1442 return TRUE;
1443 }
1444
1445 pOldOnRoof = pOnRoof;
1446 }
1447
1448 // Could not find it
1449 return FALSE;
1450 }
1451
1452
RemoveOnRoofFromLevelNode(UINT32 iMapIndex,LEVELNODE * pNode)1453 BOOLEAN RemoveOnRoofFromLevelNode( UINT32 iMapIndex, LEVELNODE *pNode )
1454 {
1455 LEVELNODE* pOldOnRoof = NULL;
1456
1457 for (LEVELNODE* pOnRoof = gpWorldLevelData[iMapIndex].pOnRoofHead; pOnRoof != NULL; pOnRoof = pOnRoof->pNext)
1458 {
1459 if (pOnRoof == pNode)
1460 {
1461 // OK, set links
1462 // Check for head
1463 if (pOldOnRoof == NULL)
1464 {
1465 // It's the head
1466 gpWorldLevelData[iMapIndex].pOnRoofHead = pOnRoof->pNext;
1467 }
1468 else
1469 {
1470 pOldOnRoof->pNext = pOnRoof->pNext;
1471 }
1472
1473 delete pOnRoof;
1474 return TRUE;
1475 }
1476
1477 pOldOnRoof = pOnRoof;
1478 }
1479
1480 // Could not find it
1481 return FALSE;
1482 }
1483
1484
RemoveAllOnRoofsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)1485 BOOLEAN RemoveAllOnRoofsOfTypeRange( UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType )
1486 {
1487 BOOLEAN fRetVal = FALSE;
1488
1489 // Look through all OnRoofs and Search for type
1490 for (const LEVELNODE* pOnRoof = gpWorldLevelData[iMapIndex].pOnRoofHead; pOnRoof != NULL;)
1491 {
1492 if (pOnRoof->uiFlags & LEVELNODE_CACHEDANITILE)
1493 {
1494 pOnRoof = pOnRoof->pNext;
1495 continue;
1496 }
1497
1498 if (pOnRoof->usIndex != NO_TILE)
1499 {
1500 const UINT32 fTileType = GetTileType(pOnRoof->usIndex);
1501
1502 // Advance to next
1503 const LEVELNODE* pOldOnRoof = pOnRoof;
1504 pOnRoof = pOnRoof->pNext; // XXX TODO0009 if pOnRoof->usIndex == NO_TILE this is an endless loop
1505
1506 if (fTileType >= fStartType && fTileType <= fEndType)
1507 {
1508 RemoveOnRoof(iMapIndex, pOldOnRoof->usIndex);
1509 fRetVal = TRUE;
1510 }
1511 }
1512 }
1513 return fRetVal;
1514 }
1515
1516
1517 // Topmost layer
1518 // #################################################################
1519
AddTopmostToTail(const UINT32 iMapIndex,const UINT16 usIndex)1520 LEVELNODE* AddTopmostToTail(const UINT32 iMapIndex, const UINT16 usIndex)
1521 {
1522 LEVELNODE* const n = CreateLevelNode();
1523 n->usIndex = usIndex;
1524
1525 // Append node to list
1526 LEVELNODE** anchor = &gpWorldLevelData[iMapIndex].pTopmostHead;
1527 while (*anchor != NULL) anchor = &(*anchor)->pNext;
1528 *anchor = n;
1529
1530 ResetSpecificLayerOptimizing(TILES_DYNAMIC_TOPMOST);
1531 return n;
1532 }
1533
1534
AddUIElem(UINT32 iMapIndex,UINT16 usIndex,INT8 sRelativeX,INT8 sRelativeY)1535 LEVELNODE* AddUIElem(UINT32 iMapIndex, UINT16 usIndex, INT8 sRelativeX, INT8 sRelativeY)
1536 {
1537 LEVELNODE* pTopmost = AddTopmostToTail(iMapIndex, usIndex);
1538
1539 // Set flags
1540 pTopmost->uiFlags |= LEVELNODE_USERELPOS;
1541 pTopmost->sRelativeX = sRelativeX;
1542 pTopmost->sRelativeY = sRelativeY;
1543
1544 ResetSpecificLayerOptimizing(TILES_DYNAMIC_TOPMOST);
1545 return pTopmost;
1546 }
1547
1548
AddTopmostToHead(const UINT32 iMapIndex,const UINT16 usIndex)1549 LEVELNODE* AddTopmostToHead(const UINT32 iMapIndex, const UINT16 usIndex)
1550 {
1551 LEVELNODE* const n = CreateLevelNode();
1552 n->usIndex = usIndex;
1553
1554 // Prepend node to list
1555 LEVELNODE** const head = &gpWorldLevelData[iMapIndex].pTopmostHead;
1556 n->pNext = *head;
1557 *head = n;
1558
1559 ResetSpecificLayerOptimizing(TILES_DYNAMIC_TOPMOST);
1560 return n;
1561 }
1562
1563
RemoveTopmost(UINT32 iMapIndex,UINT16 usIndex)1564 BOOLEAN RemoveTopmost(UINT32 iMapIndex, UINT16 usIndex)
1565 {
1566 // Look through all topmosts and remove index if found
1567 LEVELNODE* pOldTopmost = NULL;
1568 for (LEVELNODE* pTopmost = gpWorldLevelData[iMapIndex].pTopmostHead; pTopmost != NULL; pTopmost = pTopmost->pNext)
1569 {
1570 if (pTopmost->usIndex == usIndex)
1571 {
1572 // OK, set links
1573 // Check for head
1574 if (pOldTopmost == NULL)
1575 {
1576 // It's the head
1577 gpWorldLevelData[iMapIndex].pTopmostHead = pTopmost->pNext;
1578 }
1579 else
1580 {
1581 pOldTopmost->pNext = pTopmost->pNext;
1582 }
1583
1584 delete pTopmost;
1585 return TRUE;
1586 }
1587
1588 pOldTopmost = pTopmost;
1589 }
1590
1591 // Could not find it
1592 return FALSE;
1593 }
1594
1595
RemoveTopmostFromLevelNode(UINT32 iMapIndex,LEVELNODE * pNode)1596 BOOLEAN RemoveTopmostFromLevelNode(UINT32 iMapIndex, LEVELNODE* pNode)
1597 {
1598 // Look through all topmosts and remove index if found
1599 LEVELNODE* pOldTopmost = NULL;
1600 for (LEVELNODE* pTopmost = gpWorldLevelData[iMapIndex].pTopmostHead; pTopmost != NULL; pTopmost = pTopmost->pNext)
1601 {
1602 if (pTopmost == pNode)
1603 {
1604 // OK, set links
1605 // Check for head or tail
1606 if (pOldTopmost == NULL)
1607 {
1608 // It's the head
1609 gpWorldLevelData[iMapIndex].pTopmostHead = pTopmost->pNext;
1610 }
1611 else
1612 {
1613 pOldTopmost->pNext = pTopmost->pNext;
1614 }
1615
1616 delete pTopmost;
1617 return TRUE;
1618 }
1619
1620 pOldTopmost = pTopmost;
1621 }
1622
1623 // Could not find it
1624 return FALSE;
1625 }
1626
1627
RemoveAllTopmostsOfTypeRange(UINT32 iMapIndex,UINT32 fStartType,UINT32 fEndType)1628 BOOLEAN RemoveAllTopmostsOfTypeRange(UINT32 iMapIndex, UINT32 fStartType, UINT32 fEndType)
1629 {
1630 BOOLEAN fRetVal = FALSE;
1631
1632 // Look through all topmosts and Search for type
1633 for (const LEVELNODE* pTopmost = gpWorldLevelData[iMapIndex].pTopmostHead; pTopmost != NULL;)
1634 {
1635 const LEVELNODE* pOldTopmost = pTopmost;
1636 pTopmost = pTopmost->pNext;
1637
1638 if (pOldTopmost->usIndex != NO_TILE && pOldTopmost->usIndex < NUMBEROFTILES)
1639 {
1640 const UINT32 fTileType = GetTileType(pOldTopmost->usIndex);
1641 if (fTileType >= fStartType && fTileType <= fEndType)
1642 {
1643 // Remove Item
1644 RemoveTopmost(iMapIndex, pOldTopmost->usIndex);
1645 fRetVal = TRUE;
1646 }
1647 }
1648 }
1649 return fRetVal;
1650 }
1651
1652
FindTypeInTopmostLayer(UINT32 const map_idx,UINT32 const type)1653 LEVELNODE* FindTypeInTopmostLayer(UINT32 const map_idx, UINT32 const type)
1654 {
1655 return FindTypeInLayer(gpWorldLevelData[map_idx].pTopmostHead, type);
1656 }
1657
1658
IsHeigherLevel(INT16 sGridNo)1659 BOOLEAN IsHeigherLevel(INT16 sGridNo)
1660 {
1661 const STRUCTURE* pStructure = FindStructure(sGridNo, STRUCTURE_NORMAL_ROOF);
1662 return pStructure != NULL;
1663 }
1664
1665
IsRoofVisible(INT16 sMapPos)1666 BOOLEAN IsRoofVisible(INT16 sMapPos)
1667 {
1668 if (gfBasement) return TRUE;
1669
1670 const STRUCTURE* pStructure = FindStructure(sMapPos, STRUCTURE_ROOF);
1671 return
1672 pStructure != NULL &&
1673 !(gpWorldLevelData[sMapPos].uiFlags & MAPELEMENT_REVEALED);
1674 }
1675
1676
IsRoofVisible2(INT16 sMapPos)1677 BOOLEAN IsRoofVisible2(INT16 sMapPos)
1678 {
1679 if (!gfBasement)
1680 {
1681 const STRUCTURE* pStructure = FindStructure(sMapPos, STRUCTURE_ROOF);
1682 if (pStructure == NULL) return FALSE;
1683 }
1684
1685 return !(gpWorldLevelData[sMapPos].uiFlags & MAPELEMENT_REVEALED);
1686 }
1687
1688
WhoIsThere2(INT16 const gridno,INT8 const level)1689 SOLDIERTYPE* WhoIsThere2(INT16 const gridno, INT8 const level)
1690 {
1691 if (!GridNoOnVisibleWorldTile(gridno)) return NULL;
1692
1693 for (STRUCTURE const* structure = gpWorldLevelData[gridno].pStructureHead; structure; structure = structure->pNext)
1694 {
1695 if (!(structure->fFlags & STRUCTURE_PERSON)) continue;
1696
1697 SOLDIERTYPE& tgt = GetMan(structure->usStructureID);
1698 // person must either have their pSoldier->sGridNo here or be non-passable
1699 if (structure->fFlags & STRUCTURE_PASSABLE && tgt.sGridNo != gridno) continue;
1700
1701 if ((level == 0 && structure->sCubeOffset == 0) ||
1702 (level > 0 && structure->sCubeOffset > 0))
1703 {
1704 // found a person, on the right level!
1705 // structure ID and merc ID are identical for merc structures
1706 return &tgt;
1707 }
1708 }
1709
1710 return NULL;
1711 }
1712
1713
GetTerrainType(GridNo const grid_no)1714 UINT8 GetTerrainType(GridNo const grid_no)
1715 {
1716 return gpWorldLevelData[grid_no].ubTerrainID;
1717 }
1718
1719
Water(GridNo const grid_no)1720 bool Water(GridNo const grid_no)
1721 {
1722 if (grid_no == NOWHERE) return false;
1723
1724 UINT8 const terrain = GetTerrainType(grid_no);
1725 return
1726 terrain == LOW_WATER ||
1727 terrain == MED_WATER ||
1728 terrain == DEEP_WATER;
1729 }
1730
1731
DeepWater(GridNo const grid_no)1732 bool DeepWater(GridNo const grid_no)
1733 {
1734 return GetTerrainType(grid_no) == DEEP_WATER;
1735 }
1736
1737
WaterTooDeepForAttacks(GridNo const grid_no)1738 bool WaterTooDeepForAttacks(GridNo const grid_no)
1739 {
1740 return DeepWater(grid_no);
1741 }
1742
1743
SetStructAframeFlags(UINT32 const iMapIndex,LevelnodeFlags const uiFlags)1744 void SetStructAframeFlags(UINT32 const iMapIndex, LevelnodeFlags const uiFlags)
1745 {
1746 // Look through all Roofs and Search for type
1747 for (LEVELNODE* pStruct = gpWorldLevelData[iMapIndex].pRoofHead; pStruct != NULL;)
1748 {
1749 if ( pStruct->usIndex != NO_TILE )
1750 {
1751 if (GetTileFlags(pStruct->usIndex) & AFRAME_TILE)
1752 {
1753 pStruct->uiFlags |= uiFlags;
1754 }
1755 pStruct = pStruct->pNext; // XXX TODO0009 if pStruct->usIndex == NO_TILE this is an endless loop
1756 }
1757 }
1758 }
1759
1760
FindLevelNodeBasedOnStructure(STRUCTURE const * const s)1761 LEVELNODE* FindLevelNodeBasedOnStructure(STRUCTURE const* const s)
1762 {
1763 Assert(s->fFlags & STRUCTURE_BASE_TILE);
1764 MAP_ELEMENT const& me = gpWorldLevelData[s->sGridNo];
1765
1766 // ATE: First look on the struct layer
1767 for (LEVELNODE* i = me.pStructHead; i; i = i->pNext)
1768 {
1769 if (i->pStructureData == s) return i;
1770 }
1771
1772 // Next the roof layer
1773 for (LEVELNODE* i = me.pRoofHead; i; i = i->pNext)
1774 {
1775 if (i->pStructureData == s) return i;
1776 }
1777
1778 // Then the object layer
1779 for (LEVELNODE* i = me.pObjectHead; i; i = i->pNext)
1780 {
1781 if (i->pStructureData == s) return i;
1782 }
1783
1784 // Finally the onroof layer
1785 for (LEVELNODE* i = me.pOnRoofHead; i; i = i->pNext)
1786 {
1787 if (i->pStructureData == s) return i;
1788 }
1789
1790 throw std::logic_error("FindLevelNodeBasedOnStruct failed");
1791 }
1792
1793
FindShadow(INT16 sGridNo,UINT16 usStructIndex)1794 LEVELNODE* FindShadow(INT16 sGridNo, UINT16 usStructIndex)
1795 {
1796 if (usStructIndex < FIRSTOSTRUCT1 || usStructIndex >= FIRSTSHADOW1)
1797 {
1798 return NULL;
1799 }
1800
1801 UINT16 usShadowIndex = usStructIndex - FIRSTOSTRUCT1 + FIRSTSHADOW1;
1802 LEVELNODE* pLevelNode;
1803 for (pLevelNode = gpWorldLevelData[sGridNo].pShadowHead; pLevelNode != NULL; pLevelNode = pLevelNode->pNext)
1804 {
1805 if (pLevelNode->usIndex == usShadowIndex)
1806 {
1807 break;
1808 }
1809 }
1810 return pLevelNode;
1811 }
1812
1813
WorldHideTrees(void)1814 void WorldHideTrees(void)
1815 {
1816 FOR_EACH_WORLD_TILE(i)
1817 {
1818 for (LEVELNODE* pNode = i->pStructHead; pNode != NULL; pNode = pNode->pNext)
1819 {
1820 if (pNode->uiFlags & LEVELNODE_ANIMATION) continue;
1821 if (GetTileFlags(pNode->usIndex) & FULL3D_TILE)
1822 {
1823 pNode->uiFlags |= LEVELNODE_REVEALTREES;
1824 }
1825 }
1826 }
1827
1828 SetRenderFlags(RENDER_FLAG_FULL);
1829 }
1830
1831
WorldShowTrees(void)1832 void WorldShowTrees(void)
1833 {
1834 FOR_EACH_WORLD_TILE(i)
1835 {
1836 for (LEVELNODE* pNode = i->pStructHead; pNode != NULL; pNode = pNode->pNext)
1837 {
1838 if (pNode->uiFlags & LEVELNODE_ANIMATION) continue;
1839 if (GetTileFlags(pNode->usIndex) & FULL3D_TILE)
1840 {
1841 pNode->uiFlags &= ~LEVELNODE_REVEALTREES;
1842 }
1843 }
1844 }
1845
1846 SetRenderFlags(RENDER_FLAG_FULL);
1847 }
1848
1849
SetWallLevelnodeFlags(UINT16 const sGridNo,LevelnodeFlags const uiFlags)1850 void SetWallLevelnodeFlags(UINT16 const sGridNo, LevelnodeFlags const uiFlags)
1851 {
1852 for (LEVELNODE* pStruct = gpWorldLevelData[sGridNo].pStructHead; pStruct != NULL; pStruct = pStruct->pNext)
1853 {
1854 if (pStruct->pStructureData != NULL &&
1855 pStruct->pStructureData->fFlags & STRUCTURE_WALLSTUFF) // See if we are a wall!
1856 {
1857 pStruct->uiFlags |= uiFlags;
1858 }
1859 }
1860 }
1861
1862
RemoveWallLevelnodeFlags(UINT16 const sGridNo,LevelnodeFlags const uiFlags)1863 void RemoveWallLevelnodeFlags(UINT16 const sGridNo, LevelnodeFlags const uiFlags)
1864 {
1865 for (LEVELNODE* pStruct = gpWorldLevelData[sGridNo].pStructHead; pStruct != NULL; pStruct = pStruct->pNext)
1866 {
1867 if (pStruct->pStructureData != NULL &&
1868 pStruct->pStructureData->fFlags & STRUCTURE_WALLSTUFF) // See if we are a wall!
1869 {
1870 pStruct->uiFlags &= ~uiFlags;
1871 }
1872 }
1873 }
1874
1875
SetTreeTopStateForMap(void)1876 void SetTreeTopStateForMap(void)
1877 {
1878 if (!gGameSettings.fOptions[TOPTION_TOGGLE_TREE_TOPS])
1879 {
1880 WorldHideTrees();
1881 gTacticalStatus.uiFlags |= NOHIDE_REDUNDENCY;
1882 }
1883 else
1884 {
1885 WorldShowTrees();
1886 gTacticalStatus.uiFlags &= ~NOHIDE_REDUNDENCY;
1887 }
1888
1889 // FOR THE NEXT RENDER LOOP, RE-EVALUATE REDUNDENT TILES
1890 InvalidateWorldRedundency();
1891 }
1892