1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /*
21  * ObjMem.c
22  *
23  * Object memory management functions.
24  *
25  */
26 #include <string.h>
27 
28 #include "lib/framework/frame.h"
29 #include "objects.h"
30 #include "lib/gamelib/gtime.h"
31 #include "lib/netplay/netplay.h"
32 #include "hci.h"
33 #include "map.h"
34 #include "power.h"
35 #include "objects.h"
36 #include "mission.h"
37 #include "structuredef.h"
38 #include "structure.h"
39 #include "droid.h"
40 #include "mapgrid.h"
41 #include "combat.h"
42 #include "visibility.h"
43 #include "qtscript.h"
44 
45 // the initial value for the object ID
46 #define OBJ_ID_INIT 20000
47 
48 /* The id number for the next object allocated
49  * Each object will have a unique id number irrespective of type
50  */
51 uint32_t                unsynchObjID;
52 uint32_t                synchObjID;
53 
54 /* The lists of objects allocated */
55 DROID			*apsDroidLists[MAX_PLAYERS];
56 STRUCTURE		*apsStructLists[MAX_PLAYERS];
57 FEATURE			*apsFeatureLists[MAX_PLAYERS];		///< Only player zero is valid for features. TODO: Reduce to single list.
58 STRUCTURE		*apsExtractorLists[MAX_PLAYERS];
59 FEATURE			*apsOilList[1];
60 BASE_OBJECT		*apsSensorList[1];			///< List of sensors in the game.
61 
62 /*The list of Flag Positions allocated */
63 FLAG_POSITION	*apsFlagPosLists[MAX_PLAYERS];
64 
65 /* The list of destroyed objects */
66 BASE_OBJECT		*psDestroyedObj = nullptr;
67 
68 /* Forward function declarations */
69 #ifdef DEBUG
70 static void objListIntegCheck();
71 #endif
72 
73 
74 /* Initialise the object heaps */
objmemInitialise()75 bool objmemInitialise()
76 {
77 	// reset the object ID number
78 	unsynchObjID = OBJ_ID_INIT / 2; // /2 so that object IDs start around OBJ_ID_INIT*8, in case that's important when loading maps.
79 	synchObjID   = OBJ_ID_INIT * 4; // *4 so that object IDs start around OBJ_ID_INIT*8, in case that's important when loading maps.
80 
81 	return true;
82 }
83 
84 /* Release the object heaps */
objmemShutdown()85 void objmemShutdown()
86 {
87 }
88 
89 // Check that psVictim is not referred to by any other object in the game. We can dump out some extra data in debug builds that help track down sources of dangling pointer errors.
90 #ifdef DEBUG
91 #define BADREF(func, line) "Illegal reference to object %d from %s line %d", psVictim->id, func, line
92 #else
93 #define BADREF(func, line) "Illegal reference to object %d", psVictim->id
94 #endif
checkReferences(BASE_OBJECT * psVictim)95 static bool checkReferences(BASE_OBJECT *psVictim)
96 {
97 	for (int plr = 0; plr < MAX_PLAYERS; ++plr)
98 	{
99 		for (STRUCTURE *psStruct = apsStructLists[plr]; psStruct != nullptr; psStruct = psStruct->psNext)
100 		{
101 			if (psStruct == psVictim)
102 			{
103 				continue;  // Don't worry about self references.
104 			}
105 
106 			for (unsigned i = 0; i < psStruct->numWeaps; ++i)
107 			{
108 				ASSERT_OR_RETURN(false, psStruct->psTarget[i] != psVictim, BADREF(psStruct->targetFunc[i], psStruct->targetLine[i]));
109 			}
110 		}
111 		for (DROID *psDroid = apsDroidLists[plr]; psDroid != nullptr; psDroid = psDroid->psNext)
112 		{
113 			if (psDroid == psVictim)
114 			{
115 				continue;  // Don't worry about self references.
116 			}
117 
118 			ASSERT_OR_RETURN(false, psDroid->order.psObj != psVictim, "Illegal reference to object %d", psVictim->id);
119 
120 			ASSERT_OR_RETURN(false, psDroid->psBaseStruct != psVictim, "Illegal reference to object %d", psVictim->id);
121 
122 			for (unsigned i = 0; i < psDroid->numWeaps; ++i)
123 			{
124 				if (psDroid->psActionTarget[i] == psVictim)
125 				{
126 					ASSERT_OR_RETURN(false, psDroid->psActionTarget[i] != psVictim, BADREF(psDroid->actionTargetFunc[i], psDroid->actionTargetLine[i]));
127 				}
128 			}
129 		}
130 	}
131 	return true;
132 }
133 
134 /* Remove an object from the destroyed list, finally freeing its memory
135  * Hopefully by this time, no pointers still refer to it! */
objmemDestroy(BASE_OBJECT * psObj)136 static bool objmemDestroy(BASE_OBJECT *psObj)
137 {
138 	switch (psObj->type)
139 	{
140 	case OBJ_DROID:
141 		debug(LOG_MEMORY, "freeing droid at %p", static_cast<void *>(psObj));
142 		break;
143 
144 	case OBJ_STRUCTURE:
145 		debug(LOG_MEMORY, "freeing structure at %p", static_cast<void *>(psObj));
146 		break;
147 
148 	case OBJ_FEATURE:
149 		debug(LOG_MEMORY, "freeing feature at %p", static_cast<void *>(psObj));
150 		break;
151 
152 	default:
153 		ASSERT(!"unknown object type", "unknown object type in destroyed list at 0x%p", static_cast<void *>(psObj));
154 	}
155 	if (!checkReferences(psObj))
156 	{
157 		return false;
158 	}
159 	debug(LOG_MEMORY, "BASE_OBJECT* 0x%p is freed.", static_cast<void *>(psObj));
160 	delete psObj;
161 	return true;
162 }
163 
164 /* General housekeeping for the object system */
objmemUpdate()165 void objmemUpdate()
166 {
167 	BASE_OBJECT		*psCurr, *psNext, *psPrev;
168 
169 #ifdef DEBUG
170 	// do a general validity check first
171 	objListIntegCheck();
172 #endif
173 
174 	/* Go through the destroyed objects list looking for objects that
175 	   were destroyed before this turn */
176 
177 	/* First remove the objects from the start of the list */
178 	while (psDestroyedObj != nullptr && psDestroyedObj->died <= gameTime - deltaGameTime)
179 	{
180 		psNext = psDestroyedObj->psNext;
181 		objmemDestroy(psDestroyedObj);
182 		psDestroyedObj = psNext;
183 	}
184 
185 	/* Now see if there are any further down the list
186 	Keep track of the previous object to set its Next pointer*/
187 	for (psCurr = psPrev = psDestroyedObj; psCurr != nullptr; psCurr = psNext)
188 	{
189 		psNext = psCurr->psNext;
190 		if (psCurr->died <= gameTime - deltaGameTime)
191 		{
192 			/*set the linked list up - you will never be deleting the top
193 			of the list, so don't have to check*/
194 			psPrev->psNext = psNext;
195 			objmemDestroy(psCurr);
196 		}
197 		else
198 		{
199 			// do the object died callback
200 			triggerEventDestroyed(psCurr);
201 			psPrev = psCurr;
202 		}
203 	}
204 }
205 
generateNewObjectId()206 uint32_t generateNewObjectId()
207 {
208 	// Generate even ID for unsynchronized objects. This is needed for debug objects, templates and other border lines cases that should preferably be removed one day.
209 	return unsynchObjID++*MAX_PLAYERS * 2 + selectedPlayer * 2; // Was taken from createObject, where 'player' was used instead of 'selectedPlayer'. Hope there are no stupid hacks that try to recover 'player' from the last 3 bits.
210 }
211 
generateSynchronisedObjectId()212 uint32_t generateSynchronisedObjectId()
213 {
214 	// Generate odd ID for synchronized objects
215 	uint32_t ret = synchObjID++ * 2 + 1;
216 	syncDebug("New objectId = %u", ret);
217 	return ret;
218 }
219 
220 /* Add the object to its list
221  * \param list is a pointer to the object list
222  */
223 template <typename OBJECT>
addObjectToList(OBJECT * list[],OBJECT * object,int player)224 static inline void addObjectToList(OBJECT *list[], OBJECT *object, int player)
225 {
226 	ASSERT_OR_RETURN(, object != nullptr, "Invalid pointer");
227 
228 	// Prepend the object to the top of the list
229 	object->psNext = list[player];
230 	list[player] = object;
231 }
232 
233 /* Add the object to its list
234  * \param list is a pointer to the object list
235  */
236 template <typename OBJECT>
addObjectToFuncList(OBJECT * list[],OBJECT * object,int player)237 static inline void addObjectToFuncList(OBJECT *list[], OBJECT *object, int player)
238 {
239 	ASSERT_OR_RETURN(, object != nullptr, "Invalid pointer");
240 	ASSERT_OR_RETURN(, static_cast<OBJECT *>(object->psNextFunc) == nullptr, "%s(%p) is already in a function list!", objInfo(object), static_cast<void *>(object));
241 
242 	// Prepend the object to the top of the list
243 	object->psNextFunc = list[player];
244 	list[player] = object;
245 }
246 
247 /* Move an object from the active list to the destroyed list.
248  * \param list is a pointer to the object list
249  * \param del is a pointer to the object to remove
250  */
251 template <typename OBJECT>
destroyObject(OBJECT * list[],OBJECT * object)252 static inline void destroyObject(OBJECT *list[], OBJECT *object)
253 {
254 	ASSERT_OR_RETURN(, object != nullptr, "Invalid pointer");
255 	ASSERT(gameTime - deltaGameTime <= gameTime || gameTime == 2, "Expected %u <= %u, bad time", gameTime - deltaGameTime, gameTime);
256 
257 	// If the message to remove is the first one in the list then mark the next one as the first
258 	if (list[object->player] == object)
259 	{
260 		list[object->player] = list[object->player]->psNext;
261 		object->psNext = psDestroyedObj;
262 		psDestroyedObj = (BASE_OBJECT *)object;
263 		object->died = gameTime;
264 		scriptRemoveObject(object);
265 		return;
266 	}
267 
268 	// Iterate through the list and find the item before the object to delete
269 	OBJECT *psPrev = nullptr, *psCurr;
270 	for (psCurr = list[object->player]; (psCurr != object) && (psCurr != nullptr); psCurr = psCurr->psNext)
271 	{
272 		psPrev = psCurr;
273 	}
274 
275 	ASSERT(psCurr != nullptr, "Object %s(%d) not found in list", objInfo(object), object->id);
276 
277 	if (psCurr != nullptr)
278 	{
279 		// Modify the "next" pointer of the previous item to
280 		// point to the "next" item of the item to delete.
281 		psPrev->psNext = psCurr->psNext;
282 
283 		// Prepend the object to the destruction list
284 		object->psNext = psDestroyedObj;
285 		psDestroyedObj = object;
286 
287 		// Set destruction time
288 		object->died = gameTime;
289 	}
290 	scriptRemoveObject(object);
291 }
292 
293 /* Remove an object from the active list
294  * \param list is a pointer to the object list
295  * \param remove is a pointer to the object to remove
296  * \param type is the type of the object
297  */
298 template <typename OBJECT>
removeObjectFromList(OBJECT * list[],OBJECT * object,int player)299 static inline void removeObjectFromList(OBJECT *list[], OBJECT *object, int player)
300 {
301 	ASSERT_OR_RETURN(, object != nullptr, "Invalid pointer");
302 
303 	// If the message to remove is the first one in the list then mark the next one as the first
304 	if (list[player] == object)
305 	{
306 		list[player] = list[player]->psNext;
307 		return;
308 	}
309 
310 	// Iterate through the list and find the item before the object to delete
311 	OBJECT *psPrev = nullptr, *psCurr;
312 	for (psCurr = list[player]; (psCurr != object) && (psCurr != nullptr); psCurr = psCurr->psNext)
313 	{
314 		psPrev = psCurr;
315 	}
316 
317 	ASSERT_OR_RETURN(, psCurr != nullptr, "Object %p not found in list", static_cast<void *>(object));
318 
319 	// Modify the "next" pointer of the previous item to
320 	// point to the "next" item of the item to delete.
321 	psPrev->psNext = psCurr->psNext;
322 }
323 
324 /* Remove an object from the relevant function list. An object can only be in one function list at a time!
325  * \param list is a pointer to the object list
326  * \param remove is a pointer to the object to remove
327  * \param type is the type of the object
328  */
329 template <typename OBJECT>
removeObjectFromFuncList(OBJECT * list[],OBJECT * object,int player)330 static inline void removeObjectFromFuncList(OBJECT *list[], OBJECT *object, int player)
331 {
332 	ASSERT_OR_RETURN(, object != nullptr, "Invalid pointer");
333 
334 	// If the message to remove is the first one in the list then mark the next one as the first
335 	if (list[player] == object)
336 	{
337 		list[player] = list[player]->psNextFunc;
338 		object->psNextFunc = nullptr;
339 		return;
340 	}
341 
342 	// Iterate through the list and find the item before the object to delete
343 	OBJECT *psPrev = nullptr, *psCurr;
344 	for (psCurr = list[player]; psCurr != object && psCurr != nullptr; psCurr = psCurr->psNextFunc)
345 	{
346 		psPrev = psCurr;
347 	}
348 
349 	ASSERT_OR_RETURN(, psCurr != nullptr, "Object %p not found in list", static_cast<void *>(object));
350 
351 	// Modify the "next" pointer of the previous item to
352 	// point to the "next" item of the item to delete.
353 	psPrev->psNextFunc = psCurr->psNextFunc;
354 	object->psNextFunc = nullptr;
355 }
356 
357 template <typename OBJECT>
releaseAllObjectsInList(OBJECT * list[])358 static inline void releaseAllObjectsInList(OBJECT *list[])
359 {
360 	// Iterate through all players' object lists
361 	for (unsigned i = 0; i < MAX_PLAYERS; ++i)
362 	{
363 		// Iterate through all objects in list
364 		OBJECT *psNext;
365 		for (OBJECT *psCurr = list[i]; psCurr != nullptr; psCurr = psNext)
366 		{
367 			psNext = psCurr->psNext;
368 
369 			// FIXME: the next call is disabled for now, yes, it will leak memory again.
370 			// issue is with campaign games, and the swapping pointers 'trick' Pumpkin uses.
371 			//	visRemoveVisibility(psCurr);
372 			// Release object's memory
373 			delete psCurr;
374 		}
375 		list[i] = nullptr;
376 	}
377 }
378 
379 /***************************************************************************************
380  *
381  * The actual object memory management functions for the different object types
382  */
383 
384 /***************************  DROID  *********************************/
385 
386 /* add the droid to the Droid Lists */
addDroid(DROID * psDroidToAdd,DROID * pList[MAX_PLAYERS])387 void addDroid(DROID *psDroidToAdd, DROID *pList[MAX_PLAYERS])
388 {
389 	DROID_GROUP	*psGroup;
390 
391 	addObjectToList(pList, psDroidToAdd, psDroidToAdd->player);
392 
393 	/* Whenever a droid gets added to a list other than the current list
394 	 * its died flag is set to NOT_CURRENT_LIST so that anything targetting
395 	 * it will cancel itself - HACK?! */
396 	if (pList[psDroidToAdd->player] == apsDroidLists[psDroidToAdd->player])
397 	{
398 		psDroidToAdd->died = false;
399 		if (psDroidToAdd->droidType == DROID_SENSOR)
400 		{
401 			addObjectToFuncList(apsSensorList, (BASE_OBJECT *)psDroidToAdd, 0);
402 		}
403 
404 		// commanders have to get their group back if not already loaded
405 		if (psDroidToAdd->droidType == DROID_COMMAND && !psDroidToAdd->psGroup)
406 		{
407 			psGroup = grpCreate();
408 			psGroup->add(psDroidToAdd);
409 		}
410 	}
411 	else if (pList[psDroidToAdd->player] == mission.apsDroidLists[psDroidToAdd->player])
412 	{
413 		if (psDroidToAdd->droidType == DROID_SENSOR)
414 		{
415 			addObjectToFuncList(mission.apsSensorList, (BASE_OBJECT *)psDroidToAdd, 0);
416 		}
417 	}
418 }
419 
420 /* Destroy a droid */
killDroid(DROID * psDel)421 void killDroid(DROID *psDel)
422 {
423 	int i;
424 
425 	ASSERT(psDel->type == OBJ_DROID,
426 	       "killUnit: pointer is not a unit");
427 	ASSERT(psDel->player < MAX_PLAYERS,
428 	       "killUnit: invalid player for unit");
429 
430 	setDroidTarget(psDel, nullptr);
431 	for (i = 0; i < MAX_WEAPONS; i++)
432 	{
433 		setDroidActionTarget(psDel, nullptr, i);
434 	}
435 	setDroidBase(psDel, nullptr);
436 	if (psDel->droidType == DROID_SENSOR)
437 	{
438 		removeObjectFromFuncList(apsSensorList, (BASE_OBJECT *)psDel, 0);
439 	}
440 
441 	destroyObject(apsDroidLists, psDel);
442 }
443 
444 /* Remove all droids */
freeAllDroids()445 void freeAllDroids()
446 {
447 	releaseAllObjectsInList(apsDroidLists);
448 }
449 
450 /*Remove a single Droid from a list*/
removeDroid(DROID * psDroidToRemove,DROID * pList[MAX_PLAYERS])451 void removeDroid(DROID *psDroidToRemove, DROID *pList[MAX_PLAYERS])
452 {
453 	ASSERT_OR_RETURN(, psDroidToRemove->type == OBJ_DROID, "Pointer is not a unit");
454 	ASSERT_OR_RETURN(, psDroidToRemove->player < MAX_PLAYERS, "Invalid player for unit");
455 	removeObjectFromList(pList, psDroidToRemove, psDroidToRemove->player);
456 
457 	/* Whenever a droid is removed from the current list its died
458 	 * flag is set to NOT_CURRENT_LIST so that anything targetting
459 	 * it will cancel itself, and we know it is not really on the map. */
460 	if (pList[psDroidToRemove->player] == apsDroidLists[psDroidToRemove->player])
461 	{
462 		if (psDroidToRemove->droidType == DROID_SENSOR)
463 		{
464 			removeObjectFromFuncList(apsSensorList, (BASE_OBJECT *)psDroidToRemove, 0);
465 		}
466 		psDroidToRemove->died = NOT_CURRENT_LIST;
467 	}
468 	else if (pList[psDroidToRemove->player] == mission.apsDroidLists[psDroidToRemove->player])
469 	{
470 		if (psDroidToRemove->droidType == DROID_SENSOR)
471 		{
472 			removeObjectFromFuncList(mission.apsSensorList, (BASE_OBJECT *)psDroidToRemove, 0);
473 		}
474 	}
475 }
476 
477 /*Removes all droids that may be stored in the mission lists*/
freeAllMissionDroids()478 void freeAllMissionDroids()
479 {
480 	releaseAllObjectsInList(mission.apsDroidLists);
481 }
482 
483 /*Removes all droids that may be stored in the limbo lists*/
freeAllLimboDroids()484 void freeAllLimboDroids()
485 {
486 	releaseAllObjectsInList(apsLimboDroids);
487 }
488 
489 /**************************  STRUCTURE  *******************************/
490 
491 /* add the structure to the Structure Lists */
addStructure(STRUCTURE * psStructToAdd)492 void addStructure(STRUCTURE *psStructToAdd)
493 {
494 	addObjectToList(apsStructLists, psStructToAdd, psStructToAdd->player);
495 	if (psStructToAdd->pStructureType->pSensor
496 	    && psStructToAdd->pStructureType->pSensor->location == LOC_TURRET)
497 	{
498 		addObjectToFuncList(apsSensorList, (BASE_OBJECT *)psStructToAdd, 0);
499 	}
500 	else if (psStructToAdd->pStructureType->type == REF_RESOURCE_EXTRACTOR)
501 	{
502 		addObjectToFuncList(apsExtractorLists, psStructToAdd, psStructToAdd->player);
503 	}
504 }
505 
506 /* Destroy a structure */
killStruct(STRUCTURE * psBuilding)507 void killStruct(STRUCTURE *psBuilding)
508 {
509 	int i;
510 
511 	ASSERT(psBuilding->type == OBJ_STRUCTURE,
512 	       "killStruct: pointer is not a droid");
513 	ASSERT(psBuilding->player < MAX_PLAYERS,
514 	       "killStruct: invalid player for stucture");
515 
516 	if (psBuilding->pStructureType->pSensor
517 	    && psBuilding->pStructureType->pSensor->location == LOC_TURRET)
518 	{
519 		removeObjectFromFuncList(apsSensorList, (BASE_OBJECT *)psBuilding, 0);
520 	}
521 	else if (psBuilding->pStructureType->type == REF_RESOURCE_EXTRACTOR)
522 	{
523 		removeObjectFromFuncList(apsExtractorLists, psBuilding, psBuilding->player);
524 	}
525 
526 	for (i = 0; i < MAX_WEAPONS; i++)
527 	{
528 		setStructureTarget(psBuilding, nullptr, i, ORIGIN_UNKNOWN);
529 	}
530 
531 	if (psBuilding->pFunctionality != nullptr)
532 	{
533 		if (StructIsFactory(psBuilding))
534 		{
535 			FACTORY *psFactory = &psBuilding->pFunctionality->factory;
536 
537 			// remove any commander from the factory
538 			if (psFactory->psCommander != nullptr)
539 			{
540 				assignFactoryCommandDroid(psBuilding, nullptr);
541 			}
542 
543 			// remove any assembly points
544 			if (psFactory->psAssemblyPoint != nullptr)
545 			{
546 				removeFlagPosition(psFactory->psAssemblyPoint);
547 				psFactory->psAssemblyPoint = nullptr;
548 			}
549 		}
550 		else if (psBuilding->pStructureType->type == REF_REPAIR_FACILITY)
551 		{
552 			REPAIR_FACILITY *psRepair = &psBuilding->pFunctionality->repairFacility;
553 
554 			if (psRepair->psDeliveryPoint)
555 			{
556 				// free up repair fac stuff
557 				removeFlagPosition(psRepair->psDeliveryPoint);
558 				psRepair->psDeliveryPoint = nullptr;
559 			}
560 		}
561 	}
562 
563 	destroyObject(apsStructLists, psBuilding);
564 }
565 
566 /* Remove heapall structures */
freeAllStructs()567 void freeAllStructs()
568 {
569 	releaseAllObjectsInList(apsStructLists);
570 }
571 
572 /*Remove a single Structure from a list*/
removeStructureFromList(STRUCTURE * psStructToRemove,STRUCTURE * pList[MAX_PLAYERS])573 void removeStructureFromList(STRUCTURE *psStructToRemove, STRUCTURE *pList[MAX_PLAYERS])
574 {
575 	ASSERT(psStructToRemove->type == OBJ_STRUCTURE,
576 	       "removeStructureFromList: pointer is not a structure");
577 	ASSERT(psStructToRemove->player < MAX_PLAYERS,
578 	       "removeStructureFromList: invalid player for structure");
579 	removeObjectFromList(pList, psStructToRemove, psStructToRemove->player);
580 	if (psStructToRemove->pStructureType->pSensor
581 	    && psStructToRemove->pStructureType->pSensor->location == LOC_TURRET)
582 	{
583 		removeObjectFromFuncList(apsSensorList, (BASE_OBJECT *)psStructToRemove, 0);
584 	}
585 	else if (psStructToRemove->pStructureType->type == REF_RESOURCE_EXTRACTOR)
586 	{
587 		removeObjectFromFuncList(apsExtractorLists, psStructToRemove, psStructToRemove->player);
588 	}
589 }
590 
591 /**************************  FEATURE  *********************************/
592 
593 /* add the feature to the Feature Lists */
addFeature(FEATURE * psFeatureToAdd)594 void addFeature(FEATURE *psFeatureToAdd)
595 {
596 	addObjectToList(apsFeatureLists, psFeatureToAdd, 0);
597 	if (psFeatureToAdd->psStats->subType == FEAT_OIL_RESOURCE)
598 	{
599 		addObjectToFuncList(apsOilList, psFeatureToAdd, 0);
600 	}
601 }
602 
603 /* Destroy a feature */
604 // set the player to 0 since features have player = maxplayers+1. This screws up destroyObject
605 // it's a bit of a hack, but hey, it works
killFeature(FEATURE * psDel)606 void killFeature(FEATURE *psDel)
607 {
608 	ASSERT(psDel->type == OBJ_FEATURE,
609 	       "killFeature: pointer is not a feature");
610 	psDel->player = 0;
611 	destroyObject(apsFeatureLists, psDel);
612 
613 	if (psDel->psStats->subType == FEAT_OIL_RESOURCE)
614 	{
615 		removeObjectFromFuncList(apsOilList, psDel, 0);
616 	}
617 }
618 
619 /* Remove all features */
freeAllFeatures()620 void freeAllFeatures()
621 {
622 	releaseAllObjectsInList(apsFeatureLists);
623 }
624 
625 /**************************  FLAG_POSITION ********************************/
626 
627 /* Create a new Flag Position */
createFlagPosition(FLAG_POSITION ** ppsNew,UDWORD player)628 bool createFlagPosition(FLAG_POSITION **ppsNew, UDWORD player)
629 {
630 	ASSERT(player < MAX_PLAYERS, "createFlagPosition: invalid player number");
631 
632 	*ppsNew = (FLAG_POSITION *)calloc(1, sizeof(FLAG_POSITION));
633 	if (*ppsNew == nullptr)
634 	{
635 		debug(LOG_ERROR, "Out of memory");
636 		return false;
637 	}
638 	(*ppsNew)->type = POS_DELIVERY;
639 	(*ppsNew)->player = player;
640 	(*ppsNew)->frameNumber = 0;
641 	(*ppsNew)->selected = false;
642 	(*ppsNew)->coords.x = ~0;
643 	(*ppsNew)->coords.y = ~0;
644 	(*ppsNew)->coords.z = ~0;
645 	return true;
646 }
647 
isFlagPositionInList(FLAG_POSITION * psFlagPosToAdd)648 static bool isFlagPositionInList(FLAG_POSITION *psFlagPosToAdd)
649 {
650 	ASSERT_OR_RETURN(false, psFlagPosToAdd != nullptr, "Invalid FlagPosition pointer");
651 	ASSERT_OR_RETURN(false, psFlagPosToAdd->player < MAX_PLAYERS, "Invalid FlagPosition player: %u", psFlagPosToAdd->player);
652 	for (FLAG_POSITION* psCurr = apsFlagPosLists[psFlagPosToAdd->player]; (psCurr != nullptr); psCurr = psCurr->psNext)
653 	{
654 		if (psCurr == psFlagPosToAdd)
655 		{
656 			return true;
657 		}
658 	}
659 	return false;
660 }
661 
662 /* add the Flag Position to the Flag Position Lists */
addFlagPosition(FLAG_POSITION * psFlagPosToAdd)663 void addFlagPosition(FLAG_POSITION *psFlagPosToAdd)
664 {
665 	ASSERT_OR_RETURN(, psFlagPosToAdd != nullptr, "Invalid FlagPosition pointer");
666 	ASSERT_OR_RETURN(, psFlagPosToAdd->coords.x != ~0, "flag has invalid position");
667 	ASSERT_OR_RETURN(, psFlagPosToAdd->player < MAX_PLAYERS, "Invalid FlagPosition player: %u", psFlagPosToAdd->player);
668 	ASSERT_OR_RETURN(, !isFlagPositionInList(psFlagPosToAdd), "FlagPosition is already in the list!");
669 
670 	psFlagPosToAdd->psNext = apsFlagPosLists[psFlagPosToAdd->player];
671 	apsFlagPosLists[psFlagPosToAdd->player] = psFlagPosToAdd;
672 }
673 
674 /* Remove a Flag Position from the Lists */
removeFlagPosition(FLAG_POSITION * psDel)675 void removeFlagPosition(FLAG_POSITION *psDel)
676 {
677 	FLAG_POSITION		*psPrev = nullptr, *psCurr;
678 
679 	ASSERT_OR_RETURN(, psDel != nullptr, "Invalid Flag Position pointer");
680 
681 	if (apsFlagPosLists[psDel->player] == psDel)
682 	{
683 		apsFlagPosLists[psDel->player] = apsFlagPosLists[psDel->player]->psNext;
684 		free(psDel);
685 	}
686 	else
687 	{
688 		for (psCurr = apsFlagPosLists[psDel->player]; (psCurr != psDel) &&
689 		     (psCurr != nullptr); psCurr = psCurr->psNext)
690 		{
691 			psPrev = psCurr;
692 		}
693 		if (psCurr != nullptr)
694 		{
695 			psPrev->psNext = psCurr->psNext;
696 			free(psCurr);
697 		}
698 	}
699 }
700 
701 
702 // free all flag positions
freeAllFlagPositions()703 void freeAllFlagPositions()
704 {
705 	FLAG_POSITION	*psNext;
706 	SDWORD			player;
707 
708 	for (player = 0; player < MAX_PLAYERS; player++)
709 	{
710 		while (apsFlagPosLists[player])
711 		{
712 			psNext = apsFlagPosLists[player]->psNext;
713 			free(apsFlagPosLists[player]);
714 			apsFlagPosLists[player] = psNext;
715 		}
716 	}
717 }
718 
719 
720 #ifdef DEBUG
721 // check all flag positions for duplicate delivery points
checkFactoryFlags()722 void checkFactoryFlags()
723 {
724 	static std::vector<unsigned> factoryDeliveryPointCheck[NUM_FLAG_TYPES];  // Static to save allocations.
725 
726 	//check the flags
727 	for (unsigned player = 0; player < MAX_PLAYERS; ++player)
728 	{
729 		//clear the check array
730 		for (int type = 0; type < NUM_FLAG_TYPES; ++type)
731 		{
732 			factoryDeliveryPointCheck[type].clear();
733 		}
734 
735 		FLAG_POSITION *psFlag = apsFlagPosLists[player];
736 		while (psFlag)
737 		{
738 			if ((psFlag->type == POS_DELIVERY) &&//check this is attached to a unique factory
739 			    (psFlag->factoryType != REPAIR_FLAG))
740 			{
741 				unsigned type = psFlag->factoryType;
742 				unsigned factory = psFlag->factoryInc;
743 				factoryDeliveryPointCheck[type].push_back(factory);
744 			}
745 			psFlag = psFlag->psNext;
746 		}
747 		for (int type = 0; type < NUM_FLAG_TYPES; ++type)
748 		{
749 			std::sort(factoryDeliveryPointCheck[type].begin(), factoryDeliveryPointCheck[type].end());
750 			ASSERT(std::unique(factoryDeliveryPointCheck[type].begin(), factoryDeliveryPointCheck[type].end()) == factoryDeliveryPointCheck[type].end(), "DUPLICATE FACTORY DELIVERY POINT FOUND");
751 		}
752 	}
753 }
754 #endif
755 
756 
757 /**************************  OBJECT ACCESS FUNCTIONALITY ********************************/
758 
759 // Find a base object from it's id
getBaseObjFromData(unsigned id,unsigned player,OBJECT_TYPE type)760 BASE_OBJECT *getBaseObjFromData(unsigned id, unsigned player, OBJECT_TYPE type)
761 {
762 	BASE_OBJECT		*psObj;
763 	DROID			*psTrans;
764 
765 	for (int i = 0; i < 3; ++i)
766 	{
767 		psObj = nullptr;
768 		switch (i)
769 		{
770 		case 0:
771 			switch (type)
772 			{
773 			case OBJ_DROID: psObj = apsDroidLists[player]; break;
774 			case OBJ_STRUCTURE: psObj = apsStructLists[player]; break;
775 			case OBJ_FEATURE: psObj = apsFeatureLists[0];
776 			default: break;
777 			}
778 			break;
779 		case 1:
780 			switch (type)
781 			{
782 			case OBJ_DROID: psObj = mission.apsDroidLists[player]; break;
783 			case OBJ_STRUCTURE: psObj = mission.apsStructLists[player]; break;
784 			case OBJ_FEATURE: psObj = mission.apsFeatureLists[0]; break;
785 			default: break;
786 			}
787 			break;
788 		case 2:
789 			if (player == 0 && type == OBJ_DROID)
790 			{
791 				psObj = apsLimboDroids[0];
792 			}
793 			break;
794 		}
795 
796 		while (psObj)
797 		{
798 			if (psObj->id == id)
799 			{
800 				return psObj;
801 			}
802 			// if transporter check any droids in the grp
803 			if ((psObj->type == OBJ_DROID) && isTransporter((DROID *)psObj))
804 			{
805 				for (psTrans = ((DROID *)psObj)->psGroup->psList; psTrans != nullptr; psTrans = psTrans->psGrpNext)
806 				{
807 					if (psTrans->id == id)
808 					{
809 						return (BASE_OBJECT *)psTrans;
810 					}
811 				}
812 			}
813 			psObj = psObj->psNext;
814 		}
815 	}
816 	ASSERT(false, "failed to find id %d for player %d", id, player);
817 
818 	return nullptr;
819 }
820 
821 // Find a base object from it's id
getBaseObjFromId(UDWORD id)822 BASE_OBJECT *getBaseObjFromId(UDWORD id)
823 {
824 	unsigned int i;
825 	UDWORD			player;
826 	BASE_OBJECT		*psObj;
827 	DROID			*psTrans;
828 
829 	for (i = 0; i < 7; ++i)
830 	{
831 		for (player = 0; player < MAX_PLAYERS; ++player)
832 		{
833 			switch (i)
834 			{
835 			case 0:
836 				psObj = (BASE_OBJECT *)apsDroidLists[player];
837 				break;
838 			case 1:
839 				psObj = (BASE_OBJECT *)apsStructLists[player];
840 				break;
841 			case 2:
842 				if (player == 0)
843 				{
844 					psObj = (BASE_OBJECT *)apsFeatureLists[0];
845 				}
846 				else
847 				{
848 					psObj = nullptr;
849 				}
850 				break;
851 			case 3:
852 				psObj = (BASE_OBJECT *)mission.apsDroidLists[player];
853 				break;
854 			case 4:
855 				psObj = (BASE_OBJECT *)mission.apsStructLists[player];
856 				break;
857 			case 5:
858 				if (player == 0)
859 				{
860 					psObj = (BASE_OBJECT *)mission.apsFeatureLists[0];
861 				}
862 				else
863 				{
864 					psObj = nullptr;
865 				}
866 				break;
867 			case 6:
868 				if (player == 0)
869 				{
870 					psObj = (BASE_OBJECT *)apsLimboDroids[0];
871 				}
872 				else
873 				{
874 					psObj = nullptr;
875 				}
876 				break;
877 			default:
878 				psObj = nullptr;
879 				break;
880 			}
881 
882 			while (psObj)
883 			{
884 				if (psObj->id == id)
885 				{
886 					return psObj;
887 				}
888 				// if transporter check any droids in the grp
889 				if ((psObj->type == OBJ_DROID) && isTransporter((DROID *)psObj))
890 				{
891 					for (psTrans = ((DROID *)psObj)->psGroup->psList; psTrans != nullptr; psTrans = psTrans->psGrpNext)
892 					{
893 						if (psTrans->id == id)
894 						{
895 							return (BASE_OBJECT *)psTrans;
896 						}
897 					}
898 				}
899 				psObj = psObj->psNext;
900 			}
901 		}
902 	}
903 	ASSERT(!"couldn't find a BASE_OBJ with ID", "getBaseObjFromId() failed for id %d", id);
904 
905 	return nullptr;
906 }
907 
getRepairIdFromFlag(FLAG_POSITION * psFlag)908 UDWORD getRepairIdFromFlag(FLAG_POSITION *psFlag)
909 {
910 	unsigned int i;
911 	UDWORD			player;
912 	STRUCTURE		*psObj;
913 	REPAIR_FACILITY	*psRepair;
914 
915 
916 	player = psFlag->player;
917 
918 	//probably don't need to check mission list
919 	for (i = 0; i < 2; ++i)
920 	{
921 		switch (i)
922 		{
923 		case 0:
924 			psObj = (STRUCTURE *)apsStructLists[player];
925 			break;
926 		case 1:
927 			psObj = (STRUCTURE *)mission.apsStructLists[player];
928 			break;
929 		default:
930 			psObj = nullptr;
931 			break;
932 		}
933 
934 		while (psObj)
935 		{
936 			if (psObj->pFunctionality)
937 			{
938 				if	(psObj->pStructureType->type == REF_REPAIR_FACILITY)
939 				{
940 					//check for matching delivery point
941 					psRepair = ((REPAIR_FACILITY *)psObj->pFunctionality);
942 					if (psRepair->psDeliveryPoint == psFlag)
943 					{
944 						return psObj->id;
945 					}
946 				}
947 			}
948 			psObj = psObj->psNext;
949 		}
950 	}
951 	ASSERT(!"unable to find repair id for FLAG_POSITION", "getRepairIdFromFlag() failed");
952 
953 	return UDWORD_MAX;
954 }
955 
956 // integrity check the lists
957 #ifdef DEBUG
objListIntegCheck()958 static void objListIntegCheck()
959 {
960 	SDWORD			player;
961 	BASE_OBJECT		*psCurr;
962 
963 	for (player = 0; player < MAX_PLAYERS; player += 1)
964 	{
965 		for (psCurr = (BASE_OBJECT *)apsDroidLists[player]; psCurr; psCurr = psCurr->psNext)
966 		{
967 			ASSERT(psCurr->type == OBJ_DROID &&
968 			       (SDWORD)psCurr->player == player,
969 			       "objListIntegCheck: misplaced object in the droid list for player %d",
970 			       player);
971 		}
972 	}
973 	for (player = 0; player < MAX_PLAYERS; player += 1)
974 	{
975 		for (psCurr = (BASE_OBJECT *)apsStructLists[player]; psCurr; psCurr = psCurr->psNext)
976 		{
977 			ASSERT(psCurr->type == OBJ_STRUCTURE &&
978 			       (SDWORD)psCurr->player == player,
979 			       "objListIntegCheck: misplaced %s(%p) in the structure list for player %d, is owned by %d",
980 			       objInfo(psCurr), psCurr, player, (int)psCurr->player);
981 		}
982 	}
983 	for (psCurr = (BASE_OBJECT *)apsFeatureLists[0]; psCurr; psCurr = psCurr->psNext)
984 	{
985 		ASSERT(psCurr->type == OBJ_FEATURE,
986 		       "objListIntegCheck: misplaced object in the feature list");
987 	}
988 	for (psCurr = (BASE_OBJECT *)psDestroyedObj; psCurr; psCurr = psCurr->psNext)
989 	{
990 		ASSERT(psCurr->died > 0, "objListIntegCheck: Object in destroyed list but not dead!");
991 	}
992 }
993 #endif
994 
objCount(int * droids,int * structures,int * features)995 void objCount(int *droids, int *structures, int *features)
996 {
997 	*droids = 0;
998 	*structures = 0;
999 	*features = 0;
1000 
1001 	for (int i = 0; i < MAX_PLAYERS; i++)
1002 	{
1003 		for (DROID *psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
1004 		{
1005 			(*droids)++;
1006 			if (isTransporter(psDroid))
1007 			{
1008 				DROID *psTrans = psDroid->psGroup->psList;
1009 
1010 				for (psTrans = psTrans->psGrpNext; psTrans != nullptr; psTrans = psTrans->psGrpNext)
1011 				{
1012 					(*droids)++;
1013 				}
1014 			}
1015 		}
1016 
1017 		for (STRUCTURE *psStruct = apsStructLists[i]; psStruct; psStruct = psStruct->psNext)
1018 		{
1019 			(*structures)++;
1020 		}
1021 	}
1022 
1023 	for (FEATURE *psFeat = apsFeatureLists[0]; psFeat; psFeat = psFeat->psNext)
1024 	{
1025 		(*features)++;
1026 	}
1027 }
1028