warzone2100/src/objmem.cpp

1018 lines
27 KiB
C++

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* ObjMem.c
*
* Object memory management functions.
*
*/
#include <string.h>
#include "lib/framework/frame.h"
#include "objects.h"
#include "lib/gamelib/gtime.h"
#include "lib/netplay/netplay.h"
#include "hci.h"
#include "map.h"
#include "power.h"
#include "objects.h"
#include "lib/script/script.h"
#include "scriptvals.h"
#include "scripttabs.h"
#include "scriptcb.h"
#include "mission.h"
#include "structuredef.h"
#include "structure.h"
#include "droid.h"
#include "mapgrid.h"
#include "combat.h"
#include "visibility.h"
#include "qtscript.h"
// the initial value for the object ID
#define OBJ_ID_INIT 20000
/* The id number for the next object allocated
* Each object will have a unique id number irrespective of type
*/
uint32_t unsynchObjID;
uint32_t synchObjID;
/* The lists of objects allocated */
DROID *apsDroidLists[MAX_PLAYERS];
STRUCTURE *apsStructLists[MAX_PLAYERS];
FEATURE *apsFeatureLists[MAX_PLAYERS]; ///< Only player zero is valid for features. TODO: Reduce to single list.
STRUCTURE *apsExtractorLists[MAX_PLAYERS];
FEATURE *apsOilList[1];
BASE_OBJECT *apsSensorList[1]; ///< List of sensors in the game.
/*The list of Flag Positions allocated */
FLAG_POSITION *apsFlagPosLists[MAX_PLAYERS];
/* The list of destroyed objects */
BASE_OBJECT *psDestroyedObj=NULL;
/* Forward function declarations */
#ifdef DEBUG
static void objListIntegCheck(void);
#endif
/* Initialise the object heaps */
bool objmemInitialise(void)
{
// reset the object ID number
unsynchObjID = OBJ_ID_INIT/2; // /2 so that object IDs start around OBJ_ID_INIT*8, in case that's important when loading maps.
synchObjID = OBJ_ID_INIT*4; // *4 so that object IDs start around OBJ_ID_INIT*8, in case that's important when loading maps.
return true;
}
/* Release the object heaps */
void objmemShutdown(void)
{
}
/* Remove an object from the destroyed list, finally freeing its memory
* Hopefully by this time, no pointers still refer to it! */
static bool objmemDestroy(BASE_OBJECT *psObj)
{
switch (psObj->type)
{
case OBJ_DROID:
debug(LOG_MEMORY, "freeing droid at %p", psObj);
if (!droidCheckReferences((DROID *)psObj))
{
return false;
}
break;
case OBJ_STRUCTURE:
debug(LOG_MEMORY, "freeing structure at %p", psObj);
if (!structureCheckReferences((STRUCTURE *)psObj))
{
return false;
}
break;
case OBJ_FEATURE:
debug(LOG_MEMORY, "freeing feature at %p", psObj);
break;
default:
ASSERT(!"unknown object type", "objmemDestroy: unknown object type in destroyed list at 0x%p", psObj);
}
debug(LOG_MEMORY, "BASE_OBJECT* 0x%p is freed.", psObj);
delete psObj;
return true;
}
/* General housekeeping for the object system */
void objmemUpdate(void)
{
BASE_OBJECT *psCurr, *psNext, *psPrev;
#ifdef DEBUG
// do a general validity check first
objListIntegCheck();
#endif
// tell the script system about any destroyed objects
if (psDestroyedObj != NULL)
{
scrvUpdateBasePointers();
}
/* Go through the destroyed objects list looking for objects that
were destroyed before this turn */
/* First remove the objects from the start of the list */
while (psDestroyedObj != NULL && psDestroyedObj->died <= gameTime - deltaGameTime)
{
psNext = psDestroyedObj->psNext;
if (!objmemDestroy(psDestroyedObj))
{
if (psDestroyedObj->type == OBJ_DROID)
{
debug(LOG_DEATH, "skipping %p (%s: id %u) this round, will try again later.", psDestroyedObj, ((DROID *)psDestroyedObj)->aName, ((DROID *)psDestroyedObj)->id);
return;
}
}
psDestroyedObj = psNext;
}
/* Now see if there are any further down the list
Keep track of the previous object to set its Next pointer*/
for(psCurr = psPrev = psDestroyedObj; psCurr != NULL; psCurr = psNext)
{
psNext = psCurr->psNext;
if (psCurr->died <= gameTime - deltaGameTime)
{
objmemDestroy(psCurr);
/*set the linked list up - you will never be deleting the top
of the list, so don't have to check*/
psPrev->psNext = psNext;
}
else
{
// do the object died callback
psCBObjDestroyed = psCurr;
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_OBJ_DESTROYED);
switch (psCurr->type)
{
case OBJ_DROID:
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_DROID_DESTROYED);
break;
case OBJ_STRUCTURE:
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_STRUCT_DESTROYED);
break;
case OBJ_FEATURE:
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_FEATURE_DESTROYED);
break;
default:
break;
}
psCBObjDestroyed = NULL;
triggerEventDestroyed(psCurr);
psPrev = psCurr;
}
}
}
uint32_t generateNewObjectId(void)
{
// 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.
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.
}
uint32_t generateSynchronisedObjectId(void)
{
// Generate odd ID for synchronized objects
uint32_t ret = synchObjID++*2 + 1;
syncDebug("New objectId = %u", ret);
return ret;
}
/* Add the object to its list
* \param list is a pointer to the object list
*/
template <typename OBJECT>
static inline void addObjectToList(OBJECT *list[], OBJECT *object, int player)
{
ASSERT(object != NULL, "Invalid pointer");
// Prepend the object to the top of the list
object->psNext = list[player];
list[player] = object;
}
/* Add the object to its list
* \param list is a pointer to the object list
*/
template <typename OBJECT>
static inline void addObjectToFuncList(OBJECT *list[], OBJECT *object, int player)
{
ASSERT(object != NULL, "Invalid pointer");
ASSERT_OR_RETURN(, static_cast<OBJECT *>(object->psNextFunc) == NULL, "%s(%p) is already in a function list!", objInfo(object), object);
// Prepend the object to the top of the list
object->psNextFunc = list[player];
list[player] = object;
}
/* Move an object from the active list to the destroyed list.
* \param list is a pointer to the object list
* \param del is a pointer to the object to remove
*/
template <typename OBJECT>
static inline void destroyObject(OBJECT* list[], OBJECT* object)
{
ASSERT(object != NULL, "Invalid pointer");
// If the message to remove is the first one in the list then mark the next one as the first
if (list[object->player] == object)
{
list[object->player] = list[object->player]->psNext;
object->psNext = psDestroyedObj;
psDestroyedObj = (BASE_OBJECT *)object;
object->died = gameTime;
scriptRemoveObject(object);
return;
}
// Iterate through the list and find the item before the object to delete
OBJECT *psPrev = NULL, *psCurr;
for(psCurr = list[object->player]; (psCurr != object) && (psCurr != NULL); psCurr = psCurr->psNext)
{
psPrev = psCurr;
}
ASSERT(psCurr != NULL, "Object %s(%d) not found in list", objInfo(object), object->id);
if (psCurr != NULL)
{
// Modify the "next" pointer of the previous item to
// point to the "next" item of the item to delete.
psPrev->psNext = psCurr->psNext;
// Prepend the object to the destruction list
object->psNext = psDestroyedObj;
psDestroyedObj = object;
// Set destruction time
object->died = gameTime;
}
scriptRemoveObject(object);
}
/* Remove an object from the active list
* \param list is a pointer to the object list
* \param remove is a pointer to the object to remove
* \param type is the type of the object
*/
template <typename OBJECT>
static inline void removeObjectFromList(OBJECT *list[], OBJECT *object, int player)
{
ASSERT_OR_RETURN(, object != NULL, "Invalid pointer");
// If the message to remove is the first one in the list then mark the next one as the first
if (list[player] == object)
{
list[player] = list[player]->psNext;
return;
}
// Iterate through the list and find the item before the object to delete
OBJECT *psPrev = NULL, *psCurr;
for(psCurr = list[player]; (psCurr != object) && (psCurr != NULL); psCurr = psCurr->psNext)
{
psPrev = psCurr;
}
ASSERT_OR_RETURN(, psCurr != NULL, "Object %p not found in list", object);
// Modify the "next" pointer of the previous item to
// point to the "next" item of the item to delete.
psPrev->psNext = psCurr->psNext;
}
/* Remove an object from the relevant function list. An object can only be in one function list at a time!
* \param list is a pointer to the object list
* \param remove is a pointer to the object to remove
* \param type is the type of the object
*/
template <typename OBJECT>
static inline void removeObjectFromFuncList(OBJECT *list[], OBJECT *object, int player)
{
ASSERT_OR_RETURN(, object != NULL, "Invalid pointer");
// If the message to remove is the first one in the list then mark the next one as the first
if (list[player] == object)
{
list[player] = list[player]->psNextFunc;
object->psNextFunc = NULL;
return;
}
// Iterate through the list and find the item before the object to delete
OBJECT *psPrev = NULL, *psCurr;
for(psCurr = list[player]; psCurr != object && psCurr != NULL; psCurr = psCurr->psNextFunc)
{
psPrev = psCurr;
}
ASSERT_OR_RETURN(, psCurr != NULL, "Object %p not found in list", object);
// Modify the "next" pointer of the previous item to
// point to the "next" item of the item to delete.
psPrev->psNextFunc = psCurr->psNextFunc;
object->psNextFunc = NULL;
}
template <typename OBJECT>
static inline void releaseAllObjectsInList(OBJECT *list[])
{
// Iterate through all players' object lists
for (unsigned i = 0; i < MAX_PLAYERS; ++i)
{
// Iterate through all objects in list
OBJECT *psNext;
for (OBJECT *psCurr = list[i]; psCurr != NULL; psCurr = psNext)
{
psNext = psCurr->psNext;
// FIXME: the next call is disabled for now, yes, it will leak memory again.
// issue is with campaign games, and the swapping pointers 'trick' Pumpkin uses.
// visRemoveVisibility(psCurr);
// Release object's memory
delete psCurr;
}
list[i] = NULL;
}
}
/***************************************************************************************
*
* The actual object memory management functions for the different object types
*/
/*************************** DROID *********************************/
/* add the droid to the Droid Lists */
void addDroid(DROID *psDroidToAdd, DROID *pList[MAX_PLAYERS])
{
DROID_GROUP *psGroup;
addObjectToList(pList, psDroidToAdd, psDroidToAdd->player);
/* Whenever a droid gets added to a list other than the current list
* its died flag is set to NOT_CURRENT_LIST so that anything targetting
* it will cancel itself - HACK?! */
if (pList[psDroidToAdd->player] == apsDroidLists[psDroidToAdd->player])
{
psDroidToAdd->died = false;
if (psDroidToAdd->droidType == DROID_SENSOR)
{
addObjectToFuncList(apsSensorList, (BASE_OBJECT*)psDroidToAdd, 0);
}
// commanders have to get their group back if not already loaded
if (psDroidToAdd->droidType == DROID_COMMAND && !psDroidToAdd->psGroup)
{
psGroup = grpCreate();
psGroup->add(psDroidToAdd);
}
}
else if (pList[psDroidToAdd->player] == mission.apsDroidLists[psDroidToAdd->player])
{
if (psDroidToAdd->droidType == DROID_SENSOR)
{
addObjectToFuncList(mission.apsSensorList, (BASE_OBJECT*)psDroidToAdd, 0);
}
}
}
/* Destroy a droid */
void killDroid(DROID *psDel)
{
int i;
ASSERT( psDel->type == OBJ_DROID,
"killUnit: pointer is not a unit" );
ASSERT( psDel->player < MAX_PLAYERS,
"killUnit: invalid player for unit" );
setDroidTarget(psDel, NULL);
for (i = 0; i < DROID_MAXWEAPS; i++)
{
setDroidActionTarget(psDel, NULL, i);
}
setDroidBase(psDel, NULL);
if (psDel->droidType == DROID_SENSOR)
{
removeObjectFromFuncList(apsSensorList, (BASE_OBJECT *)psDel, 0);
}
destroyObject(apsDroidLists, psDel);
}
/* Remove all droids */
void freeAllDroids(void)
{
releaseAllObjectsInList(apsDroidLists);
}
/*Remove a single Droid from a list*/
void removeDroid(DROID *psDroidToRemove, DROID *pList[MAX_PLAYERS])
{
ASSERT( psDroidToRemove->type == OBJ_DROID,
"removeUnit: pointer is not a unit" );
ASSERT( psDroidToRemove->player < MAX_PLAYERS,
"removeUnit: invalid player for unit" );
removeObjectFromList(pList, psDroidToRemove, psDroidToRemove->player);
/* Whenever a droid is removed from the current list its died
* flag is set to NOT_CURRENT_LIST so that anything targetting
* it will cancel itself, and we know it is not really on the map. */
if (pList[psDroidToRemove->player] == apsDroidLists[psDroidToRemove->player])
{
if (psDroidToRemove->droidType == DROID_SENSOR)
{
removeObjectFromFuncList(apsSensorList, (BASE_OBJECT*)psDroidToRemove, 0);
}
psDroidToRemove->died = NOT_CURRENT_LIST;
}
else if (pList[psDroidToRemove->player] == mission.apsDroidLists[psDroidToRemove->player])
{
if (psDroidToRemove->droidType == DROID_SENSOR)
{
removeObjectFromFuncList(mission.apsSensorList, (BASE_OBJECT*)psDroidToRemove, 0);
}
}
}
/*Removes all droids that may be stored in the mission lists*/
void freeAllMissionDroids(void)
{
releaseAllObjectsInList(mission.apsDroidLists);
}
/*Removes all droids that may be stored in the limbo lists*/
void freeAllLimboDroids(void)
{
releaseAllObjectsInList(apsLimboDroids);
}
/************************** STRUCTURE *******************************/
/* add the structure to the Structure Lists */
void addStructure(STRUCTURE *psStructToAdd)
{
addObjectToList(apsStructLists, psStructToAdd, psStructToAdd->player);
if (psStructToAdd->pStructureType->pSensor
&& psStructToAdd->pStructureType->pSensor->location == LOC_TURRET)
{
addObjectToFuncList(apsSensorList, (BASE_OBJECT*)psStructToAdd, 0);
}
else if (psStructToAdd->pStructureType->type == REF_RESOURCE_EXTRACTOR)
{
addObjectToFuncList(apsExtractorLists, psStructToAdd, psStructToAdd->player);
}
}
/* Destroy a structure */
void killStruct(STRUCTURE *psBuilding)
{
int i;
ASSERT( psBuilding->type == OBJ_STRUCTURE,
"killStruct: pointer is not a droid" );
ASSERT( psBuilding->player < MAX_PLAYERS,
"killStruct: invalid player for stucture" );
if (psBuilding->pStructureType->pSensor
&& psBuilding->pStructureType->pSensor->location == LOC_TURRET)
{
removeObjectFromFuncList(apsSensorList, (BASE_OBJECT*)psBuilding, 0);
}
else if (psBuilding->pStructureType->type == REF_RESOURCE_EXTRACTOR)
{
removeObjectFromFuncList(apsExtractorLists, psBuilding, psBuilding->player);
}
for (i = 0; i < STRUCT_MAXWEAPS; i++)
{
setStructureTarget(psBuilding, NULL, i, ORIGIN_UNKNOWN);
}
if (psBuilding->pFunctionality != NULL)
{
if (StructIsFactory(psBuilding))
{
FACTORY *psFactory = &psBuilding->pFunctionality->factory;
// remove any commander from the factory
if (psFactory->psCommander != NULL)
{
assignFactoryCommandDroid(psBuilding, NULL);
}
// remove any assembly points
if (psFactory->psAssemblyPoint != NULL)
{
removeFlagPosition(psFactory->psAssemblyPoint);
psFactory->psAssemblyPoint = NULL;
}
}
else if (psBuilding->pStructureType->type == REF_REPAIR_FACILITY)
{
REPAIR_FACILITY *psRepair = &psBuilding->pFunctionality->repairFacility;
if (psRepair->psDeliveryPoint)
{
// free up repair fac stuff
removeFlagPosition(psRepair->psDeliveryPoint);
psRepair->psDeliveryPoint = NULL;
}
}
}
destroyObject(apsStructLists, psBuilding);
}
/* Remove heapall structures */
void freeAllStructs(void)
{
releaseAllObjectsInList(apsStructLists);
}
/*Remove a single Structure from a list*/
void removeStructureFromList(STRUCTURE *psStructToRemove, STRUCTURE *pList[MAX_PLAYERS])
{
ASSERT( psStructToRemove->type == OBJ_STRUCTURE,
"removeStructureFromList: pointer is not a structure" );
ASSERT( psStructToRemove->player < MAX_PLAYERS,
"removeStructureFromList: invalid player for structure" );
removeObjectFromList(pList, psStructToRemove, psStructToRemove->player);
if (psStructToRemove->pStructureType->pSensor
&& psStructToRemove->pStructureType->pSensor->location == LOC_TURRET)
{
removeObjectFromFuncList(apsSensorList, (BASE_OBJECT*)psStructToRemove, 0);
}
else if (psStructToRemove->pStructureType->type == REF_RESOURCE_EXTRACTOR)
{
removeObjectFromFuncList(apsExtractorLists, psStructToRemove, psStructToRemove->player);
}
}
/************************** FEATURE *********************************/
/* add the feature to the Feature Lists */
void addFeature(FEATURE *psFeatureToAdd)
{
addObjectToList(apsFeatureLists, psFeatureToAdd, 0);
if (psFeatureToAdd->psStats->subType == FEAT_OIL_RESOURCE)
{
addObjectToFuncList(apsOilList, psFeatureToAdd, 0);
}
}
/* Destroy a feature */
// set the player to 0 since features have player = maxplayers+1. This screws up destroyObject
// it's a bit of a hack, but hey, it works
void killFeature(FEATURE *psDel)
{
ASSERT( psDel->type == OBJ_FEATURE,
"killFeature: pointer is not a feature" );
psDel->player = 0;
destroyObject(apsFeatureLists, psDel);
if (psDel->psStats->subType == FEAT_OIL_RESOURCE)
{
removeObjectFromFuncList(apsOilList, psDel, 0);
}
}
/* Remove all features */
void freeAllFeatures(void)
{
releaseAllObjectsInList(apsFeatureLists);
}
/************************** FLAG_POSITION ********************************/
/* Create a new Flag Position */
bool createFlagPosition(FLAG_POSITION **ppsNew, UDWORD player)
{
ASSERT( player<MAX_PLAYERS, "createFlagPosition: invalid player number" );
*ppsNew = (FLAG_POSITION *)calloc(1, sizeof(FLAG_POSITION));
if (*ppsNew == NULL)
{
debug(LOG_ERROR, "Out of memory");
return false;
}
(*ppsNew)->type = POS_DELIVERY;
(*ppsNew)->player = player;
(*ppsNew)->frameNumber = 0;
(*ppsNew)->selected = false;
(*ppsNew)->coords.x = ~0;
(*ppsNew)->coords.y = ~0;
(*ppsNew)->coords.z = ~0;
return true;
}
/* add the Flag Position to the Flag Position Lists */
void addFlagPosition(FLAG_POSITION *psFlagPosToAdd)
{
ASSERT( psFlagPosToAdd != NULL,
"addFlagPosition: Invalid FlagPosition pointer" );
ASSERT(psFlagPosToAdd->coords.x != ~0, "flag has invalid position");
psFlagPosToAdd->psNext = apsFlagPosLists[psFlagPosToAdd->player];
apsFlagPosLists[psFlagPosToAdd->player] = psFlagPosToAdd;
}
/* Remove a Flag Position from the Lists */
void removeFlagPosition(FLAG_POSITION *psDel)
{
FLAG_POSITION *psPrev=NULL, *psCurr;
ASSERT( psDel != NULL,
"removeFlagPosition: Invalid Flag Position pointer" );
if (apsFlagPosLists[psDel->player] == psDel)
{
apsFlagPosLists[psDel->player] = apsFlagPosLists[psDel->player]->psNext;
free(psDel);
}
else
{
for(psCurr = apsFlagPosLists[psDel->player]; (psCurr != psDel) &&
(psCurr != NULL); psCurr = psCurr->psNext)
{
psPrev = psCurr;
}
if (psCurr != NULL)
{
psPrev->psNext = psCurr->psNext;
free(psCurr);
}
}
}
// free all flag positions
void freeAllFlagPositions(void)
{
FLAG_POSITION *psNext;
SDWORD player;
for(player=0; player<MAX_PLAYERS; player++)
{
while (apsFlagPosLists[player])
{
psNext = apsFlagPosLists[player]->psNext;
free(apsFlagPosLists[player]);
apsFlagPosLists[player] = psNext;
}
}
}
#ifdef DEBUG
// check all flag positions for duplicate delivery points
void checkFactoryFlags(void)
{
static std::vector<unsigned> factoryDeliveryPointCheck[NUM_FLAG_TYPES]; // Static to save allocations.
//check the flags
for (unsigned player = 0; player < MAX_PLAYERS; ++player)
{
//clear the check array
for (int type = 0; type < NUM_FLAG_TYPES; ++type)
{
factoryDeliveryPointCheck[type].clear();
}
FLAG_POSITION *psFlag = apsFlagPosLists[player];
while (psFlag)
{
if ((psFlag->type == POS_DELIVERY) &&//check this is attached to a unique factory
(psFlag->factoryType != REPAIR_FLAG))
{
unsigned type = psFlag->factoryType;
unsigned factory = psFlag->factoryInc;
factoryDeliveryPointCheck[type].push_back(factory);
}
psFlag = psFlag->psNext;
}
for (int type = 0; type < NUM_FLAG_TYPES; ++type)
{
std::sort(factoryDeliveryPointCheck[type].begin(), factoryDeliveryPointCheck[type].end());
ASSERT(std::unique(factoryDeliveryPointCheck[type].begin(), factoryDeliveryPointCheck[type].end()) == factoryDeliveryPointCheck[type].end(), "DUPLICATE FACTORY DELIVERY POINT FOUND");
}
}
}
#endif
/************************** OBJECT ACCESS FUNCTIONALITY ********************************/
// Find a base object from it's id
BASE_OBJECT *getBaseObjFromData(unsigned id, unsigned player, OBJECT_TYPE type)
{
BASE_OBJECT *psObj;
DROID *psTrans;
for (int i = 0; i < 3; ++i)
{
psObj = NULL;
switch (i)
{
case 0:
switch (type)
{
case OBJ_DROID: psObj = apsDroidLists[player]; break;
case OBJ_STRUCTURE: psObj = apsStructLists[player]; break;
case OBJ_FEATURE: psObj= apsFeatureLists[0];
default: break;
}
break;
case 1:
switch (type)
{
case OBJ_DROID: psObj = mission.apsDroidLists[player]; break;
case OBJ_STRUCTURE: psObj = mission.apsStructLists[player]; break;
case OBJ_FEATURE: psObj = mission.apsFeatureLists[0]; break;
default: break;
}
break;
case 2:
if (player == 0 && type == OBJ_DROID)
{
psObj = apsLimboDroids[0];
}
break;
}
while (psObj)
{
if (psObj->id == id)
{
return psObj;
}
// if transporter check any droids in the grp
if ((psObj->type == OBJ_DROID) && ((((DROID*)psObj)->droidType == DROID_TRANSPORTER || ((DROID*)psObj)->droidType == DROID_SUPERTRANSPORTER)))
{
for(psTrans = ((DROID*)psObj)->psGroup->psList; psTrans != NULL; psTrans = psTrans->psGrpNext)
{
if (psTrans->id == id)
{
return (BASE_OBJECT*)psTrans;
}
}
}
psObj = psObj->psNext;
}
}
ASSERT(false, "failed to find id %d for player %d", id, player);
return NULL;
}
// Find a base object from it's id
BASE_OBJECT *getBaseObjFromId(UDWORD id)
{
unsigned int i;
UDWORD player;
BASE_OBJECT *psObj;
DROID *psTrans;
for(i = 0; i < 7; ++i)
{
for(player = 0; player < MAX_PLAYERS; ++player)
{
switch (i)
{
case 0:
psObj=(BASE_OBJECT *)apsDroidLists[player];
break;
case 1:
psObj=(BASE_OBJECT *)apsStructLists[player];
break;
case 2:
if (player == 0)
{
psObj=(BASE_OBJECT *)apsFeatureLists[0];
}
else
{
psObj = NULL;
}
break;
case 3:
psObj=(BASE_OBJECT *)mission.apsDroidLists[player];
break;
case 4:
psObj=(BASE_OBJECT *)mission.apsStructLists[player];
break;
case 5:
if (player == 0)
{
psObj=(BASE_OBJECT *)mission.apsFeatureLists[0];
}
else
{
psObj = NULL;
}
break;
case 6:
if (player == 0)
{
psObj=(BASE_OBJECT *)apsLimboDroids[0];
}
else
{
psObj = NULL;
}
break;
default:
psObj = NULL;
break;
}
while (psObj)
{
if (psObj->id == id)
{
return psObj;
}
// if transporter check any droids in the grp
if ((psObj->type == OBJ_DROID) && ((((DROID*)psObj)->droidType == DROID_TRANSPORTER || ((DROID*)psObj)->droidType == DROID_SUPERTRANSPORTER)))
{
for(psTrans = ((DROID*)psObj)->psGroup->psList; psTrans != NULL; psTrans = psTrans->psGrpNext)
{
if (psTrans->id == id)
{
return (BASE_OBJECT*)psTrans;
}
}
}
psObj = psObj->psNext;
}
}
}
ASSERT(!"couldn't find a BASE_OBJ with ID", "getBaseObjFromId() failed for id %d", id);
return NULL;
}
UDWORD getRepairIdFromFlag(FLAG_POSITION *psFlag)
{
unsigned int i;
UDWORD player;
STRUCTURE *psObj;
REPAIR_FACILITY *psRepair;
player = psFlag->player;
//probably dont need to check mission list
for(i = 0; i < 2; ++i)
{
switch (i)
{
case 0:
psObj=(STRUCTURE *)apsStructLists[player];
break;
case 1:
psObj=(STRUCTURE *)mission.apsStructLists[player];
break;
default:
psObj = NULL;
break;
}
while (psObj)
{
if (psObj->pFunctionality)
{
if (psObj->pStructureType->type == REF_REPAIR_FACILITY)
{
//check for matching delivery point
psRepair = ((REPAIR_FACILITY *)psObj->pFunctionality);
if (psRepair->psDeliveryPoint == psFlag)
{
return psObj->id;
}
}
}
psObj = psObj->psNext;
}
}
ASSERT(!"unable to find repair id for FLAG_POSITION", "getRepairIdFromFlag() failed");
return UDWORD_MAX;
}
// check a base object exists for an ID
bool checkValidId(UDWORD id)
{
return getBaseObjFromId(id) != NULL;
}
// integrity check the lists
#ifdef DEBUG
static void objListIntegCheck(void)
{
SDWORD player;
BASE_OBJECT *psCurr;
for(player = 0; player <MAX_PLAYERS; player += 1)
{
for(psCurr = (BASE_OBJECT*)apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
ASSERT( psCurr->type == OBJ_DROID &&
(SDWORD)psCurr->player == player,
"objListIntegCheck: misplaced object in the droid list for player %d",
player );
}
}
for(player = 0; player <MAX_PLAYERS; player += 1)
{
for(psCurr = (BASE_OBJECT*)apsStructLists[player]; psCurr; psCurr=psCurr->psNext)
{
ASSERT( psCurr->type == OBJ_STRUCTURE &&
(SDWORD)psCurr->player == player,
"objListIntegCheck: misplaced %s(%p) in the structure list for player %d, is owned by %d",
objInfo(psCurr), psCurr, player, (int)psCurr->player);
}
}
for(psCurr = (BASE_OBJECT*)apsFeatureLists[0]; psCurr; psCurr=psCurr->psNext)
{
ASSERT( psCurr->type == OBJ_FEATURE,
"objListIntegCheck: misplaced object in the feature list" );
}
for (psCurr = (BASE_OBJECT*)psDestroyedObj; psCurr; psCurr = psCurr->psNext)
{
ASSERT( psCurr->died > 0, "objListIntegCheck: Object in destroyed list but not dead!" );
}
}
#endif
void objCount(int *droids, int *structures, int *features)
{
*droids = 0;
*structures = 0;
*features = 0;
for (int i = 0; i < MAX_PLAYERS; i++)
{
for (DROID *psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
{
(*droids)++;
if (psDroid->droidType == DROID_TRANSPORTER || psDroid->droidType == DROID_SUPERTRANSPORTER)
{
DROID *psTrans = psDroid->psGroup->psList;
for(psTrans = psTrans->psGrpNext; psTrans != NULL; psTrans = psTrans->psGrpNext)
{
(*droids)++;
}
}
}
for (STRUCTURE *psStruct = apsStructLists[i]; psStruct; psStruct = psStruct->psNext)
{
(*structures)++;
}
}
for (FEATURE *psFeat = apsFeatureLists[0]; psFeat; psFeat = psFeat->psNext)
{
(*features)++;
}
}