Add the ability of allied players to share each others' sensors. Since iterating over all structures

by all players to see if we could use each as a sensor would be rather time consuming, I created a 
new set of lists (currently with only one member) that sorts objects by function. This way we can 
very quickly iterate over all sensors, even when there are several hundred buildings on the map. 
Closes ticket:636.


git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@7765 4a71c877-e1ca-e34f-864e-861f7616d084
master
Per Inge Mathisen 2009-06-17 19:51:24 +00:00 committed by Git SVN Gateway
parent 53c81cfdc9
commit 6a683536d6
6 changed files with 210 additions and 120 deletions

153
src/ai.c
View File

@ -95,6 +95,19 @@ static BOOL aiDroidHasRange(DROID *psDroid, BASE_OBJECT *psTarget, int weapon_sl
return false;
}
static BOOL aiObjHasRange(BASE_OBJECT *psObj, BASE_OBJECT *psTarget, int weapon_slot)
{
if (psObj->type == OBJ_DROID)
{
return aiDroidHasRange((DROID *)psObj, psTarget, weapon_slot);
}
else if (psObj->type == OBJ_STRUCTURE)
{
return aiStructHasRange((STRUCTURE *)psObj, psTarget, weapon_slot);
}
return false;
}
/* alliance code for ai. return true if an alliance has formed. */
BOOL aiCheckAlliances(UDWORD s1,UDWORD s2)
{
@ -133,13 +146,73 @@ BOOL aiShutdown(void)
return true;
}
/** Search the global list of sensors for a possible target for psObj. */
BASE_OBJECT *aiSearchSensorTargets(BASE_OBJECT *psObj, int weapon_slot, WEAPON_STATS *psWStats)
{
int longRange = proj_GetLongRange(psWStats);
int tarDist = longRange * longRange;
int minDist = psWStats->minRange * psWStats->minRange;
BASE_OBJECT *psSensor, *psTarget = NULL;
for (psSensor = apsSensorList[0]; psSensor; psSensor = psSensor->psNextFunc)
{
BASE_OBJECT *psTemp = NULL;
bool isCB = false;
if (!aiCheckAlliances(psSensor->player, psObj->player))
{
continue;
}
else if (psSensor->type == OBJ_DROID)
{
DROID *psDroid = (DROID *)psSensor;
ASSERT_OR_RETURN(false, psDroid->droidType == DROID_SENSOR, "A non-sensor droid in a sensor list is non-sense");
psTemp = psDroid->psTarget;
isCB = cbSensorDroid(psDroid);
}
else if (psSensor->type == OBJ_STRUCTURE)
{
STRUCTURE *psCStruct = (STRUCTURE *)psSensor;
// skip incomplete structures
if (psCStruct->status != SS_BUILT)
{
continue;
}
psTemp = psCStruct->psTarget[0];
isCB = structCBSensor(psCStruct);
}
if (!psTemp || psTemp->died || !validTarget(psObj, psTemp, 0) || aiCheckAlliances(psTemp->player, psObj->player))
{
continue;
}
if (aiObjHasRange(psObj, psTemp, weapon_slot))
{
int distSq = objPosDiffSq(psTemp->pos, psObj->pos);
// Need to be in range, prefer closer targets or CB targets
if ((isCB || distSq < tarDist) && distSq > minDist)
{
tarDist = distSq;
psTarget = psTemp;
if (isCB)
{
break; // got CB target, drop everything and shoot!
}
}
}
}
return psTarget;
}
// Find the best nearest target for a droid
// Returns integer representing target priority, -1 if failed
SDWORD aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot)
{
UDWORD i;
SDWORD bestMod,newMod,failure=-1;
BASE_OBJECT *psTarget,*friendlyObj,*bestTarget,*targetInQuestion,*tempTarget;
SDWORD bestMod = 0,newMod, failure = -1;
BASE_OBJECT *psTarget = NULL, *friendlyObj, *bestTarget = NULL, *targetInQuestion, *tempTarget;
BOOL electronic = false;
STRUCTURE *targetStructure;
WEAPON_EFFECT weaponEffect;
@ -151,23 +224,24 @@ SDWORD aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot
}
/* Return if have no weapons */
// Watermelon:added a protection against no weapon droid 'numWeaps'
// The ai orders a non-combat droid to patrol = crash without it...
if(psDroid->asWeaps[0].nStat == 0 || psDroid->numWeaps == 0)
{
return failure;
}
// Check if we have a CB target to begin with
if (!proj_Direct(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat))
{
WEAPON_STATS *psWStats = psWStats = psDroid->asWeaps[weapon_slot].nStat + asWeaponStats;
bestTarget = aiSearchSensorTargets((BASE_OBJECT *)psDroid, weapon_slot, psWStats);
bestMod = targetAttackWeight(bestTarget, (BASE_OBJECT *)psDroid, weapon_slot);
}
droidGetNaybors(psDroid);
//weaponMod = asWeaponModifier[weaponEffect][(asPropulsionStats + ((DROID*)psObj)->asBits[COMP_PROPULSION].nStat)->propulsionType];
weaponEffect = ((WEAPON_STATS *)(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat))->weaponEffect;
//electronic warfare can only be used against structures at present - not any more! AB 6/11/98
electronic = electronicDroid(psDroid);
psTarget = NULL;
bestTarget = NULL;
bestMod = 0;
for (i=0; i< numNaybors; i++)
{
friendlyObj = NULL;
@ -567,9 +641,6 @@ static BOOL aiObjIsWall(BASE_OBJECT *psObj)
BOOL aiChooseTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget, int weapon_slot, BOOL bUpdateTarget)
{
BASE_OBJECT *psTarget = NULL;
SDWORD xdiff,ydiff, distSq, tarDist, minDist;
BOOL bCBTower;
STRUCTURE *psCStruct;
DROID *psCommander;
SECONDARY_STATE state;
SDWORD curTargetWeight=-1;
@ -631,7 +702,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget, int weapon_slot
else if (psObj->type == OBJ_STRUCTURE)
{
WEAPON_STATS *psWStats = NULL;
int longRange = 0;
int tarDist, longRange = 0;
BOOL bCommanderBlock = false;
ASSERT(((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat > 0, "no weapons on structure");
@ -669,60 +740,9 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget, int weapon_slot
// indirect fire structures use sensor towers first
tarDist = longRange * longRange;
minDist = psWStats->minRange * psWStats->minRange;
bCBTower = false;
if (psTarget == NULL && !bCommanderBlock && !proj_Direct(psWStats))
{
for(psCStruct=apsStructLists[psObj->player]; psCStruct; psCStruct=psCStruct->psNext)
{
// skip incomplete structures
if (psCStruct->status != SS_BUILT)
{
continue;
}
if (!bCBTower
&& structStandardSensor(psCStruct)
&& psCStruct->psTarget[0] != NULL
&& !psCStruct->psTarget[0]->died)
{
/* Check it is a valid target */
//Watermelon:Greater than 1 for now
if ( validTarget(psObj, psCStruct->psTarget[0], 0) &&
aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot))
{
xdiff = (SDWORD)psCStruct->psTarget[0]->pos.x - (SDWORD)psObj->pos.x;
ydiff = (SDWORD)psCStruct->psTarget[0]->pos.y - (SDWORD)psObj->pos.y;
distSq = xdiff*xdiff + ydiff*ydiff;
if ((distSq < tarDist) &&
(distSq > minDist))
{
tarDist = distSq;
psTarget = psCStruct->psTarget[0];
}
}
}
else if ((structCBSensor(psCStruct) || objRadarDetector((BASE_OBJECT *)psCStruct))
&& psCStruct->psTarget[0] != NULL
&& !psCStruct->psTarget[0]->died)
{
/* Check it is a valid target */
if ( validTarget(psObj, psCStruct->psTarget[0], 0) &&
aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot))
{
xdiff = (SDWORD)psCStruct->psTarget[0]->pos.x - (SDWORD)psObj->pos.x;
ydiff = (SDWORD)psCStruct->psTarget[0]->pos.y - (SDWORD)psObj->pos.y;
distSq = xdiff*xdiff + ydiff*ydiff;
if ((!bCBTower || (distSq < tarDist)) &&
(distSq > minDist))
{
tarDist = distSq;
psTarget = psCStruct->psTarget[0];
bCBTower = true;
}
}
}
}
psTarget = aiSearchSensorTargets(psObj, weapon_slot, psWStats);
}
if (psTarget == NULL && !bCommanderBlock)
@ -739,9 +759,8 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget, int weapon_slot
&& validTarget(psObj, psCurr, weapon_slot) && psCurr->visible[psObj->player])
{
// See if in sensor range and visible
xdiff = psCurr->pos.x - psObj->pos.x;
ydiff = psCurr->pos.y - psObj->pos.y;
distSq = xdiff * xdiff + ydiff * ydiff;
int distSq = objPosDiffSq(psCurr->pos, psObj->pos);
if (distSq < tarDist
|| (psTarget && psTarget->type == OBJ_STRUCTURE && ((STRUCTURE *)psTarget)->status != SS_BUILT)
|| (psTarget && aiObjIsWall(psTarget) && !aiObjIsWall(psCurr)))

View File

@ -97,9 +97,12 @@ extern BOOL aiChooseTarget(BASE_OBJECT *psObj,
/*set the droid to attack if wihin range otherwise move to target*/
extern void attackTarget(DROID *psDroid, BASE_OBJECT *psTarget);
/* See if there is a target in range for Sensor objects*/
/** See if there is a target in range for Sensor objects. */
extern BOOL aiChooseSensorTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget);
/** See if there is a friendly sensor anywhere holding a valid target for psObj. */
BASE_OBJECT *aiSearchSensorTargets(BASE_OBJECT *psObj, int weapon_slot, WEAPON_STATS *psWStats);
/*set of rules which determine whether the weapon associated with the object
can fire on the propulsion type of the target*/
extern BOOL validTarget(BASE_OBJECT *psObject, BASE_OBJECT *psTarget, int weapon_slot);

View File

@ -78,7 +78,8 @@ typedef enum _object_type
UDWORD armour[NUM_HIT_SIDES][WC_NUM_WEAPON_CLASSES]
#define NEXTOBJ(pointerType) \
pointerType *psNext /**< Pointer to the next object in the list */
pointerType *psNext; /**< Pointer to the next object in the object list */ \
pointerType *psNextFunc /**< Pointer to the next object in the function list */
#define SIMPLE_ELEMENTS(pointerType) \
BASE_ELEMENTS1(pointerType); \

View File

@ -2355,6 +2355,7 @@ BOOL loadGame(const char *pGameToLoad, BOOL keepObjects, BOOL freeMem, BOOL User
apsFlagPosLists[player] = NULL;
//clear all the messages?
apsProxDisp[player] = NULL;
apsSensorList[0] = NULL;
}
initFactoryNumFlag();
}

View File

@ -55,12 +55,12 @@ static SDWORD factoryDeliveryPointCheck[MAX_PLAYERS][NUM_FLAG_TYPES][MAX_FACTORY
*/
UDWORD objID;
/* The lists of objects allocated */
DROID *apsDroidLists[MAX_PLAYERS];
STRUCTURE *apsStructLists[MAX_PLAYERS];
FEATURE *apsFeatureLists[MAX_PLAYERS]; // Only player zero is valid for
// features
FEATURE *apsFeatureLists[MAX_PLAYERS]; ///< Only player zero is valid for features. TODO: Reduce to single list.
BASE_OBJECT *apsSensorList[1]; ///< List of sensors in the game.
/*The list of Flag Positions allocated */
FLAG_POSITION *apsFlagPosLists[MAX_PLAYERS];
@ -245,14 +245,25 @@ static inline BASE_OBJECT* createObject(UDWORD player, OBJECT_TYPE objType)
/* Add the object to its list
* \param list is a pointer to the object list
*/
static inline void addObjectToList(BASE_OBJECT *list[], BASE_OBJECT *object)
static inline void addObjectToList(BASE_OBJECT *list[], BASE_OBJECT *object, int player)
{
ASSERT(object != NULL,
"addObjectToList: Invalid pointer");
ASSERT(object != NULL, "Invalid pointer");
// Prepend the object to the top of the list
object->psNext = list[object->player];
list[object->player] = object;
object->psNext = list[player];
list[player] = object;
}
/* Add the object to its list
* \param list is a pointer to the object list
*/
static inline void addObjectToFuncList(BASE_OBJECT *list[], BASE_OBJECT *object, int player)
{
ASSERT(object != NULL, "Invalid pointer");
// 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.
@ -305,36 +316,61 @@ static inline void destroyObject(BASE_OBJECT* list[], BASE_OBJECT* object)
* \param remove is a pointer to the object to remove
* \param type is the type of the object
*/
static inline void removeObjectFromList(BASE_OBJECT *list[], BASE_OBJECT *object)
static inline void removeObjectFromList(BASE_OBJECT *list[], BASE_OBJECT *object, int player)
{
BASE_OBJECT *psPrev = NULL, *psCurr;
ASSERT( object != NULL,
"removeObjectFromList: Invalid pointer" );
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[object->player] == object)
if (list[player] == object)
{
list[object->player] = list[object->player]->psNext;
list[player] = list[player]->psNext;
return;
}
// Iterate through the list and find the item before the object to delete
for(psCurr = list[object->player]; (psCurr != object) && (psCurr != NULL); psCurr = psCurr->psNext)
for(psCurr = list[player]; (psCurr != object) && (psCurr != NULL); psCurr = psCurr->psNext)
{
psPrev = psCurr;
}
ASSERT( psCurr != NULL,
"removeObjectFromList: object not found in list" );
ASSERT_OR_RETURN(, psCurr != NULL, "Object %p not found in list", object);
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;
}
/* 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
*/
static inline void removeObjectFromFuncList(BASE_OBJECT *list[], BASE_OBJECT *object, int player)
{
BASE_OBJECT *psPrev = NULL, *psCurr;
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;
return;
}
// Iterate through the list and find the item before the object to delete
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;
}
static inline BASE_OBJECT* findObjectInList(BASE_OBJECT list[], UDWORD idNum)
@ -392,17 +428,22 @@ DROID* createDroid(UDWORD player)
}
/* add the droid to the Droid Lists */
void addDroid(DROID *psDroidToAdd, DROID *pList[MAX_PLAYERS])
{
void addDroid(DROID *psDroidToAdd, DROID *pList[MAX_PLAYERS])
{
DROID_GROUP *psGroup;
addObjectToList((BASE_OBJECT**)pList, (BASE_OBJECT*)psDroidToAdd);
/*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?!*/
addObjectToList((BASE_OBJECT**)pList, (BASE_OBJECT*)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 (psDroidToAdd->droidType == DROID_COMMAND)
@ -414,9 +455,9 @@ DROID* createDroid(UDWORD player)
}
}
}
}
}
/*destroy a droid */
/* Destroy a droid */
void killDroid(DROID *psDel)
{
int i;
@ -432,6 +473,10 @@ void killDroid(DROID *psDel)
setDroidActionTarget(psDel, NULL, i);
}
setDroidBase(psDel, NULL);
if (psDel->droidType == DROID_SENSOR)
{
removeObjectFromFuncList(apsSensorList, (BASE_OBJECT*)psDel, 0);
}
destroyObject((BASE_OBJECT**)apsDroidLists, (BASE_OBJECT*)psDel);
}
@ -449,13 +494,17 @@ void removeDroid(DROID *psDroidToRemove, DROID *pList[MAX_PLAYERS])
"removeUnit: pointer is not a unit" );
ASSERT( psDroidToRemove->player < MAX_PLAYERS,
"removeUnit: invalid player for unit" );
removeObjectFromList((BASE_OBJECT**)pList, (BASE_OBJECT*)psDroidToRemove);
removeObjectFromList((BASE_OBJECT**)pList, (BASE_OBJECT*)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;
}
}
@ -483,7 +532,12 @@ STRUCTURE* createStruct(UDWORD player)
/* add the structure to the Structure Lists */
void addStructure(STRUCTURE *psStructToAdd)
{
addObjectToList((BASE_OBJECT**)apsStructLists, (BASE_OBJECT*)psStructToAdd);
addObjectToList((BASE_OBJECT**)apsStructLists, (BASE_OBJECT*)psStructToAdd, psStructToAdd->player);
if (psStructToAdd->pStructureType->pSensor
&& psStructToAdd->pStructureType->pSensor->location == LOC_TURRET)
{
addObjectToFuncList(apsSensorList, (BASE_OBJECT*)psStructToAdd, 0);
}
}
/* Destroy a structure */
@ -496,6 +550,12 @@ void killStruct(STRUCTURE *psBuilding)
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);
}
for (i = 0; i < STRUCT_MAXWEAPS; i++)
{
setStructureTarget(psBuilding, NULL, i);
@ -552,7 +612,12 @@ void removeStructureFromList(STRUCTURE *psStructToRemove, STRUCTURE *pList[MAX_P
"removeStructureFromList: pointer is not a structure" );
ASSERT( psStructToRemove->player < MAX_PLAYERS,
"removeStructureFromList: invalid player for structure" );
removeObjectFromList((BASE_OBJECT**)pList, (BASE_OBJECT*)psStructToRemove);
removeObjectFromList((BASE_OBJECT**)pList, (BASE_OBJECT*)psStructToRemove, psStructToRemove->player);
if (psStructToRemove->pStructureType->pSensor
&& psStructToRemove->pStructureType->pSensor->location == LOC_TURRET)
{
removeObjectFromFuncList(apsSensorList, (BASE_OBJECT*)psStructToRemove, 0);
}
}
/************************** FEATURE *********************************/
@ -566,7 +631,7 @@ FEATURE* createFeature()
/* add the feature to the Feature Lists */
void addFeature(FEATURE *psFeatureToAdd)
{
addObjectToList((BASE_OBJECT**)apsFeatureLists, (BASE_OBJECT*)psFeatureToAdd);
addObjectToList((BASE_OBJECT**)apsFeatureLists, (BASE_OBJECT*)psFeatureToAdd, 0);
}
/* Destroy a feature */
@ -876,8 +941,8 @@ static void objListIntegCheck(void)
{
ASSERT( psCurr->type == OBJ_STRUCTURE &&
(SDWORD)psCurr->player == player,
"objListIntegCheck: misplaced object in the structure list for player %d",
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)

View File

@ -31,6 +31,7 @@ extern DROID *apsDroidLists[MAX_PLAYERS];
extern STRUCTURE *apsStructLists[MAX_PLAYERS];
extern FEATURE *apsFeatureLists[MAX_PLAYERS];
extern FLAG_POSITION *apsFlagPosLists[MAX_PLAYERS];
extern BASE_OBJECT *apsSensorList[1];
/* The list of destroyed objects */
extern BASE_OBJECT *psDestroyedObj;