Red, Black AI: try to use sidearm if not enough AP to shoot with main weapon.

Black AI: improved check to avoid friendly fire.
FindAIUsableObjClass: by default search for weapon with longest range, if fSidearm = TRUE search for fastest weapon.
AICalcChanceToHitGun: if AI_EXTRA_SUPPRESSION = TRUE, always return min 1% for AI.
RangeChangeDesire:
- civilians only want to run away
- added small bonus to range change desire if soldier has no gun or short range weapon
CalcBestShot, CheckIfShotPossible: always check if can shoot at target seen by another soldier.
CalcBestShot, CalcBestThrow, CalcBestStab: initialize attack data.
Removed obsolete code.

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8782 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Sevenfm
2020-04-08 21:24:56 +00:00
parent 5350258334
commit 4e34605f86
8 changed files with 209 additions and 607 deletions
+28 -12
View File
@@ -1744,32 +1744,48 @@ INT8 FindObjClass( SOLDIERTYPE * pSoldier, UINT32 usItemClass )
return( NO_SLOT );
}
INT8 FindAIUsableObjClass( SOLDIERTYPE * pSoldier, UINT32 usItemClass )
INT8 FindAIUsableObjClass(SOLDIERTYPE * pSoldier, UINT32 usItemClass, BOOLEAN fSidearm)
{
// finds the first object of the specified class which does NOT have
// the "unusable by AI" flag set.
// uses & rather than == so that this function can search for any weapon
// This is for the AI only so:
INT8 bBestSlot = NO_SLOT;
INT8 bInvSize = (INT8)pSoldier->inv.size();
// Do not consider tank cannons or rocket launchers to be "guns"
INT8 invsize = (INT8)pSoldier->inv.size();
for (INT8 bLoop = 0; bLoop < invsize; ++bLoop)
for (INT8 bLoop = 0; bLoop < bInvSize; ++bLoop)
{
if (pSoldier->inv[bLoop].exists() == true) {
if ( (Item[pSoldier->inv[bLoop].usItem].usItemClass & usItemClass) && !(pSoldier->inv[bLoop].fFlags & OBJECT_AI_UNUSABLE) && (pSoldier->inv[bLoop][0]->data.objectStatus >= USABLE ) )
if (pSoldier->inv[bLoop].exists())
{
if ((Item[pSoldier->inv[bLoop].usItem].usItemClass & usItemClass) &&
!(pSoldier->inv[bLoop].fFlags & OBJECT_AI_UNUSABLE) &&
(pSoldier->inv[bLoop][0]->data.objectStatus >= USABLE))
{
if ( usItemClass == IC_GUN && EXPLOSIVE_GUN( pSoldier->inv[bLoop].usItem ) )
// Do not consider tank cannons or rocket launchers to be "guns" for AI
if (usItemClass == IC_GUN && EXPLOSIVE_GUN(pSoldier->inv[bLoop].usItem))
{
continue;
}
return( bLoop );
// if not searching for gun, return first usable of required type
if (usItemClass != IC_GUN)
{
bBestSlot = bLoop;
break;
}
// we are searching for gun
if (bBestSlot == NO_SLOT || // haven't found any gun yet
// by default, search for weapon with longest range
!fSidearm && Weapon[pSoldier->inv[bLoop].usItem].usRange > Weapon[pSoldier->inv[bBestSlot].usItem].usRange ||
// when searching for sidearm, find fastest weapon
fSidearm && Weapon[Item[pSoldier->inv[bLoop].usItem].ubClassIndex].ubReadyTime + BaseAPsToShootOrStab(APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], &pSoldier->inv[bLoop], pSoldier) <
Weapon[Item[pSoldier->inv[bBestSlot].usItem].ubClassIndex].ubReadyTime + BaseAPsToShootOrStab(APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], &pSoldier->inv[bBestSlot], pSoldier))
{
bBestSlot = bLoop;
}
}
}
}
return( NO_SLOT );
return bBestSlot;
}
INT8 FindAIUsableObjClassWithin( SOLDIERTYPE * pSoldier, UINT32 usItemClass, INT8 bLower, INT8 bUpper )
+11 -17
View File
@@ -16,29 +16,23 @@ class SOLDIERTYPE;
#define NIGHTSIGHTGOGGLES_BONUS 2 * STRAIGHT_RATIO
#define UVGOGGLES_BONUS 4 * STRAIGHT_RATIO
extern UINT8 SlotToPocket[7];
extern BOOLEAN WeaponInHand( SOLDIERTYPE * pSoldier );
INT8 FindAmmo( SOLDIERTYPE * pSoldier, UINT8 ubCalibre, UINT16 ubMagSize, UINT8 ubAmmoType, INT8 bExcludeSlot );
INT8 FindBestWeaponIfCurrentIsOutOfRange(SOLDIERTYPE * pSoldier, INT8 bCurrentWeaponIndex, UINT16 bWantedRange);
INT16 FindAttachmentSlot( OBJECTTYPE* pObj, UINT16 usItem, UINT8 subObject = 0);
OBJECTTYPE* FindAttachment( OBJECTTYPE * pObj, UINT16 usItem, UINT8 subObject = 0 );
extern INT8 FindObjClass( SOLDIERTYPE * pSoldier, UINT32 usItemClass );
extern INT8 FindAIUsableObjClass( SOLDIERTYPE * pSoldier, UINT32 usItemClass );
extern INT8 FindAIUsableObjClassWithin( SOLDIERTYPE * pSoldier, UINT32 usItemClass, INT8 bLower, INT8 bUpper );
extern INT8 FindEmptySlotWithin( SOLDIERTYPE * pSoldier, INT8 bLower, INT8 bUpper );
extern INT8 FindObjInObjRange( SOLDIERTYPE * pSoldier, UINT16 usItem1, UINT16 usItem2 );
extern INT8 FindLaunchable( SOLDIERTYPE * pSoldier, UINT16 usWeapon );
extern INT8 FindGLGrenade( SOLDIERTYPE * pSoldier );
extern INT8 FindThrowableGrenade( SOLDIERTYPE * pSoldier );
extern INT8 FindUsableObj( SOLDIERTYPE * pSoldier, UINT16 usItem );
INT16 FindAttachmentSlot(OBJECTTYPE* pObj, UINT16 usItem, UINT8 subObject = 0);
OBJECTTYPE* FindAttachment(OBJECTTYPE * pObj, UINT16 usItem, UINT8 subObject = 0);
extern INT8 FindObjClass(SOLDIERTYPE * pSoldier, UINT32 usItemClass);
extern INT8 FindAIUsableObjClass(SOLDIERTYPE * pSoldier, UINT32 usItemClass, BOOLEAN fSidearm = FALSE);
extern INT8 FindAIUsableObjClassWithin(SOLDIERTYPE * pSoldier, UINT32 usItemClass, INT8 bLower, INT8 bUpper);
extern INT8 FindEmptySlotWithin(SOLDIERTYPE * pSoldier, INT8 bLower, INT8 bUpper);
extern INT8 FindObjInObjRange(SOLDIERTYPE * pSoldier, UINT16 usItem1, UINT16 usItem2);
extern INT8 FindLaunchable(SOLDIERTYPE * pSoldier, UINT16 usWeapon);
extern INT8 FindGLGrenade(SOLDIERTYPE * pSoldier);
extern INT8 FindThrowableGrenade(SOLDIERTYPE * pSoldier);
extern INT8 FindUsableObj(SOLDIERTYPE * pSoldier, UINT16 usItem);
void DeleteObj(OBJECTTYPE * pObj );
void SwapObjs( OBJECTTYPE * pObj1, OBJECTTYPE * pObj2 );
+22 -22
View File
@@ -7364,6 +7364,7 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim
// same as CCTHG but fakes the attacker always standing
usTrueState = pSoldier->usAnimState;
bTrueLevel = pSoldier->bTargetLevel;
pSoldier->bTargetLevel = bTargetLevel;
pSoldier->usAnimState = usAnimState;
if(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE)//dnl ch70 160913
@@ -7373,18 +7374,11 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim
else
{
uiChance = CalcChanceToHitGun(pSoldier, sGridNo, ubAimTime, ubAimPos);
// sevenfm: allow small CTH for AI for suppression fire
if( gGameExternalOptions.fAIExtraSuppression &&
pSoldier->aiData.bAlertStatus == STATUS_RED &&
Weapon[ pSoldier->usAttackingWeapon ].bAutofireShotsPerFiveAP > 0 )
{
uiChance = __max(1, uiChance);
}
}
pSoldier->usAnimState = usTrueState;
pSoldier->bTargetLevel = bTrueLevel;
if(UsingNewCTHSystem() == true && !(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE))//dnl ch70 160913
if(UsingNewCTHSystem() && !(Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE))//dnl ch70 160913
{
////////////////////////////////////////////////////////////////////////////////////
// HEADROCK HAM 4: NCTH calculation
@@ -7468,11 +7462,7 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim
// real aperture for shooter based on CTH calculation
dAperture = dAperture * ( 100 - uiChance ) / 100.0f;
if (dAperture == 0)
{
return 100;
}
else
if(dAperture > 0)
{
// silversurfer: This cannot be correct. An aperture of 10 already gives a very good chance to hit. If we take this value and
// calculate the target area it's PI * 10 * 10 which results in ~300 (rounded down) and not 28.26. This low number was the reason why AI
@@ -7488,18 +7478,19 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim
uiChance = (UINT32)__min(100, (dTargetArea / dApertureArea) * 100);
}
FLOAT dGunRange;
else
{
uiChance = 100;
}
// Flugente: check for underbarrel weapons and use that object if necessary
OBJECTTYPE* pObjAttHand = pSoldier->GetUsedWeapon( &(pSoldier->inv[pSoldier->ubAttackingHand]) );
dGunRange = (FLOAT)(GunRange( pObjAttHand, pSoldier ) );
FLOAT dGunRange = (FLOAT)(GunRange(pObjAttHand, pSoldier));
FLOAT dMaxGunRange = dGunRange * gGameCTHConstants.MAX_EFFECTIVE_RANGE_MULTIPLIER;
if ( dMaxGunRange < d2DDistance)
{
// Weapon out of conceivable hit range. Reduce chance to hit to 0!
return (0);
uiChance = 1; // sevenfm: set min CTH to 1% for AI
}
else if ( dGunRange < d2DDistance)
{
@@ -7508,15 +7499,24 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim
if (gGameCTHConstants.MAX_EFFECTIVE_USE_GRADIENT)
{
// Just outside range. Reduce considerably!
return min(uiChance, (UINT)(dChance - (dMaxChanceReduction * ((d2DDistance - dGunRange) / (dMaxGunRange - dGunRange)))));
uiChance = min(uiChance, (UINT32)(dChance - (dMaxChanceReduction * ((d2DDistance - dGunRange) / (dMaxGunRange - dGunRange)))));
}
else
{
return (UINT)(dChance - dMaxChanceReduction);
uiChance = (UINT32)(dChance - dMaxChanceReduction);
}
}
}
return( uiChance );
if (gGameExternalOptions.fAIExtraSuppression)
{
// sevenfm: always min 1% for AI to shoot
return max(1, uiChance);
}
else
{
return uiChance;
}
}
INT32 CalcBodyImpactReduction( UINT8 ubAmmoType, UINT8 ubHitLocation )