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