// Disable Tactical order mode. // void driveDisableTactical(void) { if (driveModeActive() && TacticalActive) { TacticalActive = false; } }
// Disable Tactical order mode. // void driveDisableTactical(void) { if(driveModeActive() && TacticalActive) { CancelTacticalScroll(); TacticalActive = false; } }
// Call whenever the selections change to re initialise drive mode. // void driveSelectionChanged(void) { if(driveModeActive()) { if(psDrivenDroid) { ChangeDriver(); StartDriverMode(NULL); } } }
// Call whenever the selections change to re initialise drive mode. // void driveSelectionChanged(void) { if(driveModeActive()) { if(psDrivenDroid) { // StopDriverMode(); ChangeDriver(); StartDriverMode(NULL); driveTacticalSelectionChanged(); } } }
// Call this whenever a droid gets killed or removed. // returns true if ok, returns false if resulted in driving mode being stopped, ie could'nt find // a selected droid to drive. // bool driveDroidKilled(DROID *psDroid) { if(driveModeActive()) { if(psDroid == psDrivenDroid) { ChangeDriver(); psDrivenDroid = NULL; DeSelectDroid(psDroid); if(!StartDriverMode(psDroid)) { return false; } } } return true; }
// 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, UWORD *targetOrigin) { SDWORD bestMod = 0,newMod, failure = -1; BASE_OBJECT *psTarget = NULL, *friendlyObj, *bestTarget = NULL, *iter, *targetInQuestion, *tempTarget; BOOL electronic = false; STRUCTURE *targetStructure; WEAPON_EFFECT weaponEffect; UWORD tmpOrigin = ORIGIN_UNKNOWN; // reset origin if (targetOrigin) { *targetOrigin = ORIGIN_UNKNOWN; } //don't bother looking if empty vtol droid if (vtolEmpty(psDroid)) { return failure; } /* Return if have no weapons */ // The ai orders a non-combat droid to patrol = crash without it... if ((psDroid->asWeaps[0].nStat == 0 || psDroid->numWeaps == 0) && psDroid->droidType != DROID_SENSOR) { 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, &tmpOrigin); bestMod = targetAttackWeight(bestTarget, (BASE_OBJECT *)psDroid, weapon_slot); } weaponEffect = ((WEAPON_STATS *)(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat))->weaponEffect; electronic = electronicDroid(psDroid); // Range was previously 9*TILE_UNITS. Increasing this doesn't seem to help much, though. Not sure why. gridStartIterate(psDroid->pos.x, psDroid->pos.y, psDroid->sensorRange + 6*TILE_UNITS); for (iter = gridIterate(); iter != NULL; iter = gridIterate()) { friendlyObj = NULL; targetInQuestion = iter; /* This is a friendly unit, check if we can reuse its target */ if(aiCheckAlliances(targetInQuestion->player,psDroid->player)) { friendlyObj = targetInQuestion; targetInQuestion = NULL; /* Can we see what it is doing? */ if(friendlyObj->visible[psDroid->player]) { if(friendlyObj->type == OBJ_DROID) { DROID *friendlyDroid = (DROID *)friendlyObj; /* See if friendly droid has a target */ tempTarget = friendlyDroid->psActionTarget[0]; if(tempTarget && !aiObjectIsProbablyDoomed(tempTarget)) { //make sure a weapon droid is targeting it if(friendlyDroid->numWeaps > 0) { // make sure this target wasn't assigned explicitly to this droid if(friendlyDroid->order != DORDER_ATTACK) { // make sure target is near enough if (aiDroidHasRange(psDroid, tempTarget, weapon_slot)) { targetInQuestion = tempTarget; //consider this target } } } } } else if(friendlyObj->type == OBJ_STRUCTURE) { tempTarget = ((STRUCTURE*)friendlyObj)->psTarget[0]; if (tempTarget && !aiObjectIsProbablyDoomed(tempTarget) && aiDroidHasRange(psDroid, tempTarget, weapon_slot)) { targetInQuestion = tempTarget; } } } } if (targetInQuestion != NULL && targetInQuestion != (BASE_OBJECT *)psDroid // in case friendly unit had me as target && (targetInQuestion->type == OBJ_DROID || targetInQuestion->type == OBJ_STRUCTURE || targetInQuestion->type == OBJ_FEATURE) && targetInQuestion->visible[psDroid->player] && !aiCheckAlliances(targetInQuestion->player,psDroid->player) && validTarget((BASE_OBJECT *)psDroid, targetInQuestion, weapon_slot) && aiDroidHasRange(psDroid, targetInQuestion, weapon_slot)) { if (targetInQuestion->type == OBJ_DROID) { // in multiPlayer - don't attack Transporters with EW if (bMultiPlayer) { // if not electronic then valid target if (!electronic || (electronic && ((DROID *)targetInQuestion)->droidType != DROID_TRANSPORTER)) { //only a valid target if NOT a transporter psTarget = targetInQuestion; } } else { psTarget = targetInQuestion; } } else if (targetInQuestion->type == OBJ_STRUCTURE) { STRUCTURE *psStruct = (STRUCTURE *)targetInQuestion; if (electronic) { /* don't want to target structures with resistance of zero if using electronic warfare */ if (validStructResistance((STRUCTURE *)targetInQuestion)) { psTarget = targetInQuestion; } } else if (psStruct->asWeaps[weapon_slot].nStat > 0) { // structure with weapons - go for this psTarget = targetInQuestion; } else if ((psStruct->pStructureType->type != REF_WALL && psStruct->pStructureType->type != REF_WALLCORNER) || driveModeActive() || (bMultiPlayer && !isHumanPlayer(psDroid->player))) { psTarget = targetInQuestion; } } else if (targetInQuestion->type == OBJ_FEATURE && gameTime - psDroid->lastFrustratedTime < FRUSTRATED_TIME && ((FEATURE *)targetInQuestion)->psStats->damageable && !(game.scavengers && psDroid->player == 7)) // hack to avoid scavs blowing up their nice feature walls { psTarget = targetInQuestion; } /* Check if our weapon is most effective against this object */ if(psTarget != NULL && psTarget == targetInQuestion) //was assigned? { newMod = targetAttackWeight(psTarget, (BASE_OBJECT *)psDroid, weapon_slot); /* Remember this one if it's our best target so far */ if( newMod >= 0 && (newMod > bestMod || bestTarget == NULL)) { bestMod = newMod; tmpOrigin = ORIGIN_ALLY; bestTarget = psTarget; } } } } if (bestTarget) { ASSERT(!bestTarget->died, "aiBestNearestTarget: AI gave us a target that is already dead."); targetStructure = visGetBlockingWall((BASE_OBJECT *)psDroid, bestTarget); /* See if target is blocked by a wall; only affects direct weapons */ if (proj_Direct(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat) && targetStructure) { //are we any good against walls? if(asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] >= 100) //can attack atleast with default strength { bestTarget = (BASE_OBJECT *)targetStructure; //attack wall } } if (targetOrigin) { *targetOrigin = tmpOrigin; } *ppsObj = bestTarget; return bestMod; } return failure; }
/* Do the AI for a droid */ void aiUpdateDroid(DROID *psDroid) { BASE_OBJECT *psTarget; BOOL lookForTarget,updateTarget; ASSERT(psDroid != NULL, "Invalid droid pointer"); if (!psDroid || isDead((BASE_OBJECT *)psDroid)) { return; } // HACK: we always want to update orders when NOT running a MP game, // and we don't want to update when the droid belongs to another human player if (!myResponsibility(psDroid->player) && bMultiPlayer && isHumanPlayer(psDroid->player)) { return; // we should not order this droid around } lookForTarget = false; updateTarget = false; // look for a target if doing nothing if (orderState(psDroid, DORDER_NONE) || orderState(psDroid, DORDER_GUARD) || orderState(psDroid, DORDER_TEMP_HOLD)) { lookForTarget = true; } // but do not choose another target if doing anything while guarding if (orderState(psDroid, DORDER_GUARD) && (psDroid->action != DACTION_NONE)) { lookForTarget = false; } // except when self-repairing if (psDroid->action == DACTION_DROIDREPAIR && psDroid->psActionTarget[0] == (BASE_OBJECT *)psDroid) { lookForTarget = true; } // don't look for a target if sulking if (psDroid->action == DACTION_SULK) { lookForTarget = false; } /* Only try to update target if already have some target */ if (psDroid->action == DACTION_ATTACK || psDroid->action == DACTION_MOVEFIRE || psDroid->action == DACTION_MOVETOATTACK || psDroid->action == DACTION_ROTATETOATTACK) { updateTarget = true; } if ((orderState(psDroid, DORDER_OBSERVE) || orderState(psDroid, DORDER_ATTACKTARGET)) && psDroid->psTarget && aiObjectIsProbablyDoomed(psDroid->psTarget)) { lookForTarget = true; updateTarget = false; } /* Don't update target if we are sent to attack and reached attack destination (attacking our target) */ if (orderState(psDroid, DORDER_ATTACK) && psDroid->psActionTarget[0] == psDroid->psTarget) { updateTarget = false; } // don't look for a target if there are any queued orders if (psDroid->listSize > 0) { lookForTarget = false; updateTarget = false; } // horrible check to stop droids looking for a target if // they would switch to the guard order in the order update loop if ((psDroid->order == DORDER_NONE) && (psDroid->player == selectedPlayer) && !isVtolDroid(psDroid) && secondaryGetState(psDroid, DSO_HALTTYPE) == DSS_HALT_GUARD) { lookForTarget = false; updateTarget = false; } // don't allow units to start attacking if they will switch to guarding the commander if(hasCommander(psDroid)) { lookForTarget = false; updateTarget = false; } if(bMultiPlayer && isVtolDroid(psDroid) && isHumanPlayer(psDroid->player)) { lookForTarget = false; updateTarget = false; } // do not look for a target if droid is currently under direct control. if(driveModeActive() && (psDroid == driveGetDriven())) { lookForTarget = false; updateTarget = false; } // CB and VTOL CB droids can't autotarget. if (psDroid->droidType == DROID_SENSOR && !standardSensorDroid(psDroid)) { lookForTarget = false; updateTarget = false; } // do not attack if the attack level is wrong if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL) != DSS_ALEV_ALWAYS) { lookForTarget = false; } /* For commanders and non-assigned non-commanders: look for a better target once in a while */ if(!lookForTarget && updateTarget) { if((psDroid->numWeaps > 0) && !hasCommander(psDroid)) //not assigned to commander { if((psDroid->id + gameTime)/TARGET_UPD_SKIP_FRAMES != (psDroid->id + gameTime - deltaGameTime)/TARGET_UPD_SKIP_FRAMES) { unsigned int i; (void)updateAttackTarget((BASE_OBJECT*)psDroid, 0); // this function always has to be called on weapon-slot 0 (even if ->numWeaps == 0) //updates all targets for (i = 1; i < psDroid->numWeaps; ++i) { (void)updateAttackTarget((BASE_OBJECT*)psDroid, i); } } } } /* Null target - see if there is an enemy to attack */ if (lookForTarget && !updateTarget) { if (psDroid->droidType == DROID_SENSOR) { if (aiChooseSensorTarget((BASE_OBJECT *)psDroid, &psTarget)) { orderDroidObj(psDroid, DORDER_OBSERVE, psTarget); } } else { if (aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget, 0, true, NULL)) { orderDroidObj(psDroid, DORDER_ATTACKTARGET, psTarget); } } } }
/* Do the AI for a droid */ void aiUpdateDroid(DROID *psDroid) { BASE_OBJECT *psTarget; bool lookForTarget, updateTarget; ASSERT(psDroid != NULL, "Invalid droid pointer"); if (!psDroid || isDead((BASE_OBJECT *)psDroid)) { return; } lookForTarget = false; updateTarget = false; // look for a target if doing nothing if (orderState(psDroid, DORDER_NONE) || orderState(psDroid, DORDER_GUARD) || orderState(psDroid, DORDER_HOLD)) { lookForTarget = true; } // but do not choose another target if doing anything while guarding // exception for sensors, to allow re-targetting when target is doomed if (orderState(psDroid, DORDER_GUARD) && psDroid->action != DACTION_NONE && psDroid->droidType != DROID_SENSOR) { lookForTarget = false; } // don't look for a target if sulking if (psDroid->action == DACTION_SULK) { lookForTarget = false; } /* Only try to update target if already have some target */ if (psDroid->action == DACTION_ATTACK || psDroid->action == DACTION_MOVEFIRE || psDroid->action == DACTION_MOVETOATTACK || psDroid->action == DACTION_ROTATETOATTACK) { updateTarget = true; } if ((orderState(psDroid, DORDER_OBSERVE) || orderState(psDroid, DORDER_ATTACKTARGET)) && psDroid->order.psObj && psDroid->order.psObj->died) { lookForTarget = true; updateTarget = false; } /* Don't update target if we are sent to attack and reached attack destination (attacking our target) */ if (orderState(psDroid, DORDER_ATTACK) && psDroid->psActionTarget[0] == psDroid->order.psObj) { updateTarget = false; } // don't look for a target if there are any queued orders if (psDroid->listSize > 0) { lookForTarget = false; updateTarget = false; } // don't allow units to start attacking if they will switch to guarding the commander if (hasCommander(psDroid)) { lookForTarget = false; updateTarget = false; } if (bMultiPlayer && isVtolDroid(psDroid) && isHumanPlayer(psDroid->player)) { lookForTarget = false; updateTarget = false; } // do not look for a target if droid is currently under direct control. if (driveModeActive() && (psDroid == driveGetDriven())) { lookForTarget = false; updateTarget = false; } // CB and VTOL CB droids can't autotarget. if (psDroid->droidType == DROID_SENSOR && !standardSensorDroid(psDroid)) { lookForTarget = false; updateTarget = false; } // do not attack if the attack level is wrong if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL) != DSS_ALEV_ALWAYS) { lookForTarget = false; } /* For commanders and non-assigned non-commanders: look for a better target once in a while */ if (!lookForTarget && updateTarget && psDroid->numWeaps > 0 && !hasCommander(psDroid) && (psDroid->id + gameTime) / TARGET_UPD_SKIP_FRAMES != (psDroid->id + gameTime - deltaGameTime) / TARGET_UPD_SKIP_FRAMES) { for (int i = 0; i < psDroid->numWeaps; ++i) { updateAttackTarget((BASE_OBJECT *)psDroid, i); } } /* Null target - see if there is an enemy to attack */ if (lookForTarget && !updateTarget) { if (psDroid->droidType == DROID_SENSOR) { if (aiChooseSensorTarget((BASE_OBJECT *)psDroid, &psTarget)) { actionDroid(psDroid, DACTION_OBSERVE, psTarget); } } else { if (aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget, 0, true, NULL)) { actionDroid(psDroid, DACTION_ATTACK, psTarget); } } } }