/* See if there is a target in range */ BOOL aiChooseTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget, int weapon_slot, BOOL bUpdateTarget, UWORD *targetOrigin) { BASE_OBJECT *psTarget = NULL; DROID *psCommander; SDWORD curTargetWeight=-1; UWORD tmpOrigin = ORIGIN_UNKNOWN; if (targetOrigin) { *targetOrigin = ORIGIN_UNKNOWN; } /* Get the sensor range */ switch (psObj->type) { case OBJ_DROID: if (((DROID *)psObj)->asWeaps[weapon_slot].nStat == 0) { return false; } if (((DROID *)psObj)->asWeaps[0].nStat == 0 && ((DROID *)psObj)->droidType != DROID_SENSOR) { // Can't attack without a weapon return false; } break; case OBJ_STRUCTURE: if (((STRUCTURE *)psObj)->numWeaps == 0 || ((STRUCTURE *)psObj)->asWeaps[0].nStat == 0) { // Can't attack without a weapon return false; } break; default: break; } /* See if there is a something in range */ if (psObj->type == OBJ_DROID) { BASE_OBJECT *psCurrTarget = ((DROID *)psObj)->psActionTarget[0]; /* find a new target */ int newTargetWeight = aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot, NULL); /* Calculate weight of the current target if updating; but take care not to target * ourselves... */ if (bUpdateTarget && psCurrTarget != psObj) { curTargetWeight = targetAttackWeight(psCurrTarget, psObj, weapon_slot); } if (newTargetWeight >= 0 // found a new target && (!bUpdateTarget // choosing a new target, don't care if current one is better || curTargetWeight <= 0 // attacker had no valid target, use new one || newTargetWeight > curTargetWeight + OLD_TARGET_THRESHOLD) // updating and new target is better && validTarget(psObj, psTarget, weapon_slot) && (aiDroidHasRange((DROID *)psObj, psTarget, weapon_slot) || (secondaryGetState((DROID *)psObj, DSO_HALTTYPE) != DSS_HALT_HOLD))) { ASSERT(!isDead(psTarget), "aiChooseTarget: Droid found a dead target!"); *ppsTarget = psTarget; return true; } } else if (psObj->type == OBJ_STRUCTURE) { WEAPON_STATS *psWStats = NULL; int tarDist, longRange = 0; BOOL bCommanderBlock = false; ASSERT(((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat > 0, "no weapons on structure"); psWStats = ((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat + asWeaponStats; longRange = proj_GetLongRange(psWStats); // see if there is a target from the command droids psTarget = NULL; psCommander = cmdDroidGetDesignator(psObj->player); if (!proj_Direct(psWStats) && (psCommander != NULL) && aiStructHasRange((STRUCTURE *)psObj, (BASE_OBJECT *)psCommander, weapon_slot)) { // there is a commander that can fire designate for this structure // set bCommanderBlock so that the structure does not fire until the commander // has a target - (slow firing weapons will not be ready to fire otherwise). bCommanderBlock = true; // I do believe this will never happen, check for yourself :-) debug(LOG_NEVER,"Commander %d is good enough for fire designation", psCommander->id); if (psCommander->action == DACTION_ATTACK && psCommander->psActionTarget[0] != NULL && !aiObjectIsProbablyDoomed(psCommander->psActionTarget[0])) { // the commander has a target to fire on if (aiStructHasRange((STRUCTURE *)psObj, psCommander->psActionTarget[0], weapon_slot)) { // target in range - fire on it tmpOrigin = ORIGIN_COMMANDER; psTarget = psCommander->psActionTarget[0]; } else { // target out of range - release the commander block bCommanderBlock = false; } } } // indirect fire structures use sensor towers first tarDist = longRange * longRange; if (psTarget == NULL && !bCommanderBlock && !proj_Direct(psWStats)) { psTarget = aiSearchSensorTargets(psObj, weapon_slot, psWStats, &tmpOrigin); } if (psTarget == NULL && !bCommanderBlock) { BASE_OBJECT *psCurr; gridStartIterate(psObj->pos.x, psObj->pos.y, PREVIOUS_DEFAULT_GRID_SEARCH_RADIUS); psCurr = gridIterate(); while (psCurr != NULL) { /* Check that it is a valid target */ if (psCurr->type != OBJ_FEATURE && !aiObjectIsProbablyDoomed(psCurr) && aiStructHasRange((STRUCTURE *)psObj, psCurr, weapon_slot) && !aiCheckAlliances(psCurr->player, psObj->player) && validTarget(psObj, psCurr, weapon_slot) && psCurr->visible[psObj->player]) { // See if in sensor range and visible 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))) { tmpOrigin = ORIGIN_VISUAL; psTarget = psCurr; tarDist = distSq; } } psCurr = gridIterate(); } } if (psTarget) { ASSERT(!psTarget->died, "aiChooseTarget: Structure found a dead target!"); if (targetOrigin) { *targetOrigin = tmpOrigin; } *ppsTarget = psTarget; return true; } } return false; }
/* See if there is a target in range for Sensor objects*/ BOOL aiChooseSensorTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget) { int sensorRange = objSensorRange(psObj); unsigned int radSquared = sensorRange * sensorRange; bool radarDetector = objRadarDetector(psObj); if (!objActiveRadar(psObj) && !radarDetector) { ASSERT(false, "Only to be used for sensor turrets!"); return false; } /* See if there is something in range */ if (radarDetector) { BASE_OBJECT *psCurr, *psTemp = NULL; int tarDist = SDWORD_MAX; gridStartIterate(psObj->pos.x, psObj->pos.y, PREVIOUS_DEFAULT_GRID_SEARCH_RADIUS); psCurr = gridIterate(); while (psCurr != NULL) { if ((psCurr->type == OBJ_STRUCTURE || psCurr->type == OBJ_DROID) && !aiObjectIsProbablyDoomed(psCurr)) { if (!aiCheckAlliances(psCurr->player,psObj->player) && objActiveRadar(psCurr)) { // See if in twice *their* sensor range const int xdiff = psCurr->pos.x - psObj->pos.x; const int ydiff = psCurr->pos.y - psObj->pos.y; const unsigned int distSq = xdiff * xdiff + ydiff * ydiff; const int targetSensor = objSensorRange(psCurr) * 2; const unsigned int sensorSquared = targetSensor * targetSensor; if (distSq < sensorSquared && distSq < tarDist) { psTemp = psCurr; tarDist = distSq; } } } psCurr = gridIterate(); } if (psTemp) { objTrace(psTemp->id, "Targetted by radar detector %d", (int)psObj->id); objTrace(psObj->id, "Targetting radar %d", (int)psTemp->id); ASSERT(!psTemp->died, "aiChooseSensorTarget gave us a dead target"); *ppsTarget = psTemp; return true; } } else if (psObj->type == OBJ_DROID) { BASE_OBJECT *psTarget = NULL; if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0, NULL) >= 0) { /* See if in sensor range */ const int xdiff = psTarget->pos.x - psObj->pos.x; const int ydiff = psTarget->pos.y - psObj->pos.y; const unsigned int distSq = xdiff * xdiff + ydiff * ydiff; // I do believe this will never happen, check for yourself :-) debug(LOG_NEVER, "Sensor droid(%d) found possible target(%d)!!!", psObj->id, psTarget->id); if (distSq < radSquared) { *ppsTarget = psTarget; return true; } } } else // structure { BASE_OBJECT *psCurr, *psTemp = NULL; int tarDist = SDWORD_MAX; gridStartIterate(psObj->pos.x, psObj->pos.y, PREVIOUS_DEFAULT_GRID_SEARCH_RADIUS); psCurr = gridIterate(); while (psCurr != NULL) { // Don't target features or doomed/dead objects if (psCurr->type != OBJ_FEATURE && !aiObjectIsProbablyDoomed(psCurr)) { if (!aiCheckAlliances(psCurr->player,psObj->player) && !aiObjIsWall(psCurr)) { // See if in sensor range and visible const int xdiff = psCurr->pos.x - psObj->pos.x; const int ydiff = psCurr->pos.y - psObj->pos.y; const unsigned int distSq = xdiff * xdiff + ydiff * ydiff; if (distSq < radSquared && psCurr->visible[psObj->player] && distSq < tarDist) { psTemp = psCurr; tarDist = distSq; } } } psCurr = gridIterate(); } if (psTemp) { ASSERT(!psTemp->died, "aiChooseSensorTarget gave us a dead target"); *ppsTarget = psTemp; return true; } } return false; }
/* See if there is a target in range for Sensor objects*/ bool aiChooseSensorTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget) { int sensorRange = objSensorRange(psObj); unsigned int radSquared = sensorRange * sensorRange; bool radarDetector = objRadarDetector(psObj); if (!objActiveRadar(psObj) && !radarDetector) { ASSERT(false, "Only to be used for sensor turrets!"); return false; } /* See if there is something in range */ if (psObj->type == OBJ_DROID) { BASE_OBJECT *psTarget = NULL; if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0) >= 0) { /* See if in sensor range */ const int xdiff = psTarget->pos.x - psObj->pos.x; const int ydiff = psTarget->pos.y - psObj->pos.y; const unsigned int distSq = xdiff * xdiff + ydiff * ydiff; // I do believe this will never happen, check for yourself :-) debug(LOG_NEVER, "Sensor droid(%d) found possible target(%d)!!!", psObj->id, psTarget->id); if (distSq < radSquared) { *ppsTarget = psTarget; return true; } } } else // structure { BASE_OBJECT *psTemp = NULL; int tarDist = SDWORD_MAX; static GridList gridList; // static to avoid allocations. gridList = gridStartIterate(psObj->pos.x, psObj->pos.y, objSensorRange(psObj)); for (GridIterator gi = gridList.begin(); gi != gridList.end(); ++gi) { BASE_OBJECT *psCurr = *gi; // Don't target features or doomed/dead objects if (psCurr->type != OBJ_FEATURE && !psCurr->died && !aiObjectIsProbablyDoomed(psCurr, false)) { if (!aiCheckAlliances(psCurr->player, psObj->player) && !aiObjIsWall(psCurr)) { // See if in sensor range and visible const int xdiff = psCurr->pos.x - psObj->pos.x; const int ydiff = psCurr->pos.y - psObj->pos.y; const unsigned int distSq = xdiff * xdiff + ydiff * ydiff; if (distSq < radSquared && psCurr->visible[psObj->player] == UBYTE_MAX && distSq < tarDist) { psTemp = psCurr; tarDist = distSq; } } } } if (psTemp) { ASSERT(!psTemp->died, "aiChooseSensorTarget gave us a dead target"); *ppsTarget = psTemp; return true; } } return false; }