AI update (by sevenfm):

- removed AI_EXTRA_FLANKING option
- LocationToLocationLineOfSightTest allows to check sight with specified height
- IsActionAffordable allows to specify action to check without faking it
- ClosestReachableDisturbance: correctly calculate path to each disturbance, added safety checks, update for noise checks
- CalcMoraleNew: update
- CalcBestShot: allow shooting at empty vehicles, but they have low priority
- allow AI to fire at dying opponents to avoid AI decision problems
- CheckIfShotPossible: allow any soldier with long range weapon to shoot in RED state (if he can hit)
- Flanking update
- WATCH decision update
- removed distance check for HELP decision to allow vanilla behavior, don't help if seen enemy recently or under fire
- suppression fire update
- pick up item decision: check that location is safe

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8125 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2016-03-26 13:41:54 +00:00
parent 285a7841c2
commit dba9e3eb88
10 changed files with 585 additions and 425 deletions
-1
View File
@@ -1477,7 +1477,6 @@ void LoadGameExternalOptions()
gGameExternalOptions.fAIExtraSuppression = iniReader.ReadBoolean("Tactical Interface Settings","AI_EXTRA_SUPPRESSION",FALSE);
gGameExternalOptions.fAINewMorale = iniReader.ReadBoolean("Tactical Interface Settings","AI_NEW_MORALE",FALSE);
gGameExternalOptions.fAIBetterCover = iniReader.ReadBoolean("Tactical Interface Settings","AI_BETTER_COVER",FALSE);
gGameExternalOptions.fAIExtraFlanking = iniReader.ReadBoolean("Tactical Interface Settings","AI_EXTRA_FLANKING",FALSE);
// Heahshot penalty
gGameExternalOptions.uShotHeadPenalty = iniReader.ReadFloat("Tactical Interface Settings","SHOT_HEAD_PENALTY",3,0,100);
-1
View File
@@ -806,7 +806,6 @@ typedef struct
BOOLEAN fAIExtraSuppression;
BOOLEAN fAINewMorale;
BOOLEAN fAIBetterCover;
BOOLEAN fAIExtraFlanking;
FLOAT uShotHeadPenalty;
FLOAT fShotHeadMultiplier;
INT16 iPenaltyShootUnSeen;
+12 -7
View File
@@ -2501,20 +2501,22 @@ INT32 SoldierToVirtualSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, INT32
return( LineOfSightTest( (FLOAT) CenterX( pStartSoldier->sGridNo ), (FLOAT) CenterY( pStartSoldier->sGridNo ), dStartZPos, (FLOAT) sXPos, (FLOAT) sYPos, dEndZPos, iTileSightLimit, bAware, HasThermalOptics( pStartSoldier), NULL, false ) );
}
INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, INT32 sEndGridNo, INT8 bEndLevel, INT8 bAware, int iTileSightLimit )
INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, INT32 sEndGridNo, INT8 bEndLevel, INT8 bAware, int iTileSightLimit, FLOAT dStartPos, FLOAT dEndPos )
{
FLOAT dStartZPos, dEndZPos;
INT16 sStartXPos, sStartYPos, sEndXPos, sEndYPos;
UINT8 ubStartID;
ubStartID = WhoIsThere2( sStartGridNo, bStartLevel );
// sevenfm: always use standing heights
/*ubStartID = WhoIsThere2( sStartGridNo, bStartLevel );
if ( ubStartID != NOBODY )
{
return( SoldierTo3DLocationLineOfSightTest( MercPtrs[ ubStartID ], sEndGridNo, bEndLevel, 0, bAware, iTileSightLimit ) );
}
}*/
// else... assume standing heights
dStartZPos = STANDING_LOS_POS + bStartLevel * HEIGHT_UNITS;
// sevenfm: use height argument
dStartZPos = dStartPos + bStartLevel * HEIGHT_UNITS;
// add in ground height
dStartZPos += CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ sStartGridNo ].sHeight );
@@ -2522,7 +2524,8 @@ INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, I
sStartXPos = sStartXPos * CELL_X_SIZE + (CELL_X_SIZE / 2);
sStartYPos = sStartYPos * CELL_Y_SIZE + (CELL_Y_SIZE / 2);
dEndZPos = STANDING_LOS_POS + bEndLevel * HEIGHT_UNITS;
// sevenfm: use height argument
dEndZPos = dEndPos + bEndLevel * HEIGHT_UNITS;
// add in ground height
dEndZPos += CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ sEndGridNo ].sHeight );
@@ -2530,10 +2533,12 @@ INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, I
sEndXPos = sEndXPos * CELL_X_SIZE + (CELL_X_SIZE / 2);
sEndYPos = sEndYPos * CELL_Y_SIZE + (CELL_Y_SIZE / 2);
if (iTileSightLimit == CALC_FROM_ALL_DIRS || iTileSightLimit == CALC_FROM_WANTED_DIR) {
if (iTileSightLimit == CALC_FROM_ALL_DIRS || iTileSightLimit == CALC_FROM_WANTED_DIR)
{
iTileSightLimit = MaxNormalDistanceVisible();
}
else if (iTileSightLimit == NO_DISTANCE_LIMIT) {
else if (iTileSightLimit == NO_DISTANCE_LIMIT)
{
iTileSightLimit = 255 + MaxNormalDistanceVisible();
}
return( LineOfSightTest( (FLOAT)sStartXPos, (FLOAT)sStartYPos, dStartZPos, (FLOAT) sEndXPos, (FLOAT) sEndYPos, dEndZPos, iTileSightLimit, bAware, FALSE, NULL ) );
+16 -13
View File
@@ -77,19 +77,6 @@ INT8 FireBulletGivenTargetTrapOnly( SOLDIERTYPE* pThrower, OBJECTTYPE* pObj, INT
#define CALC_FROM_WANTED_DIR -2
#define NO_DISTANCE_LIMIT -3
INT32 SoldierToSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier, INT8 bAware, int iTileSightLimit = CALC_FROM_ALL_DIRS, UINT8 ubAimLocation = LOS_POS, bool adjustForSight = true, bool cthCalc = false );
INT32 SoldierTo3DLocationLineOfSightTest( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bCubeLevel, INT8 bAware, int ubSightLimit = CALC_FROM_ALL_DIRS, bool adjustForSight = true );
INT32 SoldierToVirtualSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bStance, INT8 bAware, int iTileSightLimit = CALC_FROM_ALL_DIRS );
UINT8 SoldierToSoldierChanceToGetThrough( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier );
UINT8 SoldierToSoldierBodyPartChanceToGetThrough( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier, UINT8 ubAimLocation );
UINT8 AISoldierToSoldierChanceToGetThrough( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier );
UINT8 AISoldierToLocationChanceToGetThrough( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bCubeLevel );
UINT8 SoldierToLocationChanceToGetThrough( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bCubeLevel, UINT8 ubTargetID );
INT32 SoldierToLocationWindowTest( SOLDIERTYPE * pStartSoldier, INT32 sEndGridNo );
INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, INT32 sEndGridNo, INT8 bEndLevel, INT8 bAware, int iTileSightLimit = CALC_FROM_ALL_DIRS );
BOOLEAN CalculateSoldierZPos( SOLDIERTYPE * pSoldier, UINT8 ubPosType, FLOAT * pdZPos );
#define HEIGHT_UNITS 256
#define HEIGHT_UNITS_PER_INDEX (HEIGHT_UNITS / PROFILE_Z_SIZE)
#define MAX_STRUCTURE_HEIGHT 50
@@ -154,6 +141,22 @@ BOOLEAN CalculateSoldierZPos( SOLDIERTYPE * pSoldier, UINT8 ubPosType, FLOAT * p
#define CLOSE_TO_FIRER 25
#define VERY_CLOSE_TO_FIRER 21
INT32 SoldierToSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier, INT8 bAware, int iTileSightLimit = CALC_FROM_ALL_DIRS, UINT8 ubAimLocation = LOS_POS, bool adjustForSight = true, bool cthCalc = false );
INT32 SoldierTo3DLocationLineOfSightTest( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bCubeLevel, INT8 bAware, int ubSightLimit = CALC_FROM_ALL_DIRS, bool adjustForSight = true );
INT32 SoldierToVirtualSoldierLineOfSightTest( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bStance, INT8 bAware, int iTileSightLimit = CALC_FROM_ALL_DIRS );
UINT8 SoldierToSoldierChanceToGetThrough( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier );
UINT8 SoldierToSoldierBodyPartChanceToGetThrough( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier, UINT8 ubAimLocation );
UINT8 AISoldierToSoldierChanceToGetThrough( SOLDIERTYPE * pStartSoldier, SOLDIERTYPE * pEndSoldier );
UINT8 AISoldierToLocationChanceToGetThrough( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bCubeLevel );
UINT8 SoldierToLocationChanceToGetThrough( SOLDIERTYPE * pStartSoldier, INT32 sGridNo, INT8 bLevel, INT8 bCubeLevel, UINT8 ubTargetID );
INT32 SoldierToLocationWindowTest( SOLDIERTYPE * pStartSoldier, INT32 sEndGridNo );
// sevenfm: added start/end LOS heights
INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, INT32 sEndGridNo, INT8 bEndLevel, INT8 bAware, int iTileSightLimit, FLOAT dStartPos = STANDING_LOS_POS, FLOAT dEndPos = STANDING_LOS_POS);
//INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, INT32 sEndGridNo, INT8 bEndLevel, INT8 bAware, int iTileSightLimit = CALC_FROM_ALL_DIRS );
BOOLEAN CalculateSoldierZPos( SOLDIERTYPE * pSoldier, UINT8 ubPosType, FLOAT * pdZPos );
#ifdef LOS_DEBUG
typedef struct LOSResults
{
+1 -3
View File
@@ -566,9 +566,7 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
{
// successfully moved to roof!
// fake setting action to climb roof and see if we can afford this
pSoldier->aiData.bAction = AI_ACTION_CLIMB_ROOF;
if (IsActionAffordable(pSoldier))
if (IsActionAffordable(pSoldier, AI_ACTION_CLIMB_ROOF))
{
// set action to none and next action to climb roof so we do that next
pSoldier->aiData.bAction = AI_ACTION_NONE;
+277 -198
View File
@@ -22,9 +22,11 @@
#include "environment.h"
#include "lighting.h"
#include "Soldier Create.h"
#include "SkillCheck.h" // added by SANDRO
#include "Vehicles.h" // added by silversurfer
#include "Game Clock.h" // added by sevenfm
#include "SkillCheck.h" // added by SANDRO
#include "Vehicles.h" // added by silversurfer
#include "Game Clock.h" // sevenfm
#include "Rotting Corpses.h" // sevenfm
#include "wcheck.h" // sevenfm
#endif
#include "GameInitOptionsScreen.h"
@@ -460,13 +462,18 @@ void NewDest(SOLDIERTYPE *pSoldier, INT32 usGridNo)
}
BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier)
BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier, INT8 bAction)
{
INT16 bMinPointsNeeded = 0;
//NumMessage("AffordableAction - Guy#",pSoldier->ubID);
switch (pSoldier->aiData.bAction)
if( bAction == AI_ACTION_NONE )
{
bAction = pSoldier->aiData.bAction;
}
switch (bAction)
{
case AI_ACTION_NONE: // maintain current position & facing
// no cost for doing nothing!
@@ -923,13 +930,13 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
{
INT32 *psLastLoc, *pusNoiseGridNo;
INT8 *pbLastLevel;
INT32 sGridNo=-1;
INT32 sGridNo=-1;
INT8 bLevel, bClosestLevel = -1;
BOOLEAN fClimbingNecessary, fClosestClimbingNecessary = FALSE;
BOOLEAN fClimbingNecessary, fClosestClimbingNecessary = FALSE;
INT32 iPathCost;
INT32 sClosestDisturbance = NOWHERE;
UINT32 uiLoop;
UINT32 closestConscious = NOWHERE,closestUnconscious = NOWHERE;
UINT32 uiLoop;
UINT32 closestConscious = NOWHERE,closestUnconscious = NOWHERE;
INT32 iShortestPath = 1000;
INT32 iShortestPathConscious = 1000,iShortestPathUnconscious = 1000;
UINT8 *pubNoiseVolume;
@@ -938,11 +945,11 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
INT32 sClimbGridNo;
SOLDIERTYPE * pOpp;
// CJC: can't trace a path to every known disturbance!
// for starters, try the closest one as the crow flies
INT32 sClosestEnemy = NOWHERE, sDistToClosestEnemy = 1000, sDistToEnemy;
*pfChangeLevel = FALSE;
// sevenfm: safety check
if ( pfChangeLevel )
{
*pfChangeLevel = FALSE;
}
pubNoiseVolume = &gubPublicNoiseVolume[pSoldier->bTeam];
pusNoiseGridNo = &gsPublicNoiseGridNo[pSoldier->bTeam];
@@ -972,7 +979,9 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
// silversurfer: ignore empty vehicles
if ( pOpp->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpp->bVehicleID ) == 0 )
{
continue;
}
pbPersOL = pSoldier->aiData.bOppList + pOpp->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pOpp->ubID;
@@ -1021,21 +1030,11 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
continue;
}
sDistToEnemy = PythSpacesAway( pSoldier->sGridNo, sGridNo );
if (sDistToEnemy < sDistToClosestEnemy )
{
sClosestEnemy = sGridNo;
bClosestLevel = bLevel;
sDistToClosestEnemy = sDistToEnemy;
}
iPathCost = EstimatePathCostToLocation( pSoldier, sGridNo, bLevel, FALSE, &fClimbingNecessary, &sClimbGridNo );
}
if (!TileIsOutOfBounds(sClosestEnemy))
{
iPathCost = EstimatePathCostToLocation( pSoldier, sClosestEnemy, bClosestLevel, FALSE, &fClimbingNecessary, &sClimbGridNo );
// if we can get there
if (iPathCost != 0)
// if we can get there and it's first reachable enemy or closer than other known enemies
if( iPathCost != 0 &&
(TileIsOutOfBounds(sClosestDisturbance) || iPathCost < iShortestPath) )
{
if (fClimbingNecessary)
{
@@ -1043,16 +1042,14 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
}
else
{
sClosestDisturbance = sClosestEnemy;
sClosestDisturbance = sGridNo;
}
iShortestPath = iPathCost;
fClosestClimbingNecessary = fClimbingNecessary;
}
}
// sevenfm: only if not found enemy
if ( sClosestDisturbance == NOWHERE )
{
// if any "misc. noise" was also heard recently
if (!TileIsOutOfBounds(pSoldier->aiData.sNoiseGridno) && pSoldier->aiData.sNoiseGridno != sClosestDisturbance)
{
@@ -1080,11 +1077,10 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
// get the AP cost to get to the location of the noise
iPathCost = EstimatePathCostToLocation( pSoldier, sGridNo, bLevel, FALSE, &fClimbingNecessary, &sClimbGridNo );
// if we can get there
// sevenfm: check that noise is closer than known disturbance
//if (iPathCost != 0)
// sevenfm: only if we don't know enemy location or noise source is close and we have not seen enemy recently
if (iPathCost != 0 &&
PythSpacesAway( pSoldier->sGridNo, sGridNo ) < sDistToClosestEnemy &&
iPathCost < iShortestPath)
!AICheckIsFlanking(pSoldier) &&
(TileIsOutOfBounds(sClosestDisturbance) || iPathCost < iShortestPath && !GuySawEnemyThisTurnOrBefore(pSoldier)))
{
if (fClimbingNecessary)
{
@@ -1116,11 +1112,10 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
// get the AP cost to get to the location of the noise
iPathCost = EstimatePathCostToLocation( pSoldier, sGridNo, bLevel, FALSE, &fClimbingNecessary, &sClimbGridNo );
// if we can get there
// sevenfm: check that noise is closer than known disturbance
//if (iPathCost != 0)
// sevenfm: only if we don't know enemy location or noise source is close and we have not seen enemy recently
if (iPathCost != 0 &&
PythSpacesAway( pSoldier->sGridNo, sGridNo ) < sDistToClosestEnemy &&
iPathCost < iShortestPath)
!AICheckIsFlanking(pSoldier) &&
(TileIsOutOfBounds(sClosestDisturbance) || iPathCost < iShortestPath && !GuySawEnemyThisTurnOrBefore(pSoldier)))
{
if (fClimbingNecessary)
{
@@ -1143,7 +1138,6 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
(*pubNoiseVolume)--;
}
}
} // sevenfm: end check enemy closestdisturbance
#ifdef DEBUGDECISIONS
if (!TileIsOutOfBounds(sClosestDisturbance))
@@ -1152,7 +1146,12 @@ INT32 ClosestReachableDisturbance(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK,
}
#endif
*pfChangeLevel = fClosestClimbingNecessary;
// sevenfm: safety check
if ( pfChangeLevel )
{
*pfChangeLevel = fClosestClimbingNecessary;
}
return(sClosestDisturbance);
}
@@ -3271,6 +3270,8 @@ UINT8 CountFriendsInDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo )
UINT8 ubFriendDir, ubMyDir;
UINT8 ubFriends = 0;
CHECKF(pSoldier);
ubMyDir = atan8(CenterX(sTargetGridNo),CenterY(sTargetGridNo),CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo));
// Run through each friendly.
@@ -3295,43 +3296,8 @@ UINT8 CountFriendsInDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo )
return ubFriends;
}
// sevenfm: check if suppression is possible (count friends in the fire direction)
BOOLEAN CheckSuppressionDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo )
{
SOLDIERTYPE * pFriend;
UINT8 ubShootingDir, ubFriendDir;
// safety check
if( !pSoldier )
return TRUE;
ubShootingDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(sTargetGridNo),CenterY(sTargetGridNo));
// Run through each friendly.
for ( UINT8 iCounter = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID ; iCounter <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID ; iCounter ++ )
{
pFriend = MercPtrs[ iCounter ];
ubFriendDir = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(pFriend->sGridNo),CenterY(pFriend->sGridNo));
if (pFriend != pSoldier &&
pFriend->bActive &&
pFriend->stats.bLife >= OKLIFE &&
pSoldier->pathing.bLevel == pFriend->pathing.bLevel &&
ubShootingDir == ubFriendDir &&
PythSpacesAway( pSoldier->sGridNo, pFriend->sGridNo) < PythSpacesAway(pSoldier->sGridNo, sTargetGridNo) &&
CalcAverageCTGTForPosition( pSoldier, pFriend->ubID, pFriend->sGridNo, pFriend->pathing.bLevel, pSoldier->bActionPoints ) > 0 &&
( gAnimControl[ pFriend->usAnimState ].ubHeight != ANIM_PRONE ||
PythSpacesAway( pSoldier->sGridNo, pFriend->sGridNo ) <= 15 ) )
{
return FALSE;
}
}
return TRUE;
}
// sevenfm: count nearby friend soldiers
UINT8 CountNearbyFriendlies( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
UINT8 CountNearbyFriends( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
{
SOLDIERTYPE * pFriend;
UINT8 ubFriendCount = 0;
@@ -3370,11 +3336,12 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
SOLDIERTYPE *pOpponent,*pFriend;
// if army guy has NO weapons left then panic!
if ( pSoldier->bTeam == ENEMY_TEAM )
if ( pSoldier->bTeam == ENEMY_TEAM || !pSoldier->aiData.bNeutral )
{
if ( FindAIUsableObjClass( pSoldier, IC_WEAPON ) == NO_SLOT )
{
// sevenfm: disable as we can pick up weapon or attack with fists
// sevenfm: instead of leaving sector, try to attack with hands/knife
return( MORALE_FEARLESS );
//return( MORALE_HOPELESS );
}
}
@@ -3485,13 +3452,21 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
sFrndThreatValue = (iPercent * CalcManThreatValue(pFriend,pOpponent->sGridNo,FALSE,pSoldier)) / 100;
//sprintf(tempstr,"Known by friend %s, opplist status %d, percent %d, threat = %d",
// ExtMen[pFriend->ubID].name,pFriend->aiData.bOppList[pOpponent->ubID],ubPercent,sFrndThreatValue);
//PopMessage(tempstr);
// ADD this to our running total threatValue (increases my MORALE)
// We multiply by sOppThreatValue to PRO-RATE this based on opponent's
// threat value to ME personally. Divide later by sum of them all.
iOurTotalThreat += sOppThreatValue * sFrndThreatValue;
}
// this could get slow if I have a lot of friends...
//KeepInterfaceGoing();
}
// if they are no threat whatsoever
if (!iTheirTotalThreat)
sMorale = 500; // our morale is just incredible
@@ -3525,8 +3500,6 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
case AGGRESSIVE: bMoraleCategory++; break;
}
// sevenfm: new calculation
// make idiot administrators more aggressive
if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR )
{
@@ -3534,23 +3507,38 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
}
// if have good health
if( pSoldier->stats.bLife > 3*pSoldier->stats.bLifeMax/4 )
if( pSoldier->stats.bLife > pSoldier->stats.bLifeMax/2 )
{
bMoraleCategory++;
}
// bad health/breath
if( pSoldier->stats.bLife < pSoldier->stats.bLifeMax/2 )
// bad health
if( pSoldier->stats.bLife < pSoldier->stats.bLifeMax/4 )
{
bMoraleCategory--;
}
if( pSoldier->bBreath < pSoldier->bBreathMax/2 )
// good breath
if( pSoldier->bBreath > 50 )
{
bMoraleCategory++;
}
// bad breath
if( pSoldier->bBreath < 25 )
{
bMoraleCategory--;
}
// if not under fire - attack
if( !pSoldier->aiData.bUnderFire )
{
bMoraleCategory++;
}
// count friends that flank around the same spot
if( CountFriendsFlankSameSpot( pSoldier ) > 0 )
{
bMoraleCategory --;
}
INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
// if last attack of this soldier hit enemy - increase morale
if( pSoldier->aiData.bLastAttackHit )
@@ -3558,62 +3546,18 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
bMoraleCategory++;
}
// sevenfm: wait for flanking soldiers, if had contact with enemy recently and not overcrowded here
if( GuySawEnemyThisTurnOrBefore(pSoldier) )
//CountNearbyFriendlies(pSoldier, pSoldier->sGridNo, 5) < 3 )
{
// count friends that flank around the same spot
bMoraleCategory -= CountFriendsFlankSeek( pSoldier );
// check (mobile) friends nearby that had no contact with enemy recently
if( AICheckFriendsNoContact(pSoldier) )
{
bMoraleCategory--;
}
}
// if some friend hit enemy - increase morale
if( CountNearbyFriendliesLastAttackHit(pSoldier, pSoldier->sGridNo, MaxNormalVisionDistance() / 2) > 0 )
if( CountNearbyFriendsLastAttackHit(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/4 ) > 0 )
{
bMoraleCategory++;
}
// limit morale for wounded/low breath soldiers
if (pSoldier->bBreath < pSoldier->bBreathMax/2 ||
pSoldier->stats.bLife < pSoldier->stats.bLifeMax/2)
{
bMoraleCategory = __min( bMoraleCategory, MORALE_CONFIDENT );
}
INT8 bMaxMoraleCategory;
// sevenfm: limit morale depending on orders - snipers, stationary and guards should not charge enemy
switch (pSoldier->aiData.bOrders)
{
case STATIONARY:
case ONGUARD:
case SNIPER:
bMaxMoraleCategory = MORALE_CONFIDENT;
break;
default:
bMaxMoraleCategory = MORALE_FEARLESS;
}
// limit AI morale depending on morale and suppression shock
if( pSoldier->aiData.bShock )
{
bMaxMoraleCategory = __min(bMaxMoraleCategory, (20 + pSoldier->aiData.bMorale - 20*__min(3, pSoldier->aiData.bShock/5)) / 20);
bMoraleCategory = __min(bMoraleCategory, (20 + pSoldier->aiData.bMorale - 20*__min(3, pSoldier->aiData.bShock/5)) / 20);
}
// apply max limit
bMoraleCategory = __min( bMoraleCategory, bMaxMoraleCategory );
// sevenfm: no less than MORALE_WORRIED category if we can fight
if( pSoldier->stats.bLife > pSoldier->stats.bLifeMax/2 &&
pSoldier->bBreath > pSoldier->bBreathMax/4 )
{
bMoraleCategory = __max( bMoraleCategory, MORALE_WORRIED );
}
// if adjustments made it outside the allowed limits
if (bMoraleCategory < MORALE_HOPELESS)
bMoraleCategory = MORALE_HOPELESS;
@@ -3623,15 +3567,13 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
return(bMoraleCategory);
}
UINT8 CountNearbyFriendliesLastAttackHit( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
UINT8 CountNearbyFriendsLastAttackHit( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
{
CHECKF(pSoldier);
SOLDIERTYPE * pFriend;
UINT8 ubFriendCount = 0;
// safety check
if( !pSoldier )
return 0;
// Run through each friendly.
for ( UINT8 iCounter = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID ; iCounter <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID ; iCounter ++ )
{
@@ -3652,19 +3594,17 @@ UINT8 CountNearbyFriendliesLastAttackHit( SOLDIERTYPE *pSoldier, INT32 sGridNo,
return ubFriendCount;
}
UINT8 CountFriendsFlankSeek( SOLDIERTYPE *pSoldier )
UINT8 CountFriendsFlankSameSpot( SOLDIERTYPE *pSoldier )
{
CHECKF(pSoldier);
SOLDIERTYPE * pFriend;
UINT8 ubFriendCount = 0;
UINT8 ubFlankLeft = 0;
UINT8 ubFlankRight = 0;
// safety check
if( !pSoldier ) return 0;
UINT8 ubMaxDist = MaxNormalVisionDistance() / 2;
UINT8 ubNearbyFriendsContact = CountNearbyFriendliesContact(pSoldier, pSoldier->sGridNo, ubMaxDist);
UINT8 ubMaxDist = VISION_RANGE / 2;
INT32 sClosestOpponent = ClosestKnownOpponent( pSoldier, NULL, NULL );
if(TileIsOutOfBounds(sClosestOpponent))
@@ -3700,87 +3640,226 @@ UINT8 CountFriendsFlankSeek( SOLDIERTYPE *pSoldier )
}
}
if(ubFlankLeft > 0 && ubFlankLeft >= ubNearbyFriendsContact/2)
{
ubFriendCount++;
}
if(ubFlankRight > 0 && ubFlankRight >= ubNearbyFriendsContact/2)
{
ubFriendCount++;
}
return ubFriendCount;
return ubFlankLeft + ubFlankRight;
}
// sevenfm: count nearby friend soldiers - only soldiers who had contact with enemy recently
UINT8 CountNearbyFriendliesContact( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
// check that soldier is flanking
BOOLEAN AICheckIsFlanking( SOLDIERTYPE *pSoldier )
{
CHECKF(pSoldier);
if( pSoldier->aiData.bAlertStatus < STATUS_YELLOW ||
pSoldier->numFlanks == 0 ||
pSoldier->numFlanks >= MAX_FLANKS_RED )
{
return FALSE;
}
return TRUE;
}
// sevenfm: determine minimum flanking directions to stop flanking depending on soldier's attitude
UINT8 MinFlankDirections( SOLDIERTYPE *pSoldier )
{
CHECKF(pSoldier);
switch(pSoldier->aiData.bAttitude)
{
case CUNNINGAID:
case CUNNINGSOLO:
return 4;
}
return 2;
}
// count mobile friends that are in BLACK state and not in a dangerous place or have 3/4 APs or hit enemy recently
// this is mostly used to check if we can cross dangerous area (in light at night or fresh corpses)
UINT8 CountFriendsBlack( SOLDIERTYPE *pSoldier, INT32 sClosestOpponent )
{
CHECKF(pSoldier);
SOLDIERTYPE * pFriend;
UINT8 ubFriendCount = 0;
INT32 sFriendClosestOpponent;
// safety check
if( !pSoldier ) return 0;
// by default, use closest known opponent
if( sClosestOpponent == NOWHERE )
{
sClosestOpponent = ClosestKnownOpponent( pSoldier, NULL, NULL );
}
if(TileIsOutOfBounds(sClosestOpponent))
{
return 0;
}
// Run through each friendly.
for ( UINT8 iCounter = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID ; iCounter <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID ; iCounter ++ )
{
pFriend = MercPtrs[ iCounter ];
// Make sure that character is alive, not too shocked, and conscious, and of higher experience level
// than the character being suppressed.
if (pFriend != pSoldier &&
pFriend->bActive &&
pFriend->stats.bLife >= OKLIFE &&
pFriend->aiData.bOrders > ONGUARD &&
pFriend->aiData.bOrders != SNIPER &&
PythSpacesAway( sGridNo, pFriend->sGridNo ) <= ubDistance &&
GuySawEnemyThisTurnOrBefore(pFriend) )
pFriend = MercPtrs[ iCounter ];
// Make sure that character is alive, not too shocked, and conscious
if (pFriend != pSoldier &&
pFriend->bActive &&
pFriend->stats.bLife >= OKLIFE)
{
ubFriendCount++;
//sFriendClosestOpponent = ClosestKnownOpponent( pFriend, NULL, NULL );
sFriendClosestOpponent = ClosestSeenOpponent( pFriend, NULL, NULL );
if(!TileIsOutOfBounds(sFriendClosestOpponent) &&
PythSpacesAway( sClosestOpponent, sFriendClosestOpponent ) < DAY_VISION_RANGE / 4 &&
pFriend->aiData.bAlertStatus == STATUS_BLACK &&
pFriend->stats.bLife > pFriend->stats.bLifeMax / 2 &&
( GetNearestRottingCorpseAIWarning( pFriend->sGridNo ) == 0 && !InLightAtNight(pFriend->sGridNo, pFriend->pathing.bLevel) ||
pFriend->bActionPoints > 3*pFriend->bInitialActionPoints/4 ||
pFriend->aiData.bLastAttackHit )
)
{
ubFriendCount++;
}
}
}
return ubFriendCount;
}
// sevenfm: count nearby friend soldiers - only soldiers who had no contact with enemy recently
UINT8 CountNearbyFriendliesNoContact( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
// check if we have a prone sight cover from known enemies at spot
BOOLEAN ProneSightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot )
{
SOLDIERTYPE * pFriend;
UINT8 ubFriendCount = 0;
CHECKF(pSoldier);
// safety check
if( !pSoldier ) return 0;
UINT32 uiLoop;
SOLDIERTYPE *pOpponent;
INT32 *pusLastLoc;
INT8 *pbPersOL;
INT8 *pbPublOL;
INT8 *pbLastLevel;
// Run through each friendly.
for ( UINT8 iCounter = gTacticalStatus.Team[ pSoldier->bTeam ].bFirstID ; iCounter <= gTacticalStatus.Team[ pSoldier->bTeam ].bLastID ; iCounter ++ )
INT32 sThreatLoc;
//INT32 iThreatCertainty;
INT8 iThreatLevel;
// look through all opponents for those we know of
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pFriend = MercPtrs[ iCounter ];
// Make sure that character is alive, not too shocked, and conscious, and of higher experience level
// than the character being suppressed.
if (pFriend != pSoldier &&
pFriend->bActive &&
pFriend->stats.bLife >= OKLIFE &&
pFriend->aiData.bOrders > ONGUARD &&
pFriend->aiData.bOrders != SNIPER &&
PythSpacesAway( sGridNo, pFriend->sGridNo ) <= ubDistance &&
!GuySawEnemyThisTurnOrBefore(pFriend) )
pOpponent = MercSlots[ uiLoop ];
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pOpponent || pOpponent->stats.bLife < OKLIFE)
{
ubFriendCount++;
continue; // next merc
}
// if this man is neutral / on the same side, he's not an opponent
if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide))
{
continue; // next merc
}
pbPersOL = pSoldier->aiData.bOppList + pOpponent->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pOpponent->ubID;
pusLastLoc = gsLastKnownOppLoc[pSoldier->ubID] + pOpponent->ubID;
pbLastLevel = gbLastKnownOppLevel[pSoldier->ubID] + pOpponent->ubID;
// if this opponent is unknown personally and publicly
if ((*pbPersOL == NOT_HEARD_OR_SEEN) && (*pbPublOL == NOT_HEARD_OR_SEEN))
{
continue; // next merc
}
// if personal knowledge is more up to date or at least equal
if ((gubKnowledgeValue[*pbPublOL - OLDEST_HEARD_VALUE][*pbPersOL - OLDEST_HEARD_VALUE] > 0) ||
(*pbPersOL == *pbPublOL))
{
// using personal knowledge, obtain opponent's "best guess" gridno
sThreatLoc = *pusLastLoc;
iThreatLevel = *pbLastLevel;
//iThreatCertainty = ThreatPercent[*pbPersOL - OLDEST_HEARD_VALUE];
}
else
{
// using public knowledge, obtain opponent's "best guess" gridno
sThreatLoc = gsPublicLastKnownOppLoc[pSoldier->bTeam][pOpponent->ubID];
iThreatLevel = gbPublicLastKnownOppLevel[pSoldier->bTeam][pOpponent->ubID];
//iThreatCertainty = ThreatPercent[*pbPublOL - OLDEST_HEARD_VALUE];
}
if( LocationToLocationLineOfSightTest( sThreatLoc, iThreatLevel, sSpot, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS, PRONE_LOS_POS, PRONE_LOS_POS) )
{
return FALSE;
}
}
return ubFriendCount;
return TRUE;
}
BOOLEAN AICheckFriendsNoContact( SOLDIERTYPE *pSoldier )
// check if we have a sight cover from known enemies at spot
BOOLEAN SightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot )
{
UINT8 ubMaxDist = MaxNormalVisionDistance() / 2;
UINT8 ubNearbyFriendsContact = CountNearbyFriendliesContact(pSoldier, pSoldier->sGridNo, ubMaxDist);
UINT8 ubNearbyFriendsNoContact = CountNearbyFriendliesNoContact(pSoldier, pSoldier->sGridNo, ubMaxDist);
CHECKF(pSoldier);
if( ubNearbyFriendsNoContact > ubNearbyFriendsContact )
UINT32 uiLoop;
SOLDIERTYPE *pOpponent;
INT32 *pusLastLoc;
INT8 *pbPersOL;
INT8 *pbPublOL;
INT8 *pbLastLevel;
INT32 sThreatLoc;
//INT32 iThreatCertainty;
INT8 iThreatLevel;
// look through all opponents for those we know of
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
return TRUE;
pOpponent = MercSlots[ uiLoop ];
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pOpponent || pOpponent->stats.bLife < OKLIFE)
{
continue; // next merc
}
// if this man is neutral / on the same side, he's not an opponent
if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide))
{
continue; // next merc
}
pbPersOL = pSoldier->aiData.bOppList + pOpponent->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pOpponent->ubID;
pusLastLoc = gsLastKnownOppLoc[pSoldier->ubID] + pOpponent->ubID;
pbLastLevel = gbLastKnownOppLevel[pSoldier->ubID] + pOpponent->ubID;
// if this opponent is unknown personally and publicly
if ((*pbPersOL == NOT_HEARD_OR_SEEN) && (*pbPublOL == NOT_HEARD_OR_SEEN))
{
continue; // next merc
}
// if personal knowledge is more up to date or at least equal
if ((gubKnowledgeValue[*pbPublOL - OLDEST_HEARD_VALUE][*pbPersOL - OLDEST_HEARD_VALUE] > 0) ||
(*pbPersOL == *pbPublOL))
{
// using personal knowledge, obtain opponent's "best guess" gridno
sThreatLoc = *pusLastLoc;
iThreatLevel = *pbLastLevel;
//iThreatCertainty = ThreatPercent[*pbPersOL - OLDEST_HEARD_VALUE];
}
else
{
// using public knowledge, obtain opponent's "best guess" gridno
sThreatLoc = gsPublicLastKnownOppLoc[pSoldier->bTeam][pOpponent->ubID];
iThreatLevel = gbPublicLastKnownOppLevel[pSoldier->bTeam][pOpponent->ubID];
//iThreatCertainty = ThreatPercent[*pbPublOL - OLDEST_HEARD_VALUE];
}
if( LocationToLocationLineOfSightTest( sThreatLoc, iThreatLevel, sSpot, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) )
{
return FALSE;
}
}
return FALSE;
}
return TRUE;
}
+25 -11
View File
@@ -219,8 +219,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
}
// silversurfer: ignore empty vehicles
if ( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 )
continue;
// sevenfm: allow shooting at empty vehicles, but they have low priority
//if ( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 )
//continue;
// Special stuff for Carmen the bounty hunter
if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpponent->ubProfile != SLAY)
@@ -643,6 +644,12 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
iAttackValue /= 2;
}
// sevenfm: empty vehicles have very low priority
if ( pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle( pOpponent->bVehicleID ) == 0 )
{
iAttackValue /= 4;
}
#ifdef DEBUGATTACKS
DebugAI( String( "CalcBestShot: best AttackValue vs %d = %d\n",uiLoop,iAttackValue ) );
#endif
@@ -677,8 +684,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
}
}
//dnl ch62 180813 ignore firing into dying targets
if((pOpponent->bCollapsed || pOpponent->bBreathCollapsed) && pOpponent->stats.bLife < OKLIFE/* && !(pSoldier->aiData.bAttitude == AGGRESSIVE && Random(100) < 20)*/)
continue;
// sevenfm: disabled because of problems with AI
//if((pOpponent->bCollapsed || pOpponent->bBreathCollapsed) && pOpponent->stats.bLife < OKLIFE/* && !(pSoldier->aiData.bAttitude == AGGRESSIVE && Random(100) < 20)*/)
//continue;
// OOOF! That was a lot of work! But we've got a new best target!
pBestShot->ubPossible = TRUE;
@@ -3003,17 +3011,23 @@ void CheckIfShotPossible(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN s
RearrangePocket(pSoldier, HANDPOS, pBestShot->bWeaponIn, TEMPORARILY);
}
//if ( (!suppressionFire && ( (IsScoped(pObj) && GunRange(pObj) > pSoldier->MaxDistanceVisible(pBestShot->sTarget, pBestShot->bTargetLevel) ) || pSoldier->bOrders == SNIPER ) ) ||
// HEADROCK HAM B2.4: Changed this again - weapons are no longer checked for larger magazine to allow suppressive fire, due to
// suppressive fire revamp.
// if ( (!suppressionFire && ( (IsScoped(pObj) && GunRange(pObj) > MaxNormalDistanceVisible() ) || pSoldier->aiData.bOrders == SNIPER ) ) ||
// (suppressionFire && IsGunAutofireCapable(&pSoldier->inv[pBestShot->bWeaponIn] ) && GetMagSize(pObj) > 30 && (*pObj)[0]->data.gun.ubGunShotsLeft > 20 ))
BOOLEAN fEnableAISuppression = FALSE;
// CHRISL: Changed from a simple flag to two externalized values for more modder control over AI suppression
if ( ((!suppressionFire && ( (IsScoped(pObj) && GunRange(pObj, pSoldier) > MaxNormalDistanceVisible() ) || pSoldier->aiData.bOrders == SNIPER ) ) || // SANDRO - added argument to GunRange()
(suppressionFire && IsGunAutofireCapable(&pSoldier->inv[pBestShot->bWeaponIn]) && GetMagSize(pObj) >= gGameExternalOptions.ubAISuppressionMinimumMagSize && (*pObj)[0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumAmmo)) )
if( suppressionFire &&
IsGunAutofireCapable(&pSoldier->inv[pBestShot->bWeaponIn]) &&
GetMagSize(pObj) >= gGameExternalOptions.ubAISuppressionMinimumMagSize &&
(*pObj)[0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumAmmo )
{
fEnableAISuppression = TRUE;
}
// sevenfm: allow any soldier with long range weapon to shoot in RED state (if he can hit)
if( !suppressionFire &&
GunRange(pObj, pSoldier) > DAY_VISION_RANGE )
{
fEnableAISuppression = TRUE;
}
if (fEnableAISuppression)
{
+223 -170
View File
@@ -1907,16 +1907,16 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
}
//continue flanking
INT32 tempGridNo;
INT32 sFlankGridNo;
if (TileIsOutOfBounds(sNoiseGridNo))
tempGridNo = pSoldier->lastFlankSpot;
sFlankGridNo = pSoldier->lastFlankSpot;
else
tempGridNo = sNoiseGridNo;
sFlankGridNo = sNoiseGridNo;
if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_YELLOW )
{
INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier );
INT16 currDir = GetDirectionFromGridNo ( sFlankGridNo, pSoldier );
INT16 origDir = pSoldier->origDir;
pSoldier->numFlanks += 1;
if ( pSoldier->flags.lastFlankLeft )
@@ -1924,15 +1924,14 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
if ( origDir > currDir )
origDir -= NUM_WORLD_DIRECTIONS;
// sevenfm: stop flanking if no friends between me and noise gridno
if ( (currDir - origDir) >= 4 ||
CountFriendsInDirection( pSoldier, tempGridNo ) == 0 )
// stop flanking if reached desired direction
if ( (currDir - origDir) >= MinFlankDirections(pSoldier) )
{
pSoldier->numFlanks = MAX_FLANKS_YELLOW;
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_LEFT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 )
return AI_ACTION_FLANK_LEFT ;
else
@@ -1944,15 +1943,14 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
if ( origDir < currDir )
origDir += NUM_WORLD_DIRECTIONS;
// sevenfm: stop flanking if no friends between me and noise gridno
if ( (origDir - currDir) >= 4 ||
CountFriendsInDirection( pSoldier, tempGridNo ) == 0 )
// stop flanking if reached desired direction
if ( (origDir - currDir) >= MinFlankDirections(pSoldier) )
{
pSoldier->numFlanks = MAX_FLANKS_YELLOW;
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_RIGHT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))//&& (origDir - currDir) < 2 )
return AI_ACTION_FLANK_RIGHT ;
else
@@ -1964,7 +1962,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
if ( pSoldier->numFlanks == MAX_FLANKS_YELLOW )
{
pSoldier->numFlanks += 1;
pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,tempGridNo,AI_ACTION_SEEK_NOISE);
pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,sFlankGridNo,AI_ACTION_SEEK_NOISE);
return AI_ACTION_SEEK_NOISE ;
}
@@ -2091,11 +2089,9 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
}
// possibly start YELLOW flanking
// sevenfm: only CUNNINGAID and CUNNINGSOLO should flank
// check that there are some friends between me and noise gridno
// STATIONARY/ONGUARD/CLOSEPATROL/SNIPER should not flank
if ( ( pSoldier->aiData.bAttitude == CUNNINGAID || pSoldier->aiData.bAttitude == CUNNINGSOLO ) &&
CountFriendsInDirection( pSoldier, sNoiseGridNo ) > 0 &&
if ( ( pSoldier->aiData.bAttitude == CUNNINGAID || pSoldier->aiData.bAttitude == CUNNINGSOLO ) &&
pSoldier->bTeam == ENEMY_TEAM &&
( CountFriendsInDirection( pSoldier, sNoiseGridNo ) > 0 || NightTime() ) &&
( pSoldier->aiData.bOrders == SEEKENEMY ||
pSoldier->aiData.bOrders == FARPATROL ||
pSoldier->aiData.bOrders == CLOSEPATROL && NightTime() ) )
@@ -2144,6 +2140,13 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
pSoldier->origDir = GetDirectionFromGridNo ( sNoiseGridNo, pSoldier);
pSoldier->lastFlankSpot = sNoiseGridNo;
pSoldier->numFlanks++;
// sevenfm: change orders CLOSEPATROL -> FARPATROL
if( pSoldier->aiData.bOrders == CLOSEPATROL )
{
pSoldier->aiData.bOrders = FARPATROL;
}
return(action);
}
}
@@ -2415,6 +2418,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
INT8 bSeekPts = 0, bHelpPts = 0, bHidePts = 0, bWatchPts = 0;
INT8 bHighestWatchLoc;
ATTACKTYPE BestThrow, BestShot;
// sevenfm:
BOOLEAN fProneSightCover = FALSE;
BOOLEAN fDangerousSpot = FALSE;
#ifdef AI_TIMING_TEST
UINT32 uiStartTime, uiEndTime;
#endif
@@ -2443,6 +2451,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
return(AI_ACTION_NONE);
}
fProneSightCover = ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo);
if( !fProneSightCover || pSoldier->aiData.bUnderFire )
{
fDangerousSpot = TRUE;
}
// can this guy move to any of the neighbouring squares ? (sets TRUE/FALSE)
ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier));
@@ -2889,22 +2903,19 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
// CHRISL: Changed from a simple flag to two externalized values for more modder control over AI suppression
// WarmSteel - Don't *always* try to suppress when under 50 CTH
if ( BestShot.bWeaponIn != -1
&& BestShot.ubPossible
&& GetMagSize(&pSoldier->inv[BestShot.bWeaponIn]) >= gGameExternalOptions.ubAISuppressionMinimumMagSize
&& pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumAmmo
&& BestShot.ubChanceToReallyHit < (INT16)(PreRandom(100) - 50)
&& Menptr[BestShot.ubOpponent].pathing.bLevel == 0
&& pSoldier->aiData.bOrders != SNIPER &&
// sevenfm: check that we'll not shoot at our friends
CheckSuppressionDirection( pSoldier, Menptr[BestShot.ubOpponent].sGridNo ) &&
// sevenfm: don't suppress if target already cowering
!CoweringShockLevel(MercPtrs[BestShot.ubOpponent]) &&
// sevenfm: don't suppress when flanking
(pSoldier->numFlanks == 0 || pSoldier->numFlanks > MAX_FLANKS_RED) &&
// sevenfm: don't suppress if we don't have extra clip to reload
fExtraClip
)
if( BestShot.bWeaponIn != -1
&& BestShot.ubPossible
&& GetMagSize(&pSoldier->inv[BestShot.bWeaponIn]) >= gGameExternalOptions.ubAISuppressionMinimumMagSize
&& pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft >= gGameExternalOptions.ubAISuppressionMinimumAmmo
//&& BestShot.ubChanceToReallyHit < (INT16)(PreRandom(50))
//&& Menptr[BestShot.ubOpponent].pathing.bLevel == 0
&& pSoldier->aiData.bOrders != SNIPER &&
BestShot.ubFriendlyFireChance < 5 &&
!CoweringShockLevel(MercPtrs[BestShot.ubOpponent]) &&
!AICheckIsFlanking(pSoldier) &&
LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, MercPtrs[BestShot.ubOpponent]->sGridNo, MercPtrs[BestShot.ubOpponent]->pathing.bLevel, TRUE, NO_DISTANCE_LIMIT) &&
//Weapon[pSoldier->inv[BestShot.bWeaponIn].usItem].ubWeaponType == GUN_LMG ) && //Weapon[usInHand].ubWeaponClass == MGCLASS
(fExtraClip || pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft > gGameExternalOptions.ubAISuppressionMinimumMagSize) )
{
// then do it!
@@ -2912,15 +2923,21 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: suppression fire possible!");
if (BestShot.bWeaponIn != HANDPOS)
{
RearrangePocket(pSoldier,HANDPOS,BestShot.bWeaponIn,FOREVER);
}
pSoldier->aiData.usActionData = BestShot.sTarget;
pSoldier->bTargetLevel = BestShot.bTargetLevel;
pSoldier->aiData.bAimTime = 0;
pSoldier->bDoAutofire = 0;
pSoldier->bDoBurst = 1;
pSoldier->aiData.usActionData = BestShot.sTarget;
//pSoldier->aiData.bAimTime = BestShot.ubAimTime;
INT16 ubBurstAPs = 0;
FLOAT dTotalRecoil = 0;
pSoldier->bDoAutofire = 0;
if(UsingNewCTHSystem() == true){
if(UsingNewCTHSystem() == true)
{
do
{
pSoldier->bDoAutofire++;
@@ -2929,7 +2946,9 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs && pSoldier->inv[ pSoldier->ubAttackingHand ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire
&& dTotalRecoil <= 10.0f );
} else {
}
else
{
do
{
pSoldier->bDoAutofire++;
@@ -2945,30 +2964,21 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
// Make sure we decided to fire at least one shot!
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
// minimum 5 bullets //WarmSteel: 5 bullets sounds reasonable, no?
// Hmmm, automatic suppression? Howcome we don't get this?
if (pSoldier->bDoAutofire >= 5 && pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs )
// minimum 5 bullets
if (pSoldier->bDoAutofire >= 5 && pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs )
{
pSoldier->aiData.bAimTime = 0;
pSoldier->bDoBurst = 1;
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[ MSG113_SUPPRESSIONFIRE ] );
// HEADROCK HAM 4: This is the stupidest thing ever.
// Menptr[BestShot.ubOpponent].ubSuppressionPoints += pSoldier->bDoAutofire;
Menptr[BestShot.ubOpponent].ubSuppressorID = pSoldier->ubID;
if (!WeaponReady( pSoldier ) && gGameExternalOptions.ubAllowAlternativeWeaponHolding){ pSoldier->bScopeMode = USE_ALT_WEAPON_HOLD; }
return( AI_ACTION_FIRE_GUN );
}
else
{
pSoldier->aiData.bAimTime = 0;
pSoldier->bDoBurst = 0;
pSoldier->bDoAutofire = 0;
// not enough aps - do somthing else
}
}
// suppression not possible, do something else
}
/*
// CALL IN AIR STRIKE & RADIO RED ALERT
if ( !fCivilian && pSoldier->bTeam != MILITIA_TEAM && gGameOptions.fAirStrikes && airstrikeavailable && (pSoldier->bActionPoints >= APBPConstants[AP_RADIO]) && !WillAirRaidBeStopped(pSoldier->sSectorX,pSoldier->sSectorY))
@@ -3187,24 +3197,6 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
}
////////////////////////////////////////////////////////////////////////////
// WHEN IN THE LIGHT, GET OUT OF THERE!
////////////////////////////////////////////////////////////////////////////
if ( ubCanMove && InLightAtNight( pSoldier->sGridNo, pSoldier->pathing.bLevel ) && pSoldier->aiData.bOrders != STATIONARY )
{
//ddd for the enemy to run away from lighht
if(gGameExternalOptions.bNewTacticalAIBehavior)
pSoldier->aiData.bAction = AI_ACTION_LEAVE_WATER_GAS;
//ddd
pSoldier->aiData.usActionData = FindNearbyDarkerSpot( pSoldier );
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
{
// move as if leaving water or gas
return( AI_ACTION_LEAVE_WATER_GAS );
}
}
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch and rest if running out of breath");
////////////////////////////////////////////////////////////////////////
// CROUCH & REST IF RUNNING OUT OF BREATH
@@ -3362,6 +3354,22 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: main red ai");
// sevenfm: avoid light if spot is dangerous and no friends see my closest enemy
if (ubCanMove &&
InLightAtNight( pSoldier->sGridNo, pSoldier->pathing.bLevel ) &&
pSoldier->aiData.bOrders != STATIONARY &&
pSoldier->aiData.bOrders != SNIPER &&
CountFriendsBlack(pSoldier) == 0 )
{
pSoldier->aiData.usActionData = FindNearbyDarkerSpot( pSoldier );
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
{
// move as if leaving water or gas
return( AI_ACTION_LEAVE_WATER_GAS );
}
}
////////////////////////////////////////////////////////////////////////////
// MAIN RED AI: Decide soldier's preference between SEEKING,HELPING & HIDING
////////////////////////////////////////////////////////////////////////////
@@ -3371,12 +3379,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: check to continue flanking");
// continue flanking
INT32 tempGridNo;
INT32 sFlankGridNo;
if (TileIsOutOfBounds(sClosestDisturbance))
tempGridNo = pSoldier->lastFlankSpot;
sFlankGridNo = pSoldier->lastFlankSpot;
else
tempGridNo = sClosestDisturbance;
sFlankGridNo = sClosestDisturbance;
// continue flanking
// sevenfm: dont' flank when under fire
@@ -3386,7 +3394,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
!pSoldier->aiData.bUnderFire )
{
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: continue flanking");
INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier );
INT16 currDir = GetDirectionFromGridNo ( sFlankGridNo, pSoldier );
INT16 origDir = pSoldier->origDir;
pSoldier->numFlanks += 1;
if ( pSoldier->flags.lastFlankLeft )
@@ -3394,18 +3402,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
if ( origDir > currDir )
origDir -= NUM_WORLD_DIRECTIONS;
// sevenfm: stop flanking if no friend between me and noise gridno
if ( (currDir - origDir) >= 4 ||
CountFriendsInDirection( pSoldier, tempGridNo ) == 0 ||
(pSoldier->aiData.bOrders != CUNNINGSOLO &&
(currDir - origDir) >= 2 &&
CountFriendsInDirection( pSoldier, tempGridNo ) < 2) )
// stop flanking condition
if ( (currDir - origDir) >= MinFlankDirections(pSoldier) )
{
pSoldier->numFlanks = MAX_FLANKS_RED;
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_LEFT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 )
return AI_ACTION_FLANK_LEFT ;
@@ -3418,18 +3422,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
if ( origDir < currDir )
origDir += NUM_WORLD_DIRECTIONS;
// sevenfm: stop flanking if no friend between me and noise gridno
if ( (origDir - currDir) >= 4 ||
CountFriendsInDirection( pSoldier, tempGridNo ) == 0 ||
(pSoldier->aiData.bOrders != CUNNINGSOLO &&
(origDir - currDir) >= 2 &&
CountFriendsInDirection( pSoldier, tempGridNo ) < 2) )
// stop flanking condition
if ( (origDir - currDir) >= MinFlankDirections(pSoldier) )
{
pSoldier->numFlanks = MAX_FLANKS_RED;
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_RIGHT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) )//&& (origDir - currDir) < 2 )
return AI_ACTION_FLANK_RIGHT ;
@@ -3438,6 +3438,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
}
}
// sevenfm: when we finished flanking, try to reach lastFlankSpot position
// seek until we are close (DistanceVisible/2) and have line of sight to lastFlankSpot position
// don't seek if we have seen enemy recently or under fire or have shock
@@ -3446,21 +3447,21 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: stop flanking");
if( !TileIsOutOfBounds(tempGridNo) &&
// start end flank approach with full APs
if( gfTurnBasedAI && pSoldier->bActionPoints < pSoldier->bInitialActionPoints )
{
return(AI_ACTION_END_TURN);
}
if( !TileIsOutOfBounds(sFlankGridNo) &&
!GuySawEnemyThisTurnOrBefore(pSoldier) &&
!pSoldier->aiData.bUnderFire &&
!Water(pSoldier->sGridNo, pSoldier->pathing.bLevel) &&
pSoldier->bInitialActionPoints >= APBPConstants[AP_MINIMUM] &&
( PythSpacesAway( pSoldier->sGridNo, tempGridNo ) > MIN_FLANK_DIST_RED ||
!LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) ) )
{
// wait for next turn if not enough AP
if( gfTurnBasedAI && pSoldier->bActionPoints < pSoldier->bInitialActionPoints/2 )
{
return(AI_ACTION_END_TURN);
}
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,tempGridNo,GetAPsCrouch( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0);
( PythSpacesAway( pSoldier->sGridNo, sFlankGridNo ) > MIN_FLANK_DIST_RED ||
!LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) ) )
{
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sFlankGridNo,GetAPsCrouch( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0);
// sevenfm: avoid going into water, gas or light
if( !TileIsOutOfBounds(pSoldier->aiData.usActionData) &&
@@ -3469,12 +3470,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
!InLightAtNight( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel ) )
{
// if soldier can be seen at new position and he cannot be seen at his current position
if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) &&
!LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) )
if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) &&
!LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) )
{
// reserve APs for a possible crouch plus a shot
INT32 sCautiousGridNo = InternalGoAsFarAsPossibleTowards(pSoldier, tempGridNo, (INT8) (MinAPsToAttack( pSoldier, tempGridNo, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE) + GetAPsToLook(pSoldier)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
INT32 sCautiousGridNo = InternalGoAsFarAsPossibleTowards(pSoldier, sFlankGridNo, (INT8) (MinAPsToAttack( pSoldier, sFlankGridNo, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE) + GetAPsToLook(pSoldier)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
if (!TileIsOutOfBounds(sCautiousGridNo))
{
pSoldier->aiData.usActionData = sCautiousGridNo;
@@ -3505,14 +3506,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
pSoldier->numFlanks += 1;
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: stop flanking");
if (PythSpacesAway ( pSoldier->sGridNo, tempGridNo ) > MIN_FLANK_DIST_RED * 2 )
if (PythSpacesAway ( pSoldier->sGridNo, sFlankGridNo ) > MIN_FLANK_DIST_RED * 2 )
{
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,tempGridNo,GetAPsProne( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0);
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sFlankGridNo,GetAPsProne( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0);
if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) )
if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE) )
{
// reserve APs for a possible crouch plus a shot
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, tempGridNo, (INT8) (MinAPsToAttack( pSoldier, tempGridNo, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sFlankGridNo, (INT8) (MinAPsToAttack( pSoldier, sFlankGridNo, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
{
@@ -3600,6 +3601,27 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +0;
}
// 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 )
{
bWatchPts -= 10;
}
// 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( GuySawEnemyThisTurnOrBefore(pSoldier) || pSoldier->aiData.bUnderFire )
{
bHelpPts -= 10;
}
}
if (!gfTurnBasedAI)
@@ -3704,26 +3726,26 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
// return( AI_ACTION_CLIMB_ROOF );
//}
// allow flanking for extra soldiers at night or when overcrowded or no flanking friends yet
BOOLEAN fAllowExtraFlanking = FALSE;
if( NightTime() ||
CountNearbyFriendlies(pSoldier, pSoldier->sGridNo, 5) > 2 ||
CountFriendsFlankSeek(pSoldier) < CountFriendsInDirection( pSoldier, sClosestDisturbance ) )
BOOLEAN fOvercrowded = FALSE;
if( CountNearbyFriends(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/4) > 2 )
{
fAllowExtraFlanking = TRUE;
fOvercrowded = TRUE;
}
// possibly start RED flanking
// sevenfm: possibly start RED flanking
if ( ( pSoldier->aiData.bAttitude == CUNNINGAID || pSoldier->aiData.bAttitude == CUNNINGSOLO ||
( pSoldier->aiData.bAttitude == BRAVESOLO || pSoldier->aiData.bAttitude == BRAVEAID ) && fAllowExtraFlanking && gGameExternalOptions.fAIExtraFlanking ) &&
( pSoldier->aiData.bAttitude == BRAVESOLO || pSoldier->aiData.bAttitude == BRAVEAID ) && fOvercrowded ) &&
pSoldier->bTeam == ENEMY_TEAM &&
gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE &&
CountFriendsInDirection( pSoldier, sClosestDisturbance ) > 1 &&
!pSoldier->aiData.bUnderFire &&
!GuySawEnemyThisTurnOrBefore( pSoldier ) &&
pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] &&
pSoldier->pathing.bLevel == 0 &&
( pSoldier->aiData.bOrders == SEEKENEMY ||
pSoldier->aiData.bOrders == FARPATROL ||
pSoldier->aiData.bOrders == CLOSEPATROL && fAllowExtraFlanking ) )
pSoldier->aiData.bOrders == CLOSEPATROL && NightTime() ) &&
(!GuySawEnemyThisTurnOrBefore( pSoldier ) || fOvercrowded ) &&
!Water(pSoldier->sGridNo, pSoldier->pathing.bLevel) &&
pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] &&
( CountFriendsInDirection( pSoldier, sClosestDisturbance ) > 1 || NightTime() || fOvercrowded) )
{
INT8 action = AI_ACTION_SEEK_OPPONENT;
INT16 dist = PythSpacesAway ( pSoldier->sGridNo, sClosestDisturbance );
@@ -3758,7 +3780,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0);
//pSoldier->numFlanks = 0;
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) )
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS ) )
{
// reserve APs for a possible crouch plus a shot
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
@@ -3790,13 +3812,20 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
pSoldier->origDir = GetDirectionFromGridNo ( sClosestDisturbance, pSoldier);
pSoldier->lastFlankSpot = sClosestDisturbance;
pSoldier->numFlanks++;
// sevenfm: change orders when starting to flank
if( pSoldier->aiData.bOrders == CLOSEPATROL )
{
pSoldier->aiData.bOrders = FARPATROL;
}
return(action);
}
}
else
{
// let's be a bit cautious about going right up to a location without enough APs to shoot
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) )
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS ) )
{
// reserve APs for a possible crouch plus a shot
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
@@ -3832,6 +3861,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
// take a look at our highest watch point... if it's still visible, turn to face it and then wait
bHighestWatchLoc = GetHighestVisibleWatchedLoc( pSoldier->ubID );
//sDistVisible = DistanceVisible( pSoldier, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, gsWatchedLoc[ pSoldier->ubID ][ bHighestWatchLoc ] );
if ( bHighestWatchLoc != -1 )
{
// see if we need turn to face that location
@@ -3839,41 +3869,54 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
// if soldier is not already facing in that direction,
// and the opponent is close enough that he could possibly be seen
if ( pSoldier->ubDirection != ubOpponentDir && pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) )
if( pSoldier->ubDirection != ubOpponentDir &&
pSoldier->InternalIsValidStance( ubOpponentDir, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) &&
pSoldier->bActionPoints >= GetAPsToLook(pSoldier) )
{
// turn
pSoldier->aiData.usActionData = ubOpponentDir;
pSoldier->aiData.bNextAction = AI_ACTION_END_TURN;
return(AI_ACTION_CHANGE_FACING);
}
else
// consider at least crouching
if( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND &&
IsValidStance( pSoldier, ANIM_CROUCH ) &&
pSoldier->bActionPoints >= GetAPsCrouch(pSoldier, TRUE) )
{
// consider at least crouching
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && pSoldier->InternalIsValidStance( ubOpponentDir, ANIM_CROUCH ) )
{
pSoldier->aiData.usActionData = ANIM_CROUCH;
pSoldier->aiData.bNextAction = AI_ACTION_END_TURN;
return(AI_ACTION_CHANGE_STANCE);
}
else if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE )
{
// maybe go prone
if ( PreRandom( 2 ) == 0 && pSoldier->InternalIsValidStance( ubOpponentDir, ANIM_PRONE ) )
{
pSoldier->aiData.usActionData = ANIM_PRONE;
pSoldier->aiData.bNextAction = AI_ACTION_END_TURN;
return( AI_ACTION_CHANGE_STANCE );
}
// end turn
return( AI_ACTION_END_TURN );
}
pSoldier->aiData.usActionData = ANIM_CROUCH;
return(AI_ACTION_CHANGE_STANCE);
}
// possibly go prone, check that we'll have line of sight to standing enemy at watched location
if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH &&
IsValidStance( pSoldier, ANIM_PRONE ) &&
pSoldier->bActionPoints >= GetAPsProne(pSoldier, TRUE) &&
!InARoom(pSoldier->sGridNo, NULL) &&
LocationToLocationLineOfSightTest(pSoldier->sGridNo, pSoldier->pathing.bLevel, gsWatchedLoc[pSoldier->ubID][bHighestWatchLoc], gbWatchedLocLevel[pSoldier->ubID][bHighestWatchLoc], TRUE, CALC_FROM_ALL_DIRS, PRONE_LOS_POS, STANDING_LOS_POS))
{
pSoldier->aiData.usActionData = ANIM_PRONE;
return(AI_ACTION_CHANGE_STANCE);
}
// raise weapon if not raised
if( PickSoldierReadyAnimation( pSoldier, FALSE, FALSE ) != INVALID_ANIMATION &&
!WeaponReady(pSoldier) &&
(pSoldier->bBreath > 15 || GetBPCostPer10APsForGunHolding( pSoldier, TRUE ) < 50) &&
pSoldier->bActionPoints >= GetAPsToReadyWeapon( pSoldier, PickSoldierReadyAnimation( pSoldier, FALSE, FALSE ) ) )
{
return AI_ACTION_RAISE_GUN;
}
//return(AI_ACTION_END_TURN);
return(AI_ACTION_NONE);
//return(AI_ACTION_END_TURN);
}
bWatchPts = -99;
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: couldn't watch");
}
@@ -3892,7 +3935,9 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
guiRedHelpCounter++;
#endif
//WarmSteel - Dont try if we're already quite close to our friend
if (!TileIsOutOfBounds(sClosestFriend) && PythSpacesAway(pSoldier->sGridNo, sClosestFriend) > pSoldier->GetMaxDistanceVisible(sClosestFriend, 0, CALC_FROM_ALL_DIRS ))
// sevenfm: reverted to vanilla helping
//if (!TileIsOutOfBounds(sClosestFriend) && PythSpacesAway(pSoldier->sGridNo, sClosestFriend) > pSoldier->GetMaxDistanceVisible(sClosestFriend, 0, CALC_FROM_ALL_DIRS ))
if (!TileIsOutOfBounds(sClosestFriend))
{
//////////////////////////////////////////////////////////////////////
// GO DIRECTLY TOWARDS CLOSEST FRIEND UNDER FIRE OR WHO LAST RADIOED
@@ -4053,12 +4098,10 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
}
////////////////////////////////////////////////////////////////////////////
// UNDER FIRE, DON'T WANNA/CAN'T RUN AWAY, SO CROUCH
////////////////////////////////////////////////////////////////////////////
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch or go prone");
// if not in water and not already crouched
if (!fCivilian )
@@ -4091,7 +4134,6 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
}
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: look around towards opponent");
////////////////////////////////////////////////////////////////////////////
// LOOK AROUND TOWARD CLOSEST KNOWN OPPONENT, IF KNOWN
@@ -4263,17 +4305,26 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
if ( ubCanMove && !pSoldier->aiData.bNeutral && (gfTurnBasedAI || pSoldier->bTeam == ENEMY_TEAM ) )
{
pSoldier->aiData.bAction = SearchForItems( pSoldier, SEARCH_GENERAL_ITEMS, pSoldier->inv[HANDPOS].usItem );
// sevenfm: check that location is safe
if( pSoldier->aiData.bAction != AI_ACTION_NONE &&
!TileIsOutOfBounds(pSoldier->aiData.usActionData) &&
(GetNearestRottingCorpseAIWarning( pSoldier->aiData.usActionData ) > 0 ||
InLightAtNight( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel ) && !InLightAtNight(pSoldier->aiData.usActionData, pSoldier->pathing.bLevel)) &&
!fDangerousSpot &&
CountFriendsBlack(pSoldier) == 0 )
{
// abort! abort!
pSoldier->aiData.bAction = AI_ACTION_NONE;
}
if (pSoldier->aiData.bAction != AI_ACTION_NONE)
{
return( pSoldier->aiData.bAction );
}
}
////////////////////////////////////////////////////////////////////////////
// SEEK CLOSEST FRIENDLY MEDIC
////////////////////////////////////////////////////////////////////////////
@@ -4494,8 +4545,10 @@ INT16 ubMinAPCost;
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack");
// sevenfm: stop flanking when we see enemy
if( pSoldier->numFlanks < MAX_FLANKS_RED )
if( AICheckIsFlanking(pSoldier) )
{
pSoldier->numFlanks = 0;
}
// if we have absolutely no action points, we can't do a thing under BLACK!
if (!pSoldier->bActionPoints)
@@ -7259,16 +7312,16 @@ INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
}
//continue flanking
INT32 tempGridNo;
INT32 sFlankGridNo;
if (TileIsOutOfBounds(sNoiseGridNo))
tempGridNo = pSoldier->lastFlankSpot;
sFlankGridNo = pSoldier->lastFlankSpot;
else
tempGridNo = sNoiseGridNo;
sFlankGridNo = sNoiseGridNo;
if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_YELLOW )
{
INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier );
INT16 currDir = GetDirectionFromGridNo ( sFlankGridNo, pSoldier );
INT16 origDir = pSoldier->origDir;
pSoldier->numFlanks += 1;
if ( pSoldier->flags.lastFlankLeft )
@@ -7282,7 +7335,7 @@ INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_LEFT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 )
return AI_ACTION_FLANK_LEFT ;
else
@@ -7300,7 +7353,7 @@ INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_RIGHT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))//&& (origDir - currDir) < 2 )
return AI_ACTION_FLANK_RIGHT ;
else
@@ -7312,7 +7365,7 @@ INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
if ( pSoldier->numFlanks == MAX_FLANKS_YELLOW )
{
pSoldier->numFlanks += 1;
pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,tempGridNo,AI_ACTION_SEEK_NOISE);
pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,sFlankGridNo,AI_ACTION_SEEK_NOISE);
return AI_ACTION_SEEK_NOISE ;
}
@@ -7413,17 +7466,17 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: check to continue flanking");
// continue flanking
INT32 tempGridNo;
INT32 sFlankGridNo;
if (TileIsOutOfBounds(sClosestDisturbance))
tempGridNo = pSoldier->lastFlankSpot;
sFlankGridNo = pSoldier->lastFlankSpot;
else
tempGridNo = sClosestDisturbance;
sFlankGridNo = sClosestDisturbance;
if ( pSoldier->numFlanks > 0 && pSoldier->numFlanks < MAX_FLANKS_RED && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE )
{
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: continue flanking");
INT16 currDir = GetDirectionFromGridNo ( tempGridNo, pSoldier );
INT16 currDir = GetDirectionFromGridNo ( sFlankGridNo, pSoldier );
INT16 origDir = pSoldier->origDir;
pSoldier->numFlanks += 1;
if ( pSoldier->flags.lastFlankLeft )
@@ -7437,7 +7490,7 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_LEFT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_LEFT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 )
return AI_ACTION_FLANK_LEFT ;
@@ -7456,7 +7509,7 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
}
else
{
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, tempGridNo , AI_ACTION_FLANK_RIGHT);
pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_RIGHT);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) )//&& (origDir - currDir) < 2 )
return AI_ACTION_FLANK_RIGHT ;
@@ -7469,14 +7522,14 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
pSoldier->numFlanks += 1;
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: stop flanking");
if (PythSpacesAway ( pSoldier->sGridNo, tempGridNo ) > MIN_FLANK_DIST_RED * 2 )
if (PythSpacesAway ( pSoldier->sGridNo, sFlankGridNo ) > MIN_FLANK_DIST_RED * 2 )
{
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,tempGridNo,GetAPsProne( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0);
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sFlankGridNo,GetAPsProne( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0);
if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, tempGridNo, pSoldier->pathing.bLevel, TRUE) )
if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) )
{
// reserve APs for a possible crouch plus a shot
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, tempGridNo, (INT8) (MinAPsToAttack( pSoldier, tempGridNo, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS);
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sFlankGridNo, (INT8) (MinAPsToAttack( pSoldier, sFlankGridNo, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS);
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
{
@@ -7672,7 +7725,7 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
{
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0);
//pSoldier->numFlanks = 0;
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) )
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS ) )
{
// reserve APs for a possible crouch plus a shot
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
@@ -7710,7 +7763,7 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
else
{
// let's be a bit cautious about going right up to a location without enough APs to shoot
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE ) )
if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS ) )
{
// reserve APs for a possible crouch plus a shot
pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sClosestDisturbance, (INT8) (MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST, 0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS );
+10 -6
View File
@@ -841,7 +841,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
}
// sevenfm: check for nearby friends, add bonus/penalty
ubNearbyFriends = __min(5, CountNearbyFriendlies( pSoldier, pSoldier->sGridNo, 5 ));
ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, pSoldier->sGridNo, 5 ));
iCurrentCoverValue -= ubNearbyFriends * abs(iCurrentCoverValue) / (10-ubDiff);
// sevenfm: penalize locations with fresh corpses
@@ -1066,7 +1066,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
}
// sevenfm: check for nearby friends in 10 radius, add bonus/penalty 10%
ubNearbyFriends = __min(5, CountNearbyFriendlies( pSoldier, sGridNo, 5 ));
ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, sGridNo, 5 ));
iCoverValue -= ubNearbyFriends * abs(iCoverValue) / (10-ubDiff);
// sevenfm: penalize locations with fresh corpses
@@ -2494,7 +2494,7 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction )
INT32 sBestSpot = NOWHERE;
INT32 iSearchRange = 8; // sevenfm: increase search range
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT16 sDistanceVisible = MaxNormalVisionDistance();
INT16 sDistanceVisible = VISION_RANGE;
DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("FindFlankingSpot: orig loc = %d, loc to flank = %d", pSoldier->sGridNo , sPos));
@@ -2617,7 +2617,9 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction )
}
// sevenfm: allow water flanking only for CUNNINGSOLO soldiers
if( Water( sGridNo, pSoldier->pathing.bLevel ) && pSoldier->aiData.bAttitude != CUNNINGSOLO )
if( Water( sGridNo, pSoldier->pathing.bLevel ) &&
pSoldier->aiData.bAttitude != CUNNINGSOLO &&
pSoldier->aiData.bAttitude != CUNNINGAID )
{
continue;
}
@@ -2635,8 +2637,8 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction )
}
// sevenfm: penalize locations with no sight cover from noise gridno (supposed that we are sneaking)
if( PythSpacesAway( sGridNo, sPos) < sDistanceVisible &&
LocationToLocationLineOfSightTest( sGridNo, pSoldier->pathing.bLevel, sPos, pSoldier->pathing.bLevel, TRUE) )
if( //PythSpacesAway( sGridNo, sPos) <= sDistanceVisible &&
LocationToLocationLineOfSightTest( sGridNo, pSoldier->pathing.bLevel, sPos, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) )
{
//continue;
sTempDist = sTempDist / 2;
@@ -2647,12 +2649,14 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction )
{
sTempDist = sTempDist / 2;
}
// sevenfm: try to flank closer to vision distance limit for faster flanking
if( PythSpacesAway( sGridNo, sPos) > sDistanceVisible + 10 )
{
sTempDist = sTempDist / 2;
}
// allow extra directions for flanking
if ( bAction == AI_ACTION_FLANK_LEFT )
{
// sevenfm: allow two extra directions
+21 -15
View File
@@ -4,6 +4,7 @@
#include "types.h"
#include "worlddef.h"
#include "Soldier Control.h"
#include "Isometric Utils.h"
#define TESTAICONTROL
@@ -221,7 +222,7 @@ void HandleInitialRedAlert( INT8 bTeam, UINT8 ubCommunicate);
void InitPanicSystem();
INT16 InWaterOrGas(SOLDIERTYPE *pSoldier, INT32 sGridno);
BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier);
BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier, INT8 bAction = AI_ACTION_NONE);
BOOLEAN InitAI( void );
void MakeClosestEnemyChosenOne();
@@ -274,29 +275,34 @@ UINT8 GetClosestMedicSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTe
// sevenfm:
INT16 MaxNormalVisionDistance( void );
UINT8 MinFlankDirections( SOLDIERTYPE *pSoldier );
UINT8 CountFriendsInDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo );
BOOLEAN GuySawEnemyThisTurnOrBefore( SOLDIERTYPE * pSoldier );
BOOLEAN CheckSuppressionDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo );
UINT8 CountNearbyFriendlies( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 CountNearbyFriendliesLastAttackHit( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 CountFriendsFlankSeek( SOLDIERTYPE *pSoldier );
UINT8 CountNearbyFriendliesContact( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 CountNearbyFriendliesNoContact( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 CountNearbyFriends( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 CountNearbyFriendsLastAttackHit( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 CountFriendsFlankSameSpot( SOLDIERTYPE *pSoldier );
UINT8 CountFriendsBlack( SOLDIERTYPE *pSoldier, INT32 sClosestOpponent = NOWHERE );
BOOLEAN AICheckFriendsNoContact( SOLDIERTYPE *pSoldier );
BOOLEAN AICheckIsFlanking( SOLDIERTYPE *pSoldier );
INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier);
// moved from DecideAction.cpp
// sevenfm: set MAX_FLANKS_RED and MAX_FLANKS_YELLOW to equal values to avoid problems when soldier's alert state changes
BOOLEAN ProneSightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot );
BOOLEAN SightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot );
#define MAX_FLANKS_RED 25
#define MAX_FLANKS_YELLOW 25
// limit min/max flank distance depending on sight range and time of day
#define MIN_FLANK_DIST_YELLOW (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO)
#define MAX_FLANK_DIST_YELLOW (MaxNormalVisionDistance() + 20)
// vision range defines
#define DAY_VISION_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
#define NIGHT_VISION_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO )
#define VISION_RANGE MaxNormalVisionDistance()
// limit min/max flank distance depending on sight range and time of day
#define MIN_FLANK_DIST_RED (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO)
#define MAX_FLANK_DIST_RED (MaxNormalVisionDistance() + 20)
// sevenfm: limit min/max flank distance depending on sight range and time of day
#define MIN_FLANK_DIST_YELLOW (DAY_VISION_RANGE/2)
#define MAX_FLANK_DIST_YELLOW (VISION_RANGE + 20)
#define MIN_FLANK_DIST_RED (DAY_VISION_RANGE/2)
#define MAX_FLANK_DIST_RED (VISION_RANGE + 20)
#endif