diff --git a/src/action.c b/src/action.c index 94c3b027a..18d923ef3 100644 --- a/src/action.c +++ b/src/action.c @@ -1218,9 +1218,9 @@ static void actionUpdateTransporter( DROID *psDroid ) { if (psDroid->asWeaps[i].nStat > 0) { - if (psDroid->psActionTarget[0] == NULL) + if (psDroid->psActionTarget[0] == NULL && CAN_UPDATE_NAYBORS(psDroid)) { - aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], i); + (void)aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], i); } if ( psDroid->psActionTarget[0] != NULL ) @@ -1461,7 +1461,7 @@ void actionUpdateDroid(DROID *psDroid) UDWORD tlx,tly; STRUCTURE *psStruct; STRUCTURE_STATS *psStructStats; - BASE_OBJECT *psTarget;//, *psObj; + BASE_OBJECT *psTarget; WEAPON_STATS *psWeapStats; SDWORD targetDir, dirDiff, pbx,pby; SDWORD xdiff,ydiff, rangeSq; @@ -1704,7 +1704,8 @@ void actionUpdateDroid(DROID *psDroid) psDroid->asWeaps[i].nStat > 0 && psWeapStats->rotate && psWeapStats->fireOnMove != FOM_NO && - aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], 0)) + CAN_UPDATE_NAYBORS(psDroid) && + (aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], i) >= 0)) { if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state)) { @@ -1755,7 +1756,7 @@ void actionUpdateDroid(DROID *psDroid) } else { - vtResult = 1; + vtResult = INVALID_TARGET; } // firing on something while moving @@ -1765,7 +1766,7 @@ void actionUpdateDroid(DROID *psDroid) psDroid->action = DACTION_NONE; } else if ((psDroid->psActionTarget[0] == NULL) || - (vtResult == 1) || + (vtResult == INVALID_TARGET) || (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS))) { // Target lost @@ -1788,6 +1789,7 @@ void actionUpdateDroid(DROID *psDroid) } else { + /* Deal with target */ if (visibleObject((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0])) { for(i = 0;i < psDroid->numWeaps;i++) @@ -1797,12 +1799,6 @@ void actionUpdateDroid(DROID *psDroid) //Watermelon:to fix a AA-weapon attack ground unit exploit if ( (num_weapons & (1 << (i+1))) && (vtResult & (1 << (i+1))) ) { - /*if (actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget, - &(psDroid->turretRotation), &(psDroid->turretPitch), - psDroid->turretRotRate, (SWORD)(psDroid->turretRotRate/2), - //asWeaponStats[psDroid->asWeaps->nStat].direct)) - proj_Direct(&asWeaponStats[psDroid->asWeaps->nStat]), - bInvert))*/ if (actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0], &(psDroid->turretRotation[i]), &(psDroid->turretPitch[i]), &asWeaponStats[psDroid->asWeaps[i].nStat], @@ -1852,7 +1848,7 @@ void actionUpdateDroid(DROID *psDroid) //check the target hasn't become one the same player ID - Electronic Warfare if ((electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) || - (vtResult == 1) )// || + (vtResult == INVALID_TARGET) )// || // (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS))) { psDroid->psActionTarget[0] = NULL; @@ -2048,7 +2044,7 @@ void actionUpdateDroid(DROID *psDroid) } else { - vtResult = 1; + vtResult = INVALID_TARGET; } avtResult = actionVisibleTarget(psDroid, psDroid->psActionTarget[0]); @@ -2058,7 +2054,7 @@ void actionUpdateDroid(DROID *psDroid) (psDroid->psActionTarget[0] == NULL) || //check the target hasn't become one the same player ID - Electronic Warfare (electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) || - (vtResult == 1) )// || + (vtResult == INVALID_TARGET) )// || // (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS))) { moveToRearm(psDroid); @@ -2172,7 +2168,7 @@ void actionUpdateDroid(DROID *psDroid) //check the target hasn't become one the same player ID - Electronic Warfare if ((electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) || - (vtResult == 1) )// || + (vtResult == INVALID_TARGET) )// || // (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS))) { for (i = 0;i < psDroid->numWeaps;i++) diff --git a/src/action.h b/src/action.h index 8e211b0fb..34237426f 100644 --- a/src/action.h +++ b/src/action.h @@ -130,6 +130,21 @@ extern BOOL actionRouteBlockingPos(DROID *psDroid, SDWORD x, SDWORD y); // choose a landing position for a VTOL when it goes to rearm extern BOOL actionVTOLLandingPos(DROID *psDroid, UDWORD *px, UDWORD *py); +// Try to find a better target +extern BOOL updateAttackTarget(BASE_OBJECT * psAttacker, SDWORD weapon_slot); + +//How many frames to skip before updating naybors +#define NAYBOR_SKIP_FRAMES 8 + +/* How many frames to skip before looking for a better target */ +#define TARGET_UPD_SKIP_FRAMES 1000 + +#define INVALID_TARGET 1 + +/* Macro to check if it's time to update naybor list */ +#define CAN_UPDATE_NAYBORS(object)\ +(((object)->id % NAYBOR_SKIP_FRAMES) == (frameGetFrameNumber() % NAYBOR_SKIP_FRAMES)) + #endif diff --git a/src/ai.c b/src/ai.c index 88fb849ab..1f1a1fea6 100644 --- a/src/ai.c +++ b/src/ai.c @@ -19,6 +19,9 @@ #include "projectile.h" #include "visibility.h" +/* Calculates attack priority for a certain target */ +SDWORD targetAttackWeight(BASE_OBJECT *psTarget, BASE_OBJECT *psAttacker, SDWORD weapon_slot); + // alliances UBYTE alliances[MAX_PLAYERS][MAX_PLAYERS]; @@ -69,36 +72,31 @@ BOOL aiShutdown(void) } // Find the best nearest target for a droid -BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot) +// Returns integer representing target priority, -1 if failed +SDWORD aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot) { - UDWORD i; - SDWORD bestMod,newMod,damage,targetTypeBonus; - BASE_OBJECT *psTarget, *psObj, *bestTarget; - BOOL electronic = FALSE; - WEAPON_EFFECT weaponEffect; + UDWORD i; + SDWORD bestMod,newMod,failure=-1; + BASE_OBJECT *psTarget,*friendlyObj,*bestTarget,*targetInQuestion,*tempTarget; + BOOL electronic = FALSE; STRUCTURE *targetStructure; - DROID *targetDroid; + WEAPON_EFFECT weaponEffect; + DROID *friendlyDroid; - if ((psDroid->id % 8) == (frameGetFrameNumber() % 8)) - { - droidGetNaybors(psDroid); - } - else - { - return FALSE; - } //don't bother looking if empty vtol droid if (vtolEmpty(psDroid)) { - return FALSE; + return failure; } /* 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 FALSE; + return failure; + + droidGetNaybors(psDroid); //weaponMod = asWeaponModifier[weaponEffect][(asPropulsionStats + ((DROID*)psObj)->asBits[COMP_PROPULSION].nStat)->propulsionType]; weaponEffect = ((WEAPON_STATS *)(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat))->weaponEffect; @@ -110,161 +108,132 @@ BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot) bestMod = 0; for (i=0; i< numNaybors; i++) { - if (asDroidNaybors[i].psObj->player != psDroid->player && - (asDroidNaybors[i].psObj->type == OBJ_DROID || - asDroidNaybors[i].psObj->type == OBJ_STRUCTURE) && - asDroidNaybors[i].psObj->visible[psDroid->player] && - !aiCheckAlliances(asDroidNaybors[i].psObj->player,psDroid->player)) + friendlyObj = NULL; + targetInQuestion = asDroidNaybors[i].psObj; + + /* This is a friendly unit, check if we can reuse its target */ + if(targetInQuestion->player == psDroid->player || + aiCheckAlliances(targetInQuestion->player,psDroid->player)) { - psObj = asDroidNaybors[i].psObj; - if ( validTarget((BASE_OBJECT *)psDroid, psObj) == 1) + friendlyObj = targetInQuestion; + targetInQuestion = NULL; + + /* Can we see what it is doing? */ + if(friendlyObj->visible[psDroid->player]) + { + if(friendlyObj->type == OBJ_DROID) + { + friendlyDroid = ((DROID *)friendlyObj); + + /* See if friendly droid has a target */ + tempTarget = friendlyDroid->psActionTarget[0]; + if(tempTarget != NULL) + { + //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) + { + //(WEAPON_STATS *)(asWeaponStats + ((DROID *)friendlyObj)->asWeaps[0].nStat)->; + + // make sure target is near enough + if(dirtySqrt(psDroid->x,psDroid->y,tempTarget->x,tempTarget->y) + < (psDroid->sensorRange * 2)) + { + targetInQuestion = tempTarget; //consider this target + } + } + } + } + } + else if(friendlyObj->type == OBJ_STRUCTURE) + { + targetInQuestion = ((STRUCTURE *)friendlyObj)->psTarget[0]; + } + } + } + + if (targetInQuestion != NULL && + targetInQuestion != (BASE_OBJECT *)psDroid && //in case friendly unit had me as target + (targetInQuestion->type == OBJ_DROID || + targetInQuestion->type == OBJ_STRUCTURE) && + targetInQuestion->visible[psDroid->player] && + targetInQuestion->player != psDroid->player && + !aiCheckAlliances(targetInQuestion->player,psDroid->player)) + { + + if ( validTarget((BASE_OBJECT *)psDroid, targetInQuestion) == INVALID_TARGET) { continue; } - else if (psObj->type == OBJ_DROID) + else if (targetInQuestion->type == OBJ_DROID) { //in multiPlayer - don't attack Transporters with EW if (bMultiPlayer) { //if not electronic then valid target - if (!electronic OR (electronic AND ((DROID *)psObj)-> + if (!electronic OR (electronic AND ((DROID *)targetInQuestion)-> droidType != DROID_TRANSPORTER)) { //only a valid target if NOT a transporter - psTarget = psObj; - // break; + psTarget = targetInQuestion; } } else { - psTarget = psObj; - //break; + psTarget = targetInQuestion; } } - else if (psObj->type == OBJ_STRUCTURE) + else if (targetInQuestion->type == OBJ_STRUCTURE) { if (electronic) { /*don't want to target structures with resistance of zero if using electronic warfare*/ -// if (((STRUCTURE *)psObj)->pStructureType->resistance != 0)// AND - //((STRUCTURE *)psObj)->resistance >= (SDWORD)(((STRUCTURE *) - //psObj)->pStructureType->resistance)) - //((STRUCTURE *)psObj)->resistance >= (SDWORD) - //structureResistance(((STRUCTURE *)psObj)->pStructureType, - //psObj->player)) - if (validStructResistance((STRUCTURE *)psObj)) +// if (((STRUCTURE *)targetInQuestion)->pStructureType->resistance != 0)// AND + //((STRUCTURE *)targetInQuestion)->resistance >= (SDWORD)(((STRUCTURE *) + //targetInQuestion)->pStructureType->resistance)) + //((STRUCTURE *)targetInQuestion)->resistance >= (SDWORD) + //structureResistance(((STRUCTURE *)targetInQuestion)->pStructureType, + //targetInQuestion->player)) + if (validStructResistance((STRUCTURE *)targetInQuestion)) { - psTarget = psObj; - //break; + psTarget = targetInQuestion; } } - //else if (((STRUCTURE *)psObj)->numWeaps > 0) - else if (((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat > 0) + //else if (((STRUCTURE *)targetInQuestion)->numWeaps > 0) + else if (((STRUCTURE *)targetInQuestion)->asWeaps[weapon_slot].nStat > 0) { // structure with weapons - go for this - psTarget = psObj; - //break; + psTarget = targetInQuestion; } - else if ( ( ((STRUCTURE *)psObj)->pStructureType->type != REF_WALL - &&((STRUCTURE *)psObj)->pStructureType->type != REF_WALLCORNER + else if ( ( ((STRUCTURE *)targetInQuestion)->pStructureType->type != REF_WALL + &&((STRUCTURE *)targetInQuestion)->pStructureType->type != REF_WALLCORNER ) || driveModeActive() || (bMultiPlayer && game.type == SKIRMISH && !isHumanPlayer(psDroid->player)) ) { - psTarget = psObj; + psTarget = targetInQuestion; } } /* Check if our weapon is most effective against this object */ - if(psTarget != NULL && psTarget == psObj) //was assigned? + if(psTarget != NULL && psTarget == targetInQuestion) //was assigned? { - targetTypeBonus = 0; //Sensors/ecm droids, non-military structures get lower priority - - if(psTarget->type == OBJ_DROID) - { - targetDroid = (DROID *)psTarget; - - /* Calculate damage this target suffered */ - damage = targetDroid->originalBody - targetDroid->body; - - /* See if this type of a droid should be prioritized */ - switch (targetDroid->droidType) - { - case DROID_CYBORG: - case DROID_WEAPON: - case DROID_CYBORG_SUPER: - case DROID_COMMAND: //or should it get more priority? - targetTypeBonus = WEIGHT_WEAPON_DROIDS; - break; - - case DROID_CONSTRUCT: - case DROID_REPAIR: - case DROID_CYBORG_CONSTRUCT: - case DROID_CYBORG_REPAIR: - targetTypeBonus = WEIGHT_SERVICE_DROIDS; - break; - } - - /* Now calculate the overall weight */ - newMod = asWeaponModifier[weaponEffect][(asPropulsionStats + targetDroid->asBits[COMP_PROPULSION].nStat)->propulsionType] //Weapon effect - - (WEIGHT_DIST_TILE_DROID * (dirtySqrt(psDroid->x, psDroid->y,targetDroid->x,targetDroid->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_DROID per tile, 128 world units in a tile - + (damage * 10 / targetDroid->originalBody ) * WEIGHT_HEALTH_DROID //we prefer damaged droids - + targetTypeBonus; //some droid types have higher priority - } - else if (psTarget->type == OBJ_STRUCTURE) - { - targetStructure = (STRUCTURE *)psTarget; - - /* Calculate damage this target suffered */ - damage = structureBody(targetStructure) - targetStructure->body; - - /* See if this type of a structure should be prioritized */ - switch(targetStructure->pStructureType->type) - { - case REF_DEFENSE: - targetTypeBonus = WEIGHT_WEAPON_STRUCT; - break; - - case REF_FACTORY: - case REF_CYBORG_FACTORY: - case REF_REPAIR_FACILITY: - targetTypeBonus = WEIGHT_MILITARY_STRUCT; - break; - } - - /* Now calculate the overall weight */ - newMod = asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] //Weapon effect - - (WEIGHT_DIST_TILE_STRUCT * (dirtySqrt(psDroid->x, psDroid->y,targetStructure->x,targetStructure->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_STRUCT per tile, 128 world units in a tile - + (damage * 10 / structureBody(targetStructure) ) * WEIGHT_HEALTH_STRUCT //we prefer damaged structures - + targetTypeBonus; //some structure types have higher priority - - /* Go for unfinished structures only if nothing else found (same for non-visible structures) */ - if(targetStructure->status != SS_BUILT) //a decoy? - newMod /= WEIGHT_STRUCT_NOTBUILT_F; - - } - else - { - continue; - } - - /* We prefer objects we can see and can attack immediately */ - if(!visibleObjWallBlock((BASE_OBJECT *)psDroid, psTarget)) - { - newMod /= WEIGHT_NOT_VISIBLE_F; - } + newMod = targetAttackWeight(psTarget, (BASE_OBJECT *)psDroid, weapon_slot); /* Remember this one if it's our best target so far */ - if( newMod > bestMod || bestTarget == NULL) + if( newMod >= 0 && (newMod > bestMod || bestTarget == NULL)) { bestMod = newMod; - bestTarget = psObj; + bestTarget = psTarget; } } } + } if (bestTarget) @@ -280,10 +249,118 @@ BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot) } *ppsObj = bestTarget; - return TRUE; + return bestMod; } - return FALSE; + return failure; +} + +/* Calculates attack priority for a certain target */ +SDWORD targetAttackWeight(BASE_OBJECT *psTarget, BASE_OBJECT *psAttacker, SDWORD weapon_slot) +{ + SDWORD targetTypeBonus,damage,attackWeight=0,noTarget=-1; + DROID *targetDroid; + STRUCTURE *targetStructure; + WEAPON_EFFECT weaponEffect; + + if(psTarget == NULL || psAttacker == NULL){ + return noTarget; + } + + targetTypeBonus = 0; //Sensors/ecm droids, non-military structures get lower priority + + /* Get attacker weapon effect */ + if(psAttacker->type == OBJ_DROID){ + weaponEffect = ((WEAPON_STATS *)(asWeaponStats + ((DROID *)psAttacker)->asWeaps[weapon_slot].nStat))->weaponEffect; + }else if(psAttacker->type == OBJ_STRUCTURE){ + weaponEffect = ((WEAPON_STATS *)(asWeaponStats + ((STRUCTURE *)psAttacker)->asWeaps[weapon_slot].nStat))->weaponEffect; + } + else /* feature */ + { + ASSERT(FALSE, "targetAttackWeight: Invalid attacker object type"); + return noTarget; + } + + /* Calculate attack weight */ + if(psTarget->type == OBJ_DROID) + { + targetDroid = (DROID *)psTarget; + + /* Calculate damage this target suffered */ + damage = targetDroid->originalBody - targetDroid->body; + + /* See if this type of a droid should be prioritized */ + switch (targetDroid->droidType) + { + case DROID_CYBORG: + case DROID_WEAPON: + case DROID_CYBORG_SUPER: + case DROID_COMMAND: //or should it get more priority? + targetTypeBonus = WEIGHT_WEAPON_DROIDS; + break; + + case DROID_CONSTRUCT: + case DROID_REPAIR: + case DROID_CYBORG_CONSTRUCT: + case DROID_CYBORG_REPAIR: + targetTypeBonus = WEIGHT_SERVICE_DROIDS; + break; + } + + /* Now calculate the overall weight */ + attackWeight = asWeaponModifier[weaponEffect][(asPropulsionStats + targetDroid->asBits[COMP_PROPULSION].nStat)->propulsionType] //Weapon effect + - (WEIGHT_DIST_TILE_DROID * (dirtySqrt(psAttacker->x, psAttacker->y,targetDroid->x,targetDroid->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_DROID per tile, 128 world units in a tile + + (damage * 10 / targetDroid->originalBody ) * WEIGHT_HEALTH_DROID //we prefer damaged droids + + targetTypeBonus; //some droid types have higher priority + } + else if(psTarget->type == OBJ_STRUCTURE) + { + targetStructure = (STRUCTURE *)psTarget; + + /* Calculate damage this target suffered */ + damage = structureBody(targetStructure) - targetStructure->body; + + /* See if this type of a structure should be prioritized */ + switch(targetStructure->pStructureType->type) + { + case REF_DEFENSE: + targetTypeBonus = WEIGHT_WEAPON_STRUCT; + break; + + case REF_RESOURCE_EXTRACTOR: + targetTypeBonus = WEIGHT_DERRICK_STRUCT; + break; + + case REF_FACTORY: + case REF_CYBORG_FACTORY: + case REF_REPAIR_FACILITY: + targetTypeBonus = WEIGHT_MILITARY_STRUCT; + break; + } + + /* Now calculate the overall weight */ + attackWeight = asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] //Weapon effect + - (WEIGHT_DIST_TILE_STRUCT * (dirtySqrt(psAttacker->x, psAttacker->y,targetStructure->x,targetStructure->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_STRUCT per tile, 128 world units in a tile + + (damage * 10 / structureBody(targetStructure) ) * WEIGHT_HEALTH_STRUCT //we prefer damaged structures + + targetTypeBonus; //some structure types have higher priority + + /* Go for unfinished structures only if nothing else found (same for non-visible structures) */ + if(targetStructure->status != SS_BUILT) //a decoy? + attackWeight /= WEIGHT_STRUCT_NOTBUILT_F; + + } + else //a feature + { + return noTarget; + } + + /* We prefer objects we can see and can attack immediately */ + if(!visibleObjWallBlock((BASE_OBJECT *)psAttacker, psTarget)) + { + attackWeight /= WEIGHT_NOT_VISIBLE_F; + } + + return attackWeight; } @@ -336,7 +413,7 @@ static BOOL aiObjIsWall(BASE_OBJECT *psObj) /* See if there is a target in range */ // Watermelon:to accept another int value weapon_slot BOOL aiChooseTarget(BASE_OBJECT *psObj, - BASE_OBJECT **ppsTarget,int weapon_slot) + BASE_OBJECT **ppsTarget,int weapon_slot, BOOL bUpdateTarget) { UDWORD radSquared; // UDWORD player; @@ -348,6 +425,8 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, DROID *psCommander; BOOL bCommanderBlock; UDWORD sensorRange; + SECONDARY_STATE state; + SDWORD curTargetWeight=-1,newTargetWeight; //Watermelon:weapon_slot to store which turrent is InAttackRange /* Get the sensor range */ @@ -398,7 +477,19 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, /* See if there is a something in range */ if (psObj->type == OBJ_DROID) { - if (aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot)) + /* find a new target */ + newTargetWeight = aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot); + + /* Calculate weight of the current target of updating */ + if(bUpdateTarget){ + curTargetWeight = targetAttackWeight(((DROID *)psObj)->psActionTarget[0], 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 + )) { /*check its a valid target*/ //Watermelon:Greater than 1 for now @@ -407,7 +498,9 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, /* See if in sensor range */ xdiff = psTarget->x - psObj->x; ydiff = psTarget->y - psObj->y; - if (xdiff*xdiff + ydiff*ydiff < (SDWORD)radSquared) + if ((xdiff*xdiff + ydiff*ydiff < (SDWORD)radSquared) || //target is within our sensor range + (secondaryGetState((DROID *)psObj, DSO_HALTTYPE, &state) && //in case we got this target from a friendly unit see if can pursue it + (state != DSS_HALT_HOLD))) //make sure it's guard or pursue { *ppsTarget = psTarget; return TRUE; @@ -473,7 +566,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, { /*check its a valid target*/ //Watermelon:Greater than 1 for now - if ( (validTarget(psObj, psCStruct->psTarget[0]) > 1) && + if ( (validTarget(psObj, psCStruct->psTarget[0]) > INVALID_TARGET) && aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot)) { xdiff = (SDWORD)psCStruct->psTarget[0]->x - (SDWORD)psObj->x; @@ -491,7 +584,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, psCStruct->psTarget[0] != NULL) { /*check its a valid target*/ - if ( (validTarget(psObj, psCStruct->psTarget[0]) > 1) && + if ( (validTarget(psObj, psCStruct->psTarget[0]) > INVALID_TARGET) && aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot)) { xdiff = (SDWORD)psCStruct->psTarget[0]->x - (SDWORD)psObj->x; @@ -524,7 +617,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj, { /*check its a valid target*/ //Watermelon:Greater than 1 for now - if ( (validTarget(psObj, psCurr) > 1) && + if ( (validTarget(psObj, psCurr) > INVALID_TARGET) && !aiObjIsWall(psCurr)) { // See if in sensor range and visible @@ -650,9 +743,9 @@ BOOL aiChooseSensorTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget) } /* See if there is a something in range */ - if (psObj->type == OBJ_DROID) + if (psObj->type == OBJ_DROID && CAN_UPDATE_NAYBORS( (DROID *)psObj )) { - if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0)) + if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0) >= 0) { /* See if in sensor range */ xdiff = psTarget->x - psObj->x; @@ -761,17 +854,20 @@ void aiUpdateDroid(DROID *psDroid) //Watermelon:psTarget Array BASE_OBJECT *psTarget[DROID_MAXWEAPS]; SECONDARY_STATE state; - BOOL lookForTarget; + BOOL lookForTarget,updateTarget; // BOOL bTemp; ASSERT( PTRVALID(psDroid, sizeof(DROID)), "updateUnitAI: invalid Unit pointer" ); lookForTarget = TRUE; + updateTarget = TRUE; + // don't look for a target if sulking if (psDroid->action == DACTION_SULK) { lookForTarget = FALSE; + updateTarget = FALSE; } // don't look for a target if doing something else if (!orderState(psDroid, DORDER_NONE) && @@ -779,11 +875,32 @@ void aiUpdateDroid(DROID *psDroid) { lookForTarget = FALSE; } + + /* Only try to update target if already have some target */ + if (psDroid->action != DACTION_ATTACK && + psDroid->action != DACTION_ATTACK_M && + psDroid->action != DACTION_MOVEFIRE && + psDroid->action != DACTION_MOVETOATTACK && + psDroid->action != DACTION_ROTATETOATTACK) + { + updateTarget = FALSE; + } + + /* Don't update target if we are sent to attack and reached + attack destination (attacking our target) */ + if((orderState(psDroid, DORDER_ATTACK) || orderState(psDroid, DORDER_ATTACK_M)) + && (psDroid->psActionTarget[0] == psDroid->psTarget[0])) + { + 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) && @@ -793,6 +910,7 @@ void aiUpdateDroid(DROID *psDroid) (state == DSS_HALT_GUARD)) { lookForTarget = FALSE; + updateTarget = FALSE; } // don't allow units to start attacking if they will switch to guarding the commander @@ -800,14 +918,15 @@ void aiUpdateDroid(DROID *psDroid) psDroid->psGroup->type == GT_COMMAND) { lookForTarget = FALSE; + updateTarget = FALSE; } if(bMultiPlayer && vtolDroid(psDroid) && isHumanPlayer(psDroid->player)) { lookForTarget = FALSE; + updateTarget = FALSE; } - // do not choose another target if doing anything while guarding if (orderState(psDroid, DORDER_GUARD) && (psDroid->action != DACTION_NONE)) @@ -823,14 +942,16 @@ void aiUpdateDroid(DROID *psDroid) // do not look for a target if droid is currently under direct control. if(driveModeActive() && (psDroid == driveGetDriven())) { lookForTarget = FALSE; + updateTarget = FALSE; } - // only computer senosr droids in the single player game aquire targets + // only computer sensor droids in the single player game aquire targets if ((psDroid->droidType == DROID_SENSOR && psDroid->player == selectedPlayer) && !bMultiPlayer ) { lookForTarget = FALSE; + updateTarget = FALSE; } // do not attack if the attack level is wrong @@ -842,10 +963,32 @@ void aiUpdateDroid(DROID *psDroid) } } - /* Null target - see if there is an enemy to attack */ - if (lookForTarget && - aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget[0], 0)) + /* Don't rebuild 'Naybor' list too often */ + if(!CAN_UPDATE_NAYBORS(psDroid)) { + 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) && ((psDroid->droidType == DROID_COMMAND) || + !(psDroid->psGroup && (psDroid->psGroup->type == GT_COMMAND)))) //not assigned to commander + { + if((psDroid->id % TARGET_UPD_SKIP_FRAMES) == + (frameGetFrameNumber() % TARGET_UPD_SKIP_FRAMES)) + { + (void)updateAttackTarget((BASE_OBJECT*)psDroid, 0); + } + } + } + + /* Null target - see if there is an enemy to attack */ + if (lookForTarget && !updateTarget && + aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget[0], 0, FALSE)) + { + //console("Choosing first-time target"); // my_error("",0,"","Droid(%s) attacking : %d\n", // psDroid->pName, psTarget->id ); @@ -871,7 +1014,7 @@ can fire on the propulsion type of the target*/ int validTarget(BASE_OBJECT *psObject, BASE_OBJECT *psTarget) { //Watermelon:return value int - int ValidTarget = 1; + int ValidTarget = INVALID_TARGET; int i; BOOL bTargetInAir; //BOOL bTargetInAir, bValidTarget; @@ -975,3 +1118,43 @@ int validTarget(BASE_OBJECT *psObject, BASE_OBJECT *psTarget) return ValidTarget; } + +/* Make droid/structure look for a better target */ +BOOL updateAttackTarget(BASE_OBJECT * psAttacker, SDWORD weapon_slot) +{ + BASE_OBJECT *psBetterTarget=NULL; + DROID *psDroid; + + psBetterTarget = NULL; + + if(aiChooseTarget(psAttacker, &psBetterTarget, weapon_slot, TRUE)) //update target + { + //if(psAttacker->player == selectedPlayer) + // console("GOT A NEW TARGET"); + + if(psAttacker->type == OBJ_DROID) + { + psDroid = (DROID *)psAttacker; + if( orderState(psDroid, DORDER_NONE) || + orderState(psDroid, DORDER_GUARD) || + orderState(psDroid, DORDER_ATTACKTARGET)) + { + orderDroidObj((DROID *)psAttacker, DORDER_ATTACKTARGET, psBetterTarget); + } + else //can't override current order + { + //psDroid->action = DACTION_MOVEFIRE; + psDroid->psActionTarget[0] = psBetterTarget; + } + } + else if (psAttacker->type == OBJ_STRUCTURE) + { + ((STRUCTURE *)psAttacker)->psTarget[weapon_slot] = psBetterTarget; + } + + return TRUE; + } + + + return FALSE; +} diff --git a/src/ai.h b/src/ai.h index 9b4ece864..c17fb7d17 100644 --- a/src/ai.h +++ b/src/ai.h @@ -20,22 +20,27 @@ #define ALLIANCES_TEAMS 2 //locked teams //#define GROUP_WINS 2 -/* Weights used for target selection code */ -#define WEIGHT_DIST_TILE 11 //In points used in weaponmodifier.txt and structuremodifier.txt -#define WEIGHT_DIST_TILE_DROID WEIGHT_DIST_TILE //How much weight a distance of 1 tile (128 world units) has when looking for the best nearest target -#define WEIGHT_DIST_TILE_STRUCT WEIGHT_DIST_TILE -#define WEIGHT_HEALTH_DROID WEIGHT_DIST_TILE //How much weight unit damage has (per 10% of damage, ie for each 10% of damage we add WEIGHT_HEALTH_DROID) - //~100% damage should be ~8 tiles (max sensor range) -#define WEIGHT_HEALTH_STRUCT WEIGHT_DIST_TILE +/* Weights used for target selection code, + * target distance is used as 'common currency' + */ +#define WEIGHT_DIST_TILE 11 //In points used in weaponmodifier.txt and structuremodifier.txt +#define WEIGHT_DIST_TILE_DROID WEIGHT_DIST_TILE //How much weight a distance of 1 tile (128 world units) has when looking for the best nearest target +#define WEIGHT_DIST_TILE_STRUCT WEIGHT_DIST_TILE +#define WEIGHT_HEALTH_DROID WEIGHT_DIST_TILE //How much weight unit damage has (per 10% of damage, ie for each 10% of damage we add WEIGHT_HEALTH_DROID) + //~100% damage should be ~8 tiles (max sensor range) +#define WEIGHT_HEALTH_STRUCT WEIGHT_DIST_TILE -#define WEIGHT_NOT_VISIBLE_F 10 //We really don't like objects we can't see +#define WEIGHT_NOT_VISIBLE_F 10 //We really don't like objects we can't see -#define WEIGHT_SERVICE_DROIDS (WEIGHT_DIST_TILE_DROID * 5) //We don't want them to be repairing droids or structures while we are after them -#define WEIGHT_WEAPON_DROIDS (WEIGHT_DIST_TILE_DROID * 3) //We prefer to go after anything that has a gun and can hurt us -#define WEIGHT_MILITARY_STRUCT WEIGHT_DIST_TILE_STRUCT //Droid/cyborg factories, repair facility; shouldn't have too much weight -#define WEIGHT_WEAPON_STRUCT WEIGHT_WEAPON_DROIDS //Same as weapon droids (?) +#define WEIGHT_SERVICE_DROIDS (WEIGHT_DIST_TILE_DROID * 5) //We don't want them to be repairing droids or structures while we are after them +#define WEIGHT_WEAPON_DROIDS (WEIGHT_DIST_TILE_DROID * 3) //We prefer to go after anything that has a gun and can hurt us +#define WEIGHT_MILITARY_STRUCT WEIGHT_DIST_TILE_STRUCT //Droid/cyborg factories, repair facility; shouldn't have too much weight +#define WEIGHT_WEAPON_STRUCT WEIGHT_WEAPON_DROIDS //Same as weapon droids (?) +#define WEIGHT_DERRICK_STRUCT (WEIGHT_WEAPON_STRUCT + WEIGHT_DIST_TILE_STRUCT * 4) //Even if it's 4 tiles further away than defenses we still choose it -#define WEIGHT_STRUCT_NOTBUILT_F 8 //Humans won't fool us anymore! +#define WEIGHT_STRUCT_NOTBUILT_F 8 //Humans won't fool us anymore! + +#define OLD_TARGET_THRESHOLD (WEIGHT_DIST_TILE * 4) //it only makes sense to switch target if new one is 4+ tiles closer // alliances extern UBYTE alliances[MAX_PLAYERS][MAX_PLAYERS]; @@ -55,12 +60,13 @@ extern BOOL aiInitDroid(DROID *psDroid); /* Do the AI for a droid */ extern void aiUpdateDroid(DROID *psDroid); -// Find the nearest target to a droid added int weapon_slot -extern BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot); +// Find the nearest best target for a droid +// returns integer representing quality of choice, -1 if failed +extern SDWORD aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot); /* See if there is a target in range added int weapon_slot*/ extern BOOL aiChooseTarget(BASE_OBJECT *psObj, - BASE_OBJECT **ppsTarget, int weapon_slot); + BASE_OBJECT **ppsTarget, int weapon_slot, BOOL bUpdateTarget); /*set the droid to attack if wihin range otherwise move to target*/ extern void attackTarget(DROID *psDroid, BASE_OBJECT *psTarget); diff --git a/src/combat.c b/src/combat.c index 59accc8a6..9f536acbc 100644 --- a/src/combat.c +++ b/src/combat.c @@ -129,7 +129,7 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in psStats = asWeaponStats + psWeap->nStat; //check valid weapon/prop combination - if (validTarget(psAttacker, psTarget) == 1) + if (validTarget(psAttacker, psTarget) == INVALID_TARGET) { return; } diff --git a/src/droid.c b/src/droid.c index 0f39bcf53..8ebed96c4 100644 --- a/src/droid.c +++ b/src/droid.c @@ -208,18 +208,13 @@ BOOL droidDamage(DROID *psDroid, UDWORD damage, UDWORD weaponClass, UDWORD weapo //TOP if (angle == HIT_ANGLE_TOP) { - impact_side = HIT_SIDE_TOP; //4 + impact_side = HIT_SIDE_TOP; //4 } //BOTTOM else if (angle == HIT_ANGLE_BOTTOM) { impact_side = HIT_SIDE_BOTTOM; //5 } - //FRONT - else if (angle <= 45 || angle >= 315) - { - impact_side = HIT_SIDE_FRONT; //0 - } //RIGHT else if (angle > 45 && angle < 135) { @@ -235,6 +230,11 @@ BOOL droidDamage(DROID *psDroid, UDWORD damage, UDWORD weaponClass, UDWORD weapo { impact_side = HIT_SIDE_LEFT; //2 } + //FRONT - default + else //if (angle <= 45 || angle >= 315) + { + impact_side = HIT_SIDE_FRONT; //0 + } debug( LOG_ATTACK, "unitDamage(%d): body %d armour %d damage: %d\n", psDroid->id, psDroid->body, psDroid->armour[impact_side][WC_KINETIC], damage); diff --git a/src/droid.h b/src/droid.h index 69ddc99aa..266df99fd 100644 --- a/src/droid.h +++ b/src/droid.h @@ -31,7 +31,7 @@ extern DROID_TEMPLATE *apsDroidTemplates[MAX_PLAYERS]; /* The range for neighbouring objects */ -#define NAYBOR_RANGE (TILE_UNITS*8) +#define NAYBOR_RANGE (TILE_UNITS*9) //range of lancer, BB, TK etc //used to stop structures being built too near the edge and droids being placed down - pickATile #define TOO_NEAR_EDGE 3 @@ -52,7 +52,7 @@ typedef enum } PICKTILE; /* Store for the objects near the droid currently being updated */ -#define MAX_NAYBORS 100 +#define MAX_NAYBORS 120 extern NAYBOR_INFO asDroidNaybors[MAX_NAYBORS]; extern UDWORD numNaybors; diff --git a/src/keybind.c b/src/keybind.c index 2871c36e2..2945806f9 100644 --- a/src/keybind.c +++ b/src/keybind.c @@ -632,6 +632,12 @@ void kf_ToggleCamera( void ) camToggleStatus(); } +/* Toggle 'watch' window on/off */ +void kf_ToggleWatchWindow( void ) +{ + (void)addDebugMenu(!DebugMenuUp); +} + // -------------------------------------------------------------------------- /* Simulates a close down */ diff --git a/src/keybind.h b/src/keybind.h index 10bc52a00..38ad21069 100644 --- a/src/keybind.h +++ b/src/keybind.h @@ -207,6 +207,8 @@ extern void kf_NormalSpeed( void ); void kf_ToggleRadarTerrain( void ); //radar terrain void kf_ToggleRadarAllyEnemy( void ); //enemy/ally color toggle +extern void kf_ToggleWatchWindow( void ); + extern int fogCol; #endif diff --git a/src/keymap.c b/src/keymap.c index bc28af38b..382b7b932 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -158,7 +158,6 @@ _keymapsave keyMapSaveTable[] = kf_SelectAllUnits, kf_SelectAllVTOLs, kf_SelectAllWheeled, - kf_FinishResearch, kf_FrameRate, kf_SelectAllSameType, kf_SelectNextFactory, @@ -224,6 +223,7 @@ _keymapsave keyMapSaveTable[] = kf_DownDroidScale, kf_RaiseGamma, kf_LowerGamma, + kf_ToggleWatchWindow, NULL // last function! }; @@ -424,6 +424,7 @@ void keyInitMappings( BOOL bForceDefaults ) keyAddMapping(KEYMAP__DEBUG,KEY_LCTRL,KEY_G,KEYMAP_PRESSED,kf_ToggleGodMode, "Toggle god Mode Status"); keyAddMapping(KEYMAP__DEBUG,KEY_LCTRL,KEY_O,KEYMAP_PRESSED,kf_ChooseOptions, "Display Options Screen"); keyAddMapping(KEYMAP__DEBUG,KEY_LCTRL,KEY_X,KEYMAP_PRESSED,kf_FinishResearch, "Complete current research"); + keyAddMapping(KEYMAP__DEBUG,KEY_LALT,KEY_SPACE,KEYMAP_PRESSED,kf_ToggleWatchWindow, "Toggle watch window"); saveKeyMap(); // save out the default key mappings. diff --git a/src/multimenu.c b/src/multimenu.c index 0845dd603..d0f53f506 100644 --- a/src/multimenu.c +++ b/src/multimenu.c @@ -1120,9 +1120,31 @@ void addMultiPlayer(UDWORD player,UDWORD pos) } } +/* Output some text to the debug menu */ +void setDebugMenuEntry(char *entry, SDWORD index) +{ + BOOL bAddingNew = FALSE; + + /* New one? */ + if(!strcmp(debugMenuEntry[index],"")) + { + bAddingNew = TRUE; + } + + /* Set */ + strcpy(debugMenuEntry[index], entry); + + /* Re-open it if already open to recalculate height */ + if(DebugMenuUp && bAddingNew) + { + intCloseDebugMenuNoAnim(); + (void)addDebugMenu(TRUE); + } +} + void intCloseDebugMenuNoAnim(void) { - widgDelete(psWScreen, DEBUGMENU_CLOSE); + //widgDelete(psWScreen, DEBUGMENU_CLOSE); widgDelete(psWScreen, DEBUGMENU); DebugMenuUp = FALSE; //intMode = INT_NORMAL; @@ -1192,7 +1214,7 @@ BOOL addDebugMenu(BOOL bAdd) } // Add the close button. - memset(&sButInit, 0, sizeof(W_BUTINIT)); + /* memset(&sButInit, 0, sizeof(W_BUTINIT)); sButInit.formID = DEBUGMENU; sButInit.id = DEBUGMENU_CLOSE; sButInit.style = WBUT_PLAIN; @@ -1207,7 +1229,7 @@ BOOL addDebugMenu(BOOL bAdd) if (!widgAddButton(psWScreen, &sButInit)) { return FALSE; - } + } */ DebugMenuUp = TRUE; diff --git a/src/multimenu.h b/src/multimenu.h index 20e5c8c3e..64b474237 100644 --- a/src/multimenu.h +++ b/src/multimenu.h @@ -23,6 +23,7 @@ extern BOOL intAddMultiMenu (void); extern BOOL addDebugMenu (BOOL bAdd); extern void intCloseDebugMenuNoAnim (void); +extern void setDebugMenuEntry(char *entry, SDWORD index); extern BOOL MultiMenuUp; extern BOOL ClosingMultiMenu; @@ -31,13 +32,13 @@ extern BOOL DebugMenuUp; //extern void intDisplayMiniMultiMenu (void); -#define MULTIMENU 10600 -#define MULTIMENU_FORM MULTIMENU +#define MULTIMENU 10600 +#define MULTIMENU_FORM MULTIMENU #define DEBUGMENU 106000 -#define DEBUGMENU_CLOSE (DEBUGMENU+1) +#define DEBUGMENU_CLOSE (DEBUGMENU+1) #define DEBUGMENU_MAX_ENTRIES 10 -#define DEBUGMENU_BUTTON (DEBUGMENU_CLOSE + DEBUGMENU_MAX_ENTRIES) +#define DEBUGMENU_BUTTON (DEBUGMENU_CLOSE + DEBUGMENU_MAX_ENTRIES) extern char debugMenuEntry[DEBUGMENU_MAX_ENTRIES][MAX_STR_LENGTH]; diff --git a/src/order.c b/src/order.c index 8c241f2b3..07be146e0 100644 --- a/src/order.c +++ b/src/order.c @@ -101,6 +101,7 @@ void initRunData(void) } } +//FIXME: unit doesn't shoot while returning to the guard position // check whether a droid has to move back to the thing it is guarding void orderCheckGuardPosition(DROID *psDroid, SDWORD range) { @@ -580,9 +581,9 @@ if(!bMultiPlayer || myResponsibility(psDroid->player)) case DORDER_SCOUT: case DORDER_PATROL: // if there is an enemy around, attack it - if ( (psDroid->action == DACTION_MOVE) && - (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) && - aiBestNearestTarget(psDroid, &psObj, 0) ) + if ( (psDroid->action == DACTION_MOVE) && CAN_UPDATE_NAYBORS(psDroid) && + (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) && + aiBestNearestTarget(psDroid, &psObj, 0) >= 0 ) { switch (psDroid->droidType) { @@ -643,11 +644,11 @@ if(!bMultiPlayer || myResponsibility(psDroid->player)) } } break; -case DORDER_CIRCLE: + case DORDER_CIRCLE: // if there is an enemy around, attack it - if ( (psDroid->action == DACTION_MOVE) && - (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) && - aiBestNearestTarget(psDroid, &psObj, 0) ) + if ( (psDroid->action == DACTION_MOVE) && CAN_UPDATE_NAYBORS(psDroid) && + (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) && + aiBestNearestTarget(psDroid, &psObj, 0) >= 0) { switch (psDroid->droidType) { @@ -3249,7 +3250,7 @@ DROID_ORDER chooseOrderObj(DROID *psDroid, BASE_OBJECT *psObj) && !aiCheckAlliances(psObj->player , psDroid->player) ) { //check valid weapon/prop combination - if (validTarget((BASE_OBJECT *)psDroid, psObj) == 1) + if (validTarget((BASE_OBJECT *)psDroid, psObj) == INVALID_TARGET) { order = DORDER_NONE; } diff --git a/src/scriptai.c b/src/scriptai.c index 6ed47e572..623000edf 100644 --- a/src/scriptai.c +++ b/src/scriptai.c @@ -1690,7 +1690,7 @@ BOOL scrSkVtolEnableCheck(void) // ******************************************************************************************** BOOL scrSkGetFactoryCapacity(void) { - SDWORD count=0,structure; + SDWORD count=0; STRUCTURE *psStructure; if (!stackPopParams(1,ST_STRUCTURE, &psStructure)) diff --git a/src/scriptfuncs.c b/src/scriptfuncs.c index 5fea49e55..d1ff126e3 100644 --- a/src/scriptfuncs.c +++ b/src/scriptfuncs.c @@ -10590,7 +10590,6 @@ BOOL scrDebugMenu(void) BOOL scrSetDebugMenuEntry(void) { SDWORD index; - BOOL bAddingNew = FALSE; if (!stackPopParams(2, VAL_STRING, &strParam1, VAL_INT, &index)) { @@ -10598,21 +10597,7 @@ BOOL scrSetDebugMenuEntry(void) return FALSE; } - /* New one? */ - if(!strcmp(debugMenuEntry[index],"")) - { - bAddingNew = TRUE; - } - - /* Set */ - strcpy(debugMenuEntry[index], strParam1); - - /* Re-open it if already open to recalculate height */ - if(DebugMenuUp && bAddingNew) - { - intCloseDebugMenuNoAnim(); - (void)addDebugMenu(TRUE); - } + setDebugMenuEntry(strParam1, index); return TRUE; } diff --git a/src/structure.c b/src/structure.c index 23a030b92..b301c7a00 100644 --- a/src/structure.c +++ b/src/structure.c @@ -3995,7 +3995,7 @@ static void aiUpdateStructure(STRUCTURE *psStructure) { if ((psStructure->id % 20) == (frameGetFrameNumber() % 20)) { - if (aiChooseTarget((BASE_OBJECT *)psStructure, &psChosenObj, i)) + if (aiChooseTarget((BASE_OBJECT *)psStructure, &psChosenObj, i, TRUE)) //structures always update their targets { debug( LOG_ATTACK, "Struct(%d) attacking : %d\n", psStructure->id, psChosenObj->id );