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