mirror of
https://github.com/1dot13/source.git
synced 2026-08-19 14:20:30 +02:00
Added Headrock's HAM 2.8
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@2650 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
@@ -231,4 +231,8 @@ INT16 FindBestCoverNearTheGridNo(SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubS
|
||||
|
||||
INT8 FindDirectionForClimbing( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bLevel);
|
||||
|
||||
// HEADROCK HAM B2.7: Functions to assist group AI
|
||||
|
||||
INT16 AssessTacticalSituation( INT8 bSide );
|
||||
BOOLEAN TeamSeesOpponent( INT8 bSide, SOLDIERTYPE * pOpponent );
|
||||
INT32 CalcStraightThreatValue( SOLDIERTYPE *pEnemy );
|
||||
|
||||
@@ -2565,3 +2565,236 @@ void AINameMessage(SOLDIERTYPE * pSoldier,const STR8 str,INT32 num)
|
||||
DebugAI( tempstr );
|
||||
}
|
||||
#endif
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// HEADROCK:
|
||||
//
|
||||
// The following function(s) are part of my half-assed attempt to have the AI analyze the tactical
|
||||
// situation, by comparing (known) squad sizes, the state of all combatants, and the orders of all
|
||||
// friendlies. The idea is to return a value called "TacticalSituation" which can tell a combatant
|
||||
// whether he should try to undertake a smarter course of action.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
INT16 AssessTacticalSituation( INT8 bTeam )
|
||||
{
|
||||
UINT16 ubFriendlyTeamTacticalValue = 0;
|
||||
UINT16 ubEnemyTeamTacticalValue = 0;
|
||||
UINT8 ubSoldierTacticalThreat;
|
||||
INT16 ubTacticalSituation;
|
||||
UINT16 cnt;
|
||||
SOLDIERTYPE * pSoldier;
|
||||
|
||||
// begin loop through all MERCs.
|
||||
for ( cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++ )
|
||||
{
|
||||
pSoldier = MercPtrs[ cnt ];
|
||||
ubSoldierTacticalThreat = CalcStraightThreatValue( pSoldier );
|
||||
// Player-controlled Mercs are 1.5 times more threatening than AIs
|
||||
if (pSoldier->flags.uiStatusFlags & SOLDIER_PC)
|
||||
ubSoldierTacticalThreat = (UINT8)((float)ubSoldierTacticalThreat * 1.5);
|
||||
|
||||
// Assess Threat
|
||||
if (bTeam == OUR_TEAM || bTeam == MILITIA_TEAM)
|
||||
{
|
||||
// Friendly!
|
||||
ubFriendlyTeamTacticalValue += ubSoldierTacticalThreat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enemy!
|
||||
if ( TeamSeesOpponent( ENEMY_TEAM, pSoldier ) )
|
||||
ubEnemyTeamTacticalValue += ubSoldierTacticalThreat;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// begin loop through all Militia.
|
||||
for ( cnt = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; cnt++ )
|
||||
{
|
||||
pSoldier = MercPtrs[ cnt ];
|
||||
ubSoldierTacticalThreat = CalcStraightThreatValue( pSoldier );
|
||||
|
||||
// Assess Threat
|
||||
if (bTeam == OUR_TEAM || bTeam == MILITIA_TEAM)
|
||||
{
|
||||
// Friendly!
|
||||
ubFriendlyTeamTacticalValue += ubSoldierTacticalThreat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enemy!
|
||||
if ( TeamSeesOpponent( ENEMY_TEAM, pSoldier ) )
|
||||
ubEnemyTeamTacticalValue += ubSoldierTacticalThreat;
|
||||
}
|
||||
}
|
||||
|
||||
// begin loop through all Enemies.
|
||||
for ( cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++ )
|
||||
{
|
||||
pSoldier = MercPtrs[ cnt ];
|
||||
ubSoldierTacticalThreat = CalcStraightThreatValue( pSoldier );
|
||||
|
||||
// Assess Threat
|
||||
|
||||
if (bTeam == ENEMY_TEAM)
|
||||
{
|
||||
// Friendly!
|
||||
ubFriendlyTeamTacticalValue += ubSoldierTacticalThreat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enemy!
|
||||
if ( TeamSeesOpponent( OUR_TEAM, pSoldier ) || TeamSeesOpponent ( MILITIA_TEAM, pSoldier) )
|
||||
ubEnemyTeamTacticalValue += ubSoldierTacticalThreat;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ubTacticalSituation = ubEnemyTeamTacticalValue - ubFriendlyTeamTacticalValue;
|
||||
|
||||
return (ubTacticalSituation);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// HEADROCK: Function to check whether a team can see the specified soldier.
|
||||
BOOLEAN TeamSeesOpponent( INT8 bTeam, SOLDIERTYPE * pOpponent )
|
||||
{
|
||||
SOLDIERTYPE * pSoldier;
|
||||
UINT16 cnt;
|
||||
|
||||
// This assertion can be safely removed, assuming the program does what it should. It simply checks
|
||||
// whether the "opponent" is on the same team being checked. That should be avoided when calling this
|
||||
// function.
|
||||
Assert( pOpponent->bTeam != bTeam );
|
||||
|
||||
// We're checking Merc/Militia visibility
|
||||
if (bTeam == OUR_TEAM || bTeam == MILITIA_TEAM )
|
||||
{
|
||||
for ( cnt = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; cnt++ )
|
||||
{
|
||||
pSoldier = MercPtrs[ cnt ];
|
||||
|
||||
if (pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE)
|
||||
{
|
||||
|
||||
|
||||
if (pSoldier->aiData.bOppList[ pOpponent->ubID ] == SEEN_CURRENTLY)
|
||||
return ( TRUE );
|
||||
}
|
||||
}
|
||||
for ( cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++ )
|
||||
{
|
||||
pSoldier = MercPtrs[ cnt ];
|
||||
|
||||
if (pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE)
|
||||
{
|
||||
// This assertion can be safely removed, assuming the program does what it should. It simply checks
|
||||
// whether the "opponent" is on the same team being checked. That should be avoided when calling this
|
||||
// function.
|
||||
//Assert( pOpponent->bSide != bSide );
|
||||
|
||||
if (pSoldier->aiData.bOppList[ pOpponent->ubID ] == SEEN_CURRENTLY)
|
||||
return ( TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
return ( FALSE );
|
||||
}
|
||||
// Check enemy visibility
|
||||
else if (bTeam == ENEMY_TEAM)
|
||||
{
|
||||
for ( cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++ )
|
||||
{
|
||||
pSoldier = MercPtrs[ cnt ];
|
||||
|
||||
if (pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE)
|
||||
{
|
||||
// This assertion can be safely removed, assuming the program does what it should. It simply checks
|
||||
// whether the "opponent" is on the same team being checked. That should be avoided when calling this
|
||||
// function.
|
||||
//Assert( pOpponent->bSide != bSide );
|
||||
|
||||
if (pSoldier->aiData.bOppList[ pOpponent->ubID ] == SEEN_CURRENTLY)
|
||||
return ( TRUE );
|
||||
}
|
||||
}
|
||||
return ( FALSE );
|
||||
}
|
||||
|
||||
else
|
||||
return (FALSE);
|
||||
|
||||
}
|
||||
|
||||
// HEADROCK: Function to assess an enemy's threat value without "me" argument.
|
||||
INT32 CalcStraightThreatValue( SOLDIERTYPE *pEnemy )
|
||||
{
|
||||
INT32 iThreatValue = 0;
|
||||
|
||||
// If man is inactive, at base, on assignment, dead, unconscious
|
||||
if (!pEnemy->bActive || !pEnemy->bInSector || !pEnemy->stats.bLife )
|
||||
{
|
||||
// he's no threat at all, return a negative number
|
||||
iThreatValue = 0;
|
||||
return(iThreatValue);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// ADD twice the man's level (2-20)
|
||||
iThreatValue += pEnemy->stats.bExpLevel;
|
||||
|
||||
// ADD man's total action points (10-35)
|
||||
iThreatValue += pEnemy->CalcActionPoints();
|
||||
|
||||
// ADD 1/2 of man's current action points (4-17)
|
||||
iThreatValue += (pEnemy->bActionPoints / 2);
|
||||
|
||||
// ADD 1/10 of man's current health (0-10)
|
||||
iThreatValue += (pEnemy->stats.bLife / 10);
|
||||
|
||||
if (pEnemy->bAssignment < ON_DUTY )
|
||||
{
|
||||
// ADD 1/4 of man's protection percentage (0-25)
|
||||
iThreatValue += ArmourPercent( pEnemy ) / 4;
|
||||
|
||||
// ADD 1/5 of man's marksmanship skill (0-20)
|
||||
iThreatValue += (pEnemy->stats.bMarksmanship / 5);
|
||||
|
||||
if ( Item[ pEnemy->inv[HANDPOS].usItem ].usItemClass & IC_WEAPON )
|
||||
{
|
||||
// ADD the deadliness of the item(weapon) he's holding (0-50)
|
||||
iThreatValue += Weapon[pEnemy->inv[HANDPOS].usItem].ubDeadliness;
|
||||
}
|
||||
}
|
||||
|
||||
// SUBTRACT 1/5 of man's bleeding (0-20)
|
||||
iThreatValue -= (pEnemy->bBleeding / 5);
|
||||
|
||||
// SUBTRACT 1/10 of man's breath deficiency (0-10)
|
||||
iThreatValue -= ((100 - pEnemy->bBreath) / 10);
|
||||
|
||||
// SUBTRACT man's current shock value
|
||||
iThreatValue -= pEnemy->aiData.bShock;
|
||||
}
|
||||
|
||||
// if this man is conscious
|
||||
if (pEnemy->stats.bLife < OKLIFE)
|
||||
{
|
||||
// if he's still something of a threat
|
||||
if (iThreatValue > 0)
|
||||
{
|
||||
// drastically reduce his threat value (divide by 5 to 18)
|
||||
iThreatValue /= (4 + (OKLIFE - pEnemy->stats.bLife));
|
||||
}
|
||||
}
|
||||
|
||||
// threat value of any opponent can never drop below 1
|
||||
if (iThreatValue < 0)
|
||||
{
|
||||
iThreatValue = 0;
|
||||
}
|
||||
|
||||
return(iThreatValue);
|
||||
}
|
||||
|
||||
+20
-4
@@ -1296,7 +1296,9 @@ void CalcBestStab(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab, BOOLEAN fBladeAt
|
||||
}
|
||||
}
|
||||
else
|
||||
ubChanceToHit = MAXCHANCETOHIT;
|
||||
// HEADROCK (HAM): Externalized maximum to JA2_OPTIONS.INI
|
||||
// ubChanceToHit = MAXCHANCETOHIT;
|
||||
ubChanceToHit = gGameExternalOptions.iMaximumCTH;
|
||||
//NumMessage("chance to Hit = ",ubChanceToHit);
|
||||
|
||||
//sprintf(tempstr,"Vs. %s, at AimTime %d, ubChanceToHit = %d",ExtMen[pOpponent->ubID].name,ubAimTime,ubChanceToHit);
|
||||
@@ -1473,7 +1475,9 @@ void CalcTentacleAttack(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab )
|
||||
ubChanceToHit = (INT16) CalcChanceToStab(pSoldier,pOpponent,ubAimTime);
|
||||
}
|
||||
else
|
||||
ubChanceToHit = MAXCHANCETOHIT;
|
||||
// HEADROCK (HAM): Externalized maximum to JA2_OPTIONS.INI
|
||||
// ubChanceToHit = MAXCHANCETOHIT;
|
||||
ubChanceToHit = gGameExternalOptions.iMaximumCTH;
|
||||
//NumMessage("chance to Hit = ",ubChanceToHit);
|
||||
|
||||
//sprintf(tempstr,"Vs. %s, at AimTime %d, ubChanceToHit = %d",ExtMen[pOpponent->ubID].name,ubAimTime,ubChanceToHit);
|
||||
@@ -2472,8 +2476,20 @@ void CheckIfShotPossible(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN s
|
||||
}
|
||||
|
||||
//if ( (!suppressionFire && ( (IsScoped(pObj) && GunRange(pObj) > pSoldier->MaxDistanceVisible(pBestShot->sTarget, pBestShot->bTargetLevel) ) || pSoldier->bOrders == SNIPER ) ) ||
|
||||
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 ))
|
||||
// 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;
|
||||
|
||||
if ( gGameExternalOptions.fIncreaseAISuppressionFire && ((!suppressionFire && ( (IsScoped(pObj) && GunRange(pObj) > MaxNormalDistanceVisible() ) || pSoldier->aiData.bOrders == SNIPER ) ) ||
|
||||
(suppressionFire && IsGunAutofireCapable(&pSoldier->inv[pBestShot->bWeaponIn]))) )
|
||||
fEnableAISuppression = TRUE;
|
||||
else if ( !gGameExternalOptions.fIncreaseAISuppressionFire && ( (!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 )))
|
||||
fEnableAISuppression = TRUE;
|
||||
|
||||
if (fEnableAISuppression)
|
||||
{
|
||||
// get the minimum cost to attack with this item
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CheckIfShotPossible: getting min aps");
|
||||
|
||||
@@ -1950,6 +1950,8 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
|
||||
BOOLEAN fCivilian = (PTR_CIVILIAN && (pSoldier->ubCivilianGroup == NON_CIV_GROUP ||
|
||||
(pSoldier->aiData.bNeutral && gTacticalStatus.fCivGroupHostile[pSoldier->ubCivilianGroup] == CIV_GROUP_NEUTRAL) ||
|
||||
(pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) );
|
||||
// HEADROCK HAM B2.7: Calculate the overall tactical situation
|
||||
INT16 ubOverallTacticalSituation = AssessTacticalSituation(pSoldier->bSide);
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("DecideActionRed: soldier orders = %d",pSoldier->aiData.bOrders));
|
||||
|
||||
@@ -2313,7 +2315,17 @@ if(!is_networked)//hayden
|
||||
CheckIfShotPossible(pSoldier,&BestShot,TRUE);
|
||||
|
||||
//must have a small chance to hit and the opponent must be on the ground (can't suppress guys on the roof)
|
||||
if ( BestShot.ubPossible && BestShot.ubChanceToReallyHit < 50 && Menptr[BestShot.ubOpponent].pathing.bLevel == 0 && pSoldier->aiData.bOrders != SNIPER )
|
||||
// HEADROCK HAM BETA2.4: Adjusted this for a random chance to suppress regardless of chance. This augments
|
||||
// current revamp of suppression fire.
|
||||
BOOLEAN fEnableAIAutofire = FALSE;
|
||||
|
||||
if ( gGameExternalOptions.fIncreaseAISuppressionFire && ( ( BestShot.ubPossible && BestShot.ubChanceToReallyHit < 50 ) || (BestShot.ubPossible && BestShot.ubChanceToReallyHit < PreRandom(100)) && Menptr[BestShot.ubOpponent].pathing.bLevel == 0 && pSoldier->aiData.bOrders != SNIPER ))
|
||||
fEnableAIAutofire = TRUE;
|
||||
|
||||
else if ( !gGameExternalOptions.fIncreaseAISuppressionFire && ( BestShot.ubPossible && BestShot.ubChanceToReallyHit < 50 && Menptr[BestShot.ubOpponent].pathing.bLevel == 0 && pSoldier->aiData.bOrders != SNIPER ))
|
||||
fEnableAIAutofire = TRUE;
|
||||
|
||||
if (fEnableAIAutofire)
|
||||
{
|
||||
// then do it!
|
||||
|
||||
@@ -3141,7 +3153,23 @@ if(!is_networked)//hayden
|
||||
{
|
||||
// only try to run if we've actually been hit recently & noticably so
|
||||
// otherwise, presumably our current cover is pretty good & sufficient
|
||||
if (pSoldier->aiData.bShock > 0 || fCivilian)
|
||||
// HEADROCK HAM B2.6: New value here helps us change the ratio of running away due to shock. This
|
||||
// is terribly important if Suppression Shock is enabled.
|
||||
UINT16 bShock = 0;
|
||||
|
||||
if (gGameExternalOptions.fSuppressionShock)
|
||||
{
|
||||
// If bShock value is greater than (2*ExpLevel + MoraleModifier)*1.5, the target will flee.
|
||||
bShock = pSoldier->aiData.bShock;
|
||||
if (bShock <= ((float)CalcSuppressionTolerance(pSoldier)*(float)1.5))
|
||||
bShock = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bShock = pSoldier->aiData.bShock;
|
||||
}
|
||||
|
||||
if (bShock > 0 || fCivilian)
|
||||
{
|
||||
// look for best place to RUN AWAY to (farthest from the closest threat)
|
||||
pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier);
|
||||
@@ -4424,7 +4452,10 @@ INT8 DecideActionBlack(SOLDIERTYPE *pSoldier)
|
||||
case ATTACKSLAYONLY:iChance += 30; break;
|
||||
}
|
||||
|
||||
if ( pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 50 )
|
||||
// HEADROCK HAM B2.6: Allows control over increased enemy burstfire.
|
||||
if ( gGameExternalOptions.fIncreaseAISuppressionFire && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 10 )
|
||||
iChance += 20;
|
||||
else if ( !gGameExternalOptions.fIncreaseAISuppressionFire && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 50 )
|
||||
iChance += 20;
|
||||
|
||||
// increase chance based on proximity and difficulty of enemy
|
||||
@@ -4502,7 +4533,10 @@ INT8 DecideActionBlack(SOLDIERTYPE *pSoldier)
|
||||
}
|
||||
|
||||
|
||||
if ( pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 50 )
|
||||
// HEADROCK HAM B2.6: Allows control over increased enemy autofire.
|
||||
if ( gGameExternalOptions.fIncreaseAISuppressionFire && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 20 )
|
||||
iChance += 30;
|
||||
else if ( !gGameExternalOptions.fIncreaseAISuppressionFire && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 50 )
|
||||
iChance += 30;
|
||||
|
||||
if ( bInGas )
|
||||
|
||||
Reference in New Issue
Block a user