mirror of
https://github.com/1dot13/source.git
synced 2026-08-12 14:10:23 +02:00
Various AI improvements.
FindThrowableGrenade: allows to specify type and damage of searched grenade. New counter SOLDIER_COUNTER_RETREAT allows to set number of turns which soldier will try to retreat and take cover. FindBestNearbyCover: use PrepareThreatlist as unified function to make list of all known opponents. Militia Control: - start retreating mode for 2 turns when ordering retreat - replace AI_ACTION_RUN_AWAY with AI_ACTION_TAKE_COVER order so that militia do not retreat from sector New AI behavior: - soldier can use smoke grenade to cover himself when under fire - soldier can use use smoke grenade on his friend to cover him - if soldier used smoke, he starts retreat mode for 2 turns - added retreat code to Red, Black AI - improved Main Red AI for civilians (they only run away) Improved suppression code: - less chance of suppression when opponent has high shock level - prefer using suppression fire when soldier is in cover git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8879 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
+396
-113
@@ -2437,19 +2437,25 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
|
||||
|
||||
INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
INT8 bActionReturned;
|
||||
INT32 iDummy;
|
||||
INT32 iChance,sClosestOpponent,sClosestFriend;
|
||||
INT32 sClosestDisturbance, sDistVisible, sCheckGridNo;
|
||||
UINT8 ubCanMove,ubOpponentDir;
|
||||
INT8 bInWater, bInDeepWater, bInGas;
|
||||
INT8 bSeekPts = 0, bHelpPts = 0, bHidePts = 0, bWatchPts = 0;
|
||||
INT8 bActionReturned;
|
||||
INT32 iDummy;
|
||||
INT32 iChance;
|
||||
INT32 sClosestOpponent = NOWHERE, sClosestFriend = NOWHERE;
|
||||
INT32 sClosestDisturbance = NOWHERE, sCheckGridNo;
|
||||
INT32 sDistVisible;
|
||||
UINT8 ubCanMove,ubOpponentDir;
|
||||
INT8 bInWater, bInDeepWater, bInGas;
|
||||
INT8 bSeekPts = 0, bHelpPts = 0, bHidePts = 0, bWatchPts = 0;
|
||||
INT8 bHighestWatchLoc;
|
||||
ATTACKTYPE BestThrow, BestShot;
|
||||
|
||||
// sevenfm:
|
||||
BOOLEAN fProneSightCover = FALSE;
|
||||
BOOLEAN fDangerousSpot = FALSE;
|
||||
BOOLEAN fAnyCover = FALSE;
|
||||
BOOLEAN fCanBeSeen = FALSE;
|
||||
INT32 sOpponentGridNo;
|
||||
INT8 bOpponentLevel;
|
||||
|
||||
#ifdef AI_TIMING_TEST
|
||||
UINT32 uiStartTime, uiEndTime;
|
||||
@@ -2480,8 +2486,22 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
return(AI_ACTION_NONE);
|
||||
}
|
||||
|
||||
// sevenfm: find closest opponent
|
||||
sClosestOpponent = ClosestKnownOpponent(pSoldier, &sOpponentGridNo, &bOpponentLevel);
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("sClosestOpponent %d", sClosestOpponent));
|
||||
|
||||
if (!SightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE))
|
||||
{
|
||||
fCanBeSeen = TRUE;
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("can be seen"));
|
||||
}
|
||||
|
||||
fProneSightCover = ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE);
|
||||
if( !fProneSightCover || pSoldier->aiData.bUnderFire )
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("prone sight cover %d", fProneSightCover));
|
||||
fAnyCover = AnyCoverAtSpot(pSoldier, pSoldier->sGridNo);
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("any cover %d", fAnyCover));
|
||||
|
||||
if (!fProneSightCover || pSoldier->aiData.bUnderFire)
|
||||
{
|
||||
fDangerousSpot = TRUE;
|
||||
}
|
||||
@@ -2797,6 +2817,68 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
}
|
||||
|
||||
// use smoke to cover friend
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[use smoke to cover friend]"));
|
||||
if (gfTurnBasedAI &&
|
||||
SoldierAI(pSoldier) &&
|
||||
!bInWater &&
|
||||
!bInGas &&
|
||||
!pSoldier->IsFlanking() &&
|
||||
pSoldier->CheckInitialAP() &&
|
||||
!pSoldier->aiData.bUnderFire &&
|
||||
SightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE) &&
|
||||
!AICheckIsSniper(pSoldier) &&
|
||||
!AICheckIsMachinegunner(pSoldier) &&
|
||||
!AICheckIsMortarOperator(pSoldier) &&
|
||||
Chance(100 - min(100, 10 * CountPublicKnownEnemies(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE))) &&
|
||||
!GuySawEnemy(pSoldier, SEEN_LAST_TURN))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("check if we can cover friend with smoke"));
|
||||
|
||||
CheckTossFriendSmoke(pSoldier, &BestThrow);
|
||||
|
||||
if (BestThrow.ubPossible)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime));
|
||||
|
||||
// start retreating for several turns
|
||||
if (BestThrow.ubOpponent != NOBODY && !MercPtrs[BestThrow.ubOpponent]->IsFlanking())
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("start retreat counter for %d", BestThrow.ubOpponent));
|
||||
MercPtrs[BestThrow.ubOpponent]->RetreatCounterStart(2);
|
||||
}
|
||||
|
||||
// if necessary, swap the usItem from holster into the hand position
|
||||
if (BestThrow.bWeaponIn != HANDPOS)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"));
|
||||
RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER);
|
||||
}
|
||||
|
||||
// stand up before throwing if needed
|
||||
if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance &&
|
||||
pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance))
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.ubStance;
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE;
|
||||
pSoldier->aiData.usNextActionData = BestThrow.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
return AI_ACTION_CHANGE_STANCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.sTarget;
|
||||
pSoldier->bTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
}
|
||||
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("throw smoke grenade to cover friend %d at spot %d level %d", BestThrow.ubOpponent, BestThrow.sTarget, BestThrow.bTargetLevel));
|
||||
|
||||
return(AI_ACTION_TOSS_PROJECTILE);
|
||||
}
|
||||
}
|
||||
|
||||
// SNIPER!
|
||||
CheckIfShotPossible(pSoldier, &BestShot);
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: is sniper shot possible? = %d, CTH = %d",BestShot.ubPossible,BestShot.ubChanceToReallyHit));
|
||||
@@ -2935,6 +3017,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
!TileIsOutOfBounds(BestShot.sTarget) &&
|
||||
BestShot.ubOpponent != NOBODY &&
|
||||
MercPtrs[BestShot.ubOpponent] &&
|
||||
Chance(100 - MercPtrs[BestShot.ubOpponent]->ShockLevelPercent() / 2) &&
|
||||
// check weapon/ammo requirements
|
||||
IsGunAutofireCapable(&pSoldier->inv[BestShot.bWeaponIn]) &&
|
||||
GetMagSize(&pSoldier->inv[BestShot.bWeaponIn]) >= gGameExternalOptions.ubAISuppressionMinimumMagSize &&
|
||||
@@ -2943,6 +3026,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
pSoldier->aiData.bOrders != SNIPER &&
|
||||
BestShot.ubFriendlyFireChance <= MIN_CHANCE_TO_ACCIDENTALLY_HIT_SOMEONE &&
|
||||
!AICheckIsFlanking(pSoldier) &&
|
||||
!pSoldier->RetreatCounterValue() &&
|
||||
// check cover
|
||||
(fAnyCover || // safe position
|
||||
!fCanBeSeen && NightLight() && CountFriendsFlankSameSpot(pSoldier, sClosestOpponent) && Chance(50) ||
|
||||
ARMED_VEHICLE(pSoldier) || // tanks don't need cover
|
||||
pSoldier->aiData.bUnderFire && (pSoldier->ubPreviousAttackerID == BestShot.ubOpponent || pSoldier->ubNextToPreviousAttackerID == BestShot.ubOpponent || MercPtrs[BestShot.ubOpponent]->sLastTarget == pSoldier->sGridNo) || // return fire
|
||||
Chance((BestShot.ubChanceToReallyHit + 100) / 2) || // 50% chance to fire without cover
|
||||
SoldierToSoldierLineOfSightTest(pSoldier, MercPtrs[BestShot.ubOpponent], TRUE, CALC_FROM_ALL_DIRS)) && // can see target after turning
|
||||
// reduce chance to shoot if target is beyond weapon range
|
||||
(AICheckIsMachinegunner(pSoldier) ||
|
||||
ARMED_VEHICLE(pSoldier) ||
|
||||
@@ -3277,6 +3368,34 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// RED RETREAT
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[retreat]"));
|
||||
if (gfTurnBasedAI &&
|
||||
!fCivilian &&
|
||||
!bInWater &&
|
||||
ubCanMove &&
|
||||
SoldierAI(pSoldier) &&
|
||||
pSoldier->aiData.bOrders != STATIONARY &&
|
||||
pSoldier->aiData.bOrders != SNIPER &&
|
||||
pSoldier->RetreatCounterValue() > 0 &&
|
||||
(pSoldier->CheckInitialAP() || !fAnyCover || pSoldier->aiData.bUnderFire))
|
||||
{
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("search for retreat spot"));
|
||||
INT32 sRetreatSpot = FindRetreatSpot(pSoldier);
|
||||
|
||||
if (!TileIsOutOfBounds(sRetreatSpot))
|
||||
{
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("found retreat spot %d", sRetreatSpot));
|
||||
|
||||
//BeginMultiPurposeLocator(sRetreatSpot, pSoldier->pathing.bLevel, FALSE);
|
||||
|
||||
pSoldier->aiData.usActionData = sRetreatSpot;
|
||||
return(AI_ACTION_TAKE_COVER);
|
||||
}
|
||||
}
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch and rest if running out of breath");
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// CROUCH & REST IF RUNNING OUT OF BREATH
|
||||
@@ -3429,6 +3548,59 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
}
|
||||
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[self smoke when under fire]"));
|
||||
if (gfTurnBasedAI &&
|
||||
pSoldier->bActionPoints == pSoldier->bInitialActionPoints &&
|
||||
pSoldier->aiData.bUnderFire &&
|
||||
!InARoom(pSoldier->sGridNo, NULL) &&
|
||||
!InSmoke(pSoldier->sGridNo, pSoldier->pathing.bLevel) &&
|
||||
RangeChangeDesire(pSoldier) <= 2 &&
|
||||
(!NightLight() || InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel)) &&
|
||||
!TileIsOutOfBounds(sClosestOpponent) &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestOpponent) > DAY_VISION_RANGE / 4 &&
|
||||
(!fProneSightCover && !AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || pSoldier->TakenLargeHit()) &&
|
||||
(pSoldier->TakenLargeHit() || pSoldier->ShockLevelPercent() > 20 + Random(80)))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("check if soldier can cover himself with smoke"));
|
||||
|
||||
CheckTossSelfSmoke(pSoldier, &BestThrow);
|
||||
|
||||
if (BestThrow.ubPossible)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime));
|
||||
|
||||
// start retreating for several turns
|
||||
pSoldier->RetreatCounterStart(2);
|
||||
|
||||
// if necessary, swap the usItem from holster into the hand position
|
||||
if (BestThrow.bWeaponIn != HANDPOS)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"));
|
||||
RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER);
|
||||
}
|
||||
|
||||
// stand up before throwing if needed
|
||||
if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance &&
|
||||
pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance))
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.ubStance;
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE;
|
||||
pSoldier->aiData.usNextActionData = BestThrow.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
return AI_ACTION_CHANGE_STANCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.sTarget;
|
||||
pSoldier->bTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
}
|
||||
|
||||
return(AI_ACTION_TOSS_PROJECTILE);
|
||||
}
|
||||
}
|
||||
|
||||
// sevenfm: no Main Red AI for civilians
|
||||
if ( (gGameExternalOptions.fEnemyTanksCanMoveInTactical || !ARMED_VEHICLE( pSoldier )) &&
|
||||
!(pSoldier->flags.uiStatusFlags & (SOLDIER_DRIVER | SOLDIER_PASSENGER)) &&
|
||||
@@ -3641,91 +3813,93 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
|
||||
if (ubCanMove && pSoldier->bActionPoints > APBPConstants[MAX_AP_CARRIED])
|
||||
{
|
||||
if (fCivilian)
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("decideactionred: checking hide/seek/help/watch points... orders = %d, attitude = %d", pSoldier->aiData.bOrders, pSoldier->aiData.bAttitude));
|
||||
// calculate initial points for watch based on highest watch loc
|
||||
|
||||
bWatchPts = GetHighestWatchedLocPoints(pSoldier->ubID);
|
||||
if (bWatchPts <= 0)
|
||||
{
|
||||
// only interested in hiding out...
|
||||
bSeekPts = -99;
|
||||
bHelpPts = -99;
|
||||
bHidePts = +1;
|
||||
// no watching
|
||||
bWatchPts = -99;
|
||||
}
|
||||
else
|
||||
|
||||
// modify RED movement tendencies according to morale
|
||||
switch (pSoldier->aiData.bAIMorale)
|
||||
{
|
||||
case MORALE_HOPELESS: bSeekPts = -99; bHelpPts = -99; bHidePts = +2; bWatchPts = -99; break;
|
||||
case MORALE_WORRIED: bSeekPts += -2; bHelpPts += 0; bHidePts += +2; bWatchPts += 1; break;
|
||||
case MORALE_NORMAL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case MORALE_CONFIDENT: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
|
||||
case MORALE_FEARLESS: bSeekPts += +1; bHelpPts += 0; bHidePts = -1; bWatchPts += 0; break;
|
||||
}
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: checking hide/seek/help/watch points... orders = %d, attitude = %d",pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude));
|
||||
// calculate initial points for watch based on highest watch loc
|
||||
// modify tendencies according to orders
|
||||
switch (pSoldier->aiData.bOrders)
|
||||
{
|
||||
case STATIONARY: bSeekPts += -1; bHelpPts += -1; bHidePts += +1; bWatchPts += +1; break;
|
||||
case ONGUARD: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; break;
|
||||
case CLOSEPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case RNDPTPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case POINTPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case FARPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case ONCALL: bSeekPts += 0; bHelpPts += +1; bHidePts += -1; bWatchPts += 0; break;
|
||||
case SEEKENEMY: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += -1; break;
|
||||
case SNIPER: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; break;
|
||||
}
|
||||
|
||||
bWatchPts = GetHighestWatchedLocPoints( pSoldier->ubID );
|
||||
if ( bWatchPts <= 0 )
|
||||
// modify tendencies according to attitude
|
||||
switch (pSoldier->aiData.bAttitude)
|
||||
{
|
||||
case DEFENSIVE: bSeekPts += -1; bHelpPts += 0; bHidePts += +2; bWatchPts += +1; break;
|
||||
case BRAVESOLO: bSeekPts += +1; bHelpPts += -1; bHidePts += -1; bWatchPts += -1; break;
|
||||
case BRAVEAID: bSeekPts += +1; bHelpPts += +1; bHidePts += -1; bWatchPts += -1; break;
|
||||
case CUNNINGSOLO: bSeekPts += 1; bHelpPts += -1; bHidePts += +1; bWatchPts += 0; break;
|
||||
case CUNNINGAID: bSeekPts += 1; bHelpPts += +1; bHidePts += +1; bWatchPts += 0; break;
|
||||
case AGGRESSIVE: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
|
||||
case ATTACKSLAYONLY:bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
|
||||
}
|
||||
|
||||
// sevenfm: snipers and soldiers with scoped guns should decide watch more often
|
||||
if (AIGunScoped(pSoldier) || AICheckIsSniper(pSoldier))
|
||||
{
|
||||
bWatchPts++;
|
||||
}
|
||||
|
||||
// sevenfm: disable watching if soldier is under fire or in dangerous place
|
||||
// don't watch if some friends can see my closest opponent
|
||||
if (fDangerousSpot ||
|
||||
InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel) ||
|
||||
CountFriendsBlack(pSoldier) > 0)
|
||||
{
|
||||
// prefer hiding when in dangerous place
|
||||
if (bHidePts > -90)
|
||||
bWatchPts = min(bWatchPts, bHidePts - 1);
|
||||
else
|
||||
bWatchPts--;
|
||||
}
|
||||
|
||||
// sevenfm: don't watch when overcrowded and not in a building
|
||||
if (!InARoom(pSoldier->sGridNo, NULL))
|
||||
{
|
||||
bWatchPts -= CountNearbyFriends(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE / 8);
|
||||
}
|
||||
|
||||
// sevenfm: don't help if seen enemy recently or under fire
|
||||
if (GuySawEnemy(pSoldier) || pSoldier->aiData.bUnderFire)
|
||||
{
|
||||
bHelpPts -= 10;
|
||||
}
|
||||
|
||||
if (pSoldier->RetreatCounterValue() > 0)
|
||||
{
|
||||
// no seeking when retreating
|
||||
bSeekPts = -99;
|
||||
// no helping when retreating
|
||||
bHelpPts = -99;
|
||||
|
||||
if (bHidePts > -90)
|
||||
{
|
||||
// no watching
|
||||
bWatchPts = -99;
|
||||
}
|
||||
|
||||
// modify RED movement tendencies according to morale
|
||||
switch (pSoldier->aiData.bAIMorale)
|
||||
{
|
||||
case MORALE_HOPELESS: bSeekPts = -99; bHelpPts = -99; bHidePts = +2; bWatchPts = -99; break;
|
||||
case MORALE_WORRIED: bSeekPts += -2; bHelpPts += 0; bHidePts += +2; bWatchPts += 1; break;
|
||||
case MORALE_NORMAL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case MORALE_CONFIDENT: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
|
||||
case MORALE_FEARLESS: bSeekPts += +1; bHelpPts += 0; bHidePts = -1; bWatchPts += 0; break;
|
||||
}
|
||||
|
||||
// modify tendencies according to orders
|
||||
switch (pSoldier->aiData.bOrders)
|
||||
{
|
||||
case STATIONARY: bSeekPts += -1; bHelpPts += -1; bHidePts += +1; bWatchPts += +1; break;
|
||||
case ONGUARD: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; break;
|
||||
case CLOSEPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case RNDPTPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case POINTPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case FARPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
|
||||
case ONCALL: bSeekPts += 0; bHelpPts += +1; bHidePts += -1; bWatchPts += 0; break;
|
||||
case SEEKENEMY: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += -1; break;
|
||||
case SNIPER: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; break;
|
||||
}
|
||||
|
||||
// modify tendencies according to attitude
|
||||
switch (pSoldier->aiData.bAttitude)
|
||||
{
|
||||
case DEFENSIVE: bSeekPts += -1; bHelpPts += 0; bHidePts += +2; bWatchPts += +1; break;
|
||||
case BRAVESOLO: bSeekPts += +1; bHelpPts += -1; bHidePts += -1; bWatchPts += -1; break;
|
||||
case BRAVEAID: bSeekPts += +1; bHelpPts += +1; bHidePts += -1; bWatchPts += -1; break;
|
||||
case CUNNINGSOLO: bSeekPts += 1; bHelpPts += -1; bHidePts += +1; bWatchPts += 0; break;
|
||||
case CUNNINGAID: bSeekPts += 1; bHelpPts += +1; bHidePts += +1; bWatchPts += 0; break;
|
||||
case AGGRESSIVE: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
|
||||
case ATTACKSLAYONLY:bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
|
||||
}
|
||||
|
||||
// sevenfm: snipers and soldiers with scoped guns should decide watch more often
|
||||
if (AIGunScoped(pSoldier) || AICheckIsSniper(pSoldier))
|
||||
{
|
||||
bWatchPts++;
|
||||
}
|
||||
|
||||
// sevenfm: disable watching if soldier is under fire or in dangerous place
|
||||
// don't watch if some friends can see my closest opponent
|
||||
if (fDangerousSpot ||
|
||||
InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel) ||
|
||||
CountFriendsBlack(pSoldier) > 0 )
|
||||
{
|
||||
// prefer hiding when in dangerous place
|
||||
if (bHidePts > -90)
|
||||
bWatchPts = min(bWatchPts, bHidePts - 1);
|
||||
else
|
||||
bWatchPts--;
|
||||
}
|
||||
|
||||
// sevenfm: don't watch when overcrowded and not in a building
|
||||
if( !InARoom(pSoldier->sGridNo, NULL) )
|
||||
{
|
||||
bWatchPts -= CountNearbyFriends(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE / 8);
|
||||
}
|
||||
|
||||
// sevenfm: don't help if seen enemy recently or under fire
|
||||
if( GuySawEnemy(pSoldier) || pSoldier->aiData.bUnderFire )
|
||||
{
|
||||
bHelpPts -= 10;
|
||||
bWatchPts = min(bWatchPts, bHidePts - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4113,7 +4287,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
// if HIDING is possible and at least as desirable as seeking or helping
|
||||
if ((bHidePts > -90) && (bHidePts >= bSeekPts) && (bHidePts >= bHelpPts) && (bHidePts >= bWatchPts ))
|
||||
{
|
||||
sClosestOpponent = ClosestKnownOpponent( pSoldier, NULL, NULL );
|
||||
//sClosestOpponent = ClosestKnownOpponent( pSoldier, NULL, NULL );
|
||||
// if an opponent is known (not necessarily reachable or conscious)
|
||||
if (!SkipCoverCheck && !TileIsOutOfBounds(sClosestOpponent))
|
||||
{
|
||||
@@ -4169,7 +4343,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// if we're currently under fire (presumably, attacker is hidden)
|
||||
if (pSoldier->aiData.bUnderFire || fCivilian)
|
||||
if (pSoldier->aiData.bUnderFire)
|
||||
{
|
||||
// only try to run if we've actually been hit recently & noticably so
|
||||
// otherwise, presumably our current cover is pretty good & sufficient
|
||||
@@ -4189,7 +4363,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
bShock = pSoldier->aiData.bShock;
|
||||
}
|
||||
|
||||
if (bShock > 0 || fCivilian)
|
||||
if (bShock > 0)
|
||||
{
|
||||
// look for best place to RUN AWAY to (farthest from the closest threat)
|
||||
pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier);
|
||||
@@ -4212,36 +4386,63 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch or go prone");
|
||||
// if not in water and not already crouched
|
||||
if (!fCivilian )
|
||||
if (gAnimControl[pSoldier->usAnimState].ubHeight == ANIM_STAND && IsValidStance(pSoldier, ANIM_CROUCH))
|
||||
{
|
||||
if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND && IsValidStance( pSoldier, ANIM_CROUCH ) )
|
||||
if (!gfTurnBasedAI || GetAPsToChangeStance(pSoldier, ANIM_CROUCH) <= pSoldier->bActionPoints)
|
||||
{
|
||||
if (!gfTurnBasedAI || GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints)
|
||||
{
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
sprintf(tempstr,"%s CROUCHES (STATUS RED)",pSoldier->name);
|
||||
AIPopMessage(tempstr);
|
||||
sprintf(tempstr, "%s CROUCHES (STATUS RED)", pSoldier->name);
|
||||
AIPopMessage(tempstr);
|
||||
#endif
|
||||
|
||||
pSoldier->aiData.usActionData = ANIM_CROUCH;
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
pSoldier->aiData.usActionData = ANIM_CROUCH;
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
else if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE )
|
||||
}
|
||||
else if (gAnimControl[pSoldier->usAnimState].ubHeight != ANIM_PRONE)
|
||||
{
|
||||
// maybe go prone
|
||||
if (PreRandom(2) == 0 && IsValidStance(pSoldier, ANIM_PRONE))
|
||||
{
|
||||
// maybe go prone
|
||||
if ( PreRandom( 2 ) == 0 && IsValidStance( pSoldier, ANIM_PRONE ) )
|
||||
{
|
||||
pSoldier->aiData.usActionData = ANIM_PRONE;
|
||||
return( AI_ACTION_CHANGE_STANCE );
|
||||
}
|
||||
|
||||
pSoldier->aiData.usActionData = ANIM_PRONE;
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// civilians are only interested in running away
|
||||
if (fCivilian &&
|
||||
//pSoldier->CheckInitialAP() &&
|
||||
(pSoldier->aiData.bUnderFire ||
|
||||
!TileIsOutOfBounds(sClosestOpponent) && PythSpacesAway(pSoldier->sGridNo, sClosestOpponent) < DAY_VISION_RANGE / 2))
|
||||
//!TileIsOutOfBounds( sClosestNoise ) && PythSpacesAway(pSoldier->sGridNo, sClosestNoise) < DAY_VISION_RANGE / 2) )
|
||||
//CorpseWarning(pSoldier, pSoldier->sGridNo, pSoldier->pathing.bLevel)
|
||||
{
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[civilians run away]"));
|
||||
|
||||
// look for best place to RUN AWAY to (farthest from the closest threat)
|
||||
pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier);
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("found run away spot %d", pSoldier->aiData.usActionData));
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
|
||||
{
|
||||
return(AI_ACTION_RUN_AWAY);
|
||||
}
|
||||
//else if (!pSoldier->SkipCoverCheck() && gfTurnBasedAI) // only do in turnbased
|
||||
else if (!SkipCoverCheck && gfTurnBasedAI) // only do in turnbased
|
||||
{
|
||||
// try to take cover
|
||||
pSoldier->aiData.bAIMorale = MORALE_WORRIED;
|
||||
pSoldier->aiData.usActionData = FindBestNearbyCover(pSoldier, MORALE_WORRIED, &iDummy);
|
||||
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
|
||||
{
|
||||
return(AI_ACTION_TAKE_COVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: look around towards opponent");
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// LOOK AROUND TOWARD CLOSEST KNOWN OPPONENT, IF KNOWN
|
||||
@@ -4251,7 +4452,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
// determine the location of the known closest opponent
|
||||
// (don't care if he's conscious, don't care if he's reachable at all)
|
||||
sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
//sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
|
||||
if (!TileIsOutOfBounds(sClosestOpponent))
|
||||
{
|
||||
@@ -4485,7 +4686,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
// if not in water and not already crouched, try to crouch down first
|
||||
if (!fCivilian && !bInWater && (gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND) && IsValidStance( pSoldier, ANIM_CROUCH ) )
|
||||
{
|
||||
sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
//sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
|
||||
//if ( ( !TileIsOutOfBounds(sClosestOpponent) && PythSpacesAway( pSoldier->sGridNo, sClosestOpponent ) < (MaxNormalDistanceVisible() * 3) / 2 ) || PreRandom( 4 ) == 0 )
|
||||
if ( (!TileIsOutOfBounds(sClosestOpponent) && PythSpacesAway( pSoldier->sGridNo, sClosestOpponent ) < (pSoldier->GetMaxDistanceVisible(sClosestOpponent) * 3) / 2 ) || PreRandom( 4 ) == 0 )
|
||||
@@ -5024,6 +5225,60 @@ INT16 ubMinAPCost;
|
||||
}
|
||||
}
|
||||
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[self smoke when under fire]"));
|
||||
if (SoldierAI(pSoldier) &&
|
||||
gfTurnBasedAI &&
|
||||
pSoldier->bActionPoints == pSoldier->bInitialActionPoints &&
|
||||
pSoldier->aiData.bUnderFire &&
|
||||
!InARoom(pSoldier->sGridNo, NULL) &&
|
||||
!InSmoke(pSoldier->sGridNo, pSoldier->pathing.bLevel) &&
|
||||
RangeChangeDesire(pSoldier) <= 2 &&
|
||||
(!NightLight() || InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel)) &&
|
||||
!TileIsOutOfBounds(sClosestOpponent) &&
|
||||
PythSpacesAway(pSoldier->sGridNo, sClosestOpponent) > DAY_VISION_RANGE / 4 &&
|
||||
(!ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE) && !AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || pSoldier->TakenLargeHit()) &&
|
||||
(pSoldier->TakenLargeHit() || pSoldier->ShockLevelPercent() > 20 + Random(80)))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("check if soldier can cover himself with smoke"));
|
||||
|
||||
CheckTossSelfSmoke(pSoldier, &BestThrow);
|
||||
|
||||
if (BestThrow.ubPossible)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime));
|
||||
|
||||
// start retreating for several turns
|
||||
pSoldier->RetreatCounterStart(3);
|
||||
|
||||
// if necessary, swap the usItem from holster into the hand position
|
||||
if (BestThrow.bWeaponIn != HANDPOS)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"));
|
||||
RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER);
|
||||
}
|
||||
|
||||
// stand up before throwing if needed
|
||||
if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance &&
|
||||
pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance))
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.ubStance;
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE;
|
||||
pSoldier->aiData.usNextActionData = BestThrow.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
return AI_ACTION_CHANGE_STANCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.sTarget;
|
||||
pSoldier->bTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
}
|
||||
|
||||
return(AI_ACTION_TOSS_PROJECTILE);
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't have a gun, look around for a weapon!
|
||||
if (FindAIUsableObjClass( pSoldier, IC_GUN ) == ITEM_NOT_FOUND && ubCanMove && !pSoldier->aiData.bNeutral)
|
||||
{
|
||||
@@ -5580,10 +5835,37 @@ INT16 ubMinAPCost;
|
||||
memset( &BestAttack, 0, sizeof( BestAttack ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NB a desire of 4 or more is only achievable by brave/aggressive guys with high morale
|
||||
UINT16 usRange = BestAttack.bWeaponIn==NO_SLOT ? 0 : GetModifiedGunRange(pSoldier->inv[BestAttack.bWeaponIn].usItem);//dnl ch69 150913
|
||||
INT32 sClosestThreat = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[Black Retreat]"));
|
||||
if (gfTurnBasedAI &&
|
||||
!bInWater &&
|
||||
ubCanMove &&
|
||||
!gfHiddenInterrupt &&
|
||||
!gTacticalStatus.fInterruptOccurred &&
|
||||
SoldierAI(pSoldier) &&
|
||||
pSoldier->aiData.bOrders != STATIONARY &&
|
||||
pSoldier->aiData.bOrders != SNIPER &&
|
||||
pSoldier->RetreatCounterValue() > 0 &&
|
||||
(ubBestAttackAction == AI_ACTION_NONE || ubBestAttackAction == AI_ACTION_FIRE_GUN && (UINT8)BestAttack.ubChanceToReallyHit < Random(10 + pSoldier->ShockLevelPercent() / 4)) &&
|
||||
(pSoldier->CheckInitialAP() || !AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || pSoldier->aiData.bUnderFire))
|
||||
{
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("search for retreat spot"));
|
||||
INT32 sRetreatSpot = FindRetreatSpot(pSoldier);
|
||||
|
||||
if (!TileIsOutOfBounds(sRetreatSpot))
|
||||
{
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("found retreat spot %d", sRetreatSpot));
|
||||
|
||||
//BeginMultiPurposeLocator(sRetreatSpot, pSoldier->pathing.bLevel, FALSE);
|
||||
|
||||
pSoldier->aiData.usActionData = sRetreatSpot;
|
||||
return(AI_ACTION_TAKE_COVER);
|
||||
}
|
||||
}
|
||||
|
||||
if ( (pSoldier->bActionPoints == pSoldier->bInitialActionPoints) &&
|
||||
(ubBestAttackAction == AI_ACTION_FIRE_GUN) &&
|
||||
@@ -6309,7 +6591,7 @@ L_NEWAIM:
|
||||
}
|
||||
else if (pSoldier->bBreath < OKBREATH ||
|
||||
pSoldier->bBreath < pSoldier->bBreathMax &&
|
||||
(pSoldier->bBreath < MercPtrs[ubOpponentID]->bBreath || !pSoldier->aiData.bLastAttackHit && (pSoldier->usSoldierFlagMask2 & SOLDIER_TAKEN_LARGE_HIT)))
|
||||
(pSoldier->bBreath < MercPtrs[ubOpponentID]->bBreath || !pSoldier->aiData.bLastAttackHit && pSoldier->TakenLargeHit()))
|
||||
{
|
||||
// maybe move away from opponent
|
||||
UINT8 ubOpponentDir = AIDirection(pSoldier->sGridNo, sClosestOpponent);
|
||||
@@ -8679,7 +8961,7 @@ INT8 ArmedVehicleDecideActionRed( SOLDIERTYPE *pSoldier)
|
||||
if ( pSoldier->aiData.bUnderFire )
|
||||
{
|
||||
// Flugente: see if we are equipped with a smoke screen. If so, use it do hide us
|
||||
if ( (pSoldier->usSoldierFlagMask2 & SOLDIER_TAKEN_LARGE_HIT) && pSoldier->HasItem( SMOKE_GRENADE ) && IsActionAffordable( pSoldier, AI_ACTION_SELFDETONATE ) )
|
||||
if (pSoldier->TakenLargeHit() && pSoldier->HasItem(SMOKE_GRENADE) && IsActionAffordable(pSoldier, AI_ACTION_SELFDETONATE))
|
||||
{
|
||||
pSoldier->aiData.usActionData = SMOKE_GRENADE;
|
||||
|
||||
@@ -9774,6 +10056,7 @@ void LogDecideInfo(SOLDIERTYPE *pSoldier)
|
||||
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("What I know %d", WhatIKnowThatPublicDont(pSoldier, FALSE)));
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Has Gun %d, Short range weapon %d, Gun Range %d, Gun Ammo %d, Gun Scoped %d ", AICheckHasGun(pSoldier), AICheckShortWeaponRange(pSoldier), AIGunRange(pSoldier), AIGunAmmo(pSoldier), AIGunScoped(pSoldier)));
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("RetreatCounter %d", pSoldier->RetreatCounterValue()));
|
||||
}
|
||||
|
||||
void LogKnowledgeInfo(SOLDIERTYPE *pSoldier)
|
||||
|
||||
Reference in New Issue
Block a user