Brief: //dnl ch69

- Add alternative fix for CalcBestShot which should remove existing APs problems maybe even create AI deadlier then ever ;-)
Details:
- Existing CalcBestShot simply became inadequate and buggier for so many add ons to game, so compiling with dnlCALCBESTSHOT is alternative option which calculate all turning, stance and raising gun cost for different stances and all available scopes, and after all hoping fix incorrect APs calculation as well, hitrate formula is adapt to 100AP system, also try to optimize a bit to get less AICalcChanceToHitGun calls and to use with different stance decisions.
- From ch58 aiming for burst and autofire was disabled as was causing invalid action handle due to incorrect APs calculation so from now aiming AI also use aiming before bursting.


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6403 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Kriplo
2013-09-15 15:59:51 +00:00
parent 2e4a12173a
commit d24b54cb5a
7 changed files with 291 additions and 89 deletions
+9 -11
View File
@@ -2116,7 +2116,7 @@ UINT16 CalculateRaiseGunCost(SOLDIERTYPE *pSoldier, BOOLEAN fAddingRaiseGunCost,
BOOLEAN fAltFireMode = FALSE;
if ( gGameExternalOptions.ubAllowAlternativeWeaponHolding )
{
if ( gGameExternalOptions.ubAllowAlternativeWeaponHolding == 3 && pSoldier->bTeam == gbPlayerNum )
if ( gGameExternalOptions.ubAllowAlternativeWeaponHolding == 3 )//dnl ch69 130913 condition (&& pSoldier->bTeam == gbPlayerNum) must be out because prevent AI from doing alternate weapon holding
{
if ( pSoldier->bScopeMode == -1 )
fAltFireMode = TRUE;
@@ -2244,7 +2244,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 bAimTime,
}
}
else
{
{
//CHRISL: When prone and using a bipod, bipod should help compensate for recoil. To reflect this, our shot AP cost should be minimially reduced
if(gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier > 0 && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE && GetBipodBonus(&(pSoldier->inv[HANDPOS])) > 0)
{
@@ -2303,7 +2303,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 bAimTime,
/////////////////////////////////////////////////////////////////////////////////////
}
if (ubForceRaiseGunCost)
if (ubForceRaiseGunCost == TRUE )//dnl ch69 140913
{
fAddingRaiseGunCost = TRUE;
}
@@ -2330,7 +2330,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 bAimTime,
}
}
if ( AM_A_ROBOT( pSoldier ) || TANK( pSoldier ) )//dnl ch64 300813 robots and tanks cannot do this
if ( AM_A_ROBOT( pSoldier ) || TANK( pSoldier ) || ubForceRaiseGunCost == 2 )//dnl ch64 300813 robots and tanks cannot do this //dnl ch69 150913 need option to override raise gun cost
{
fAddingRaiseGunCost = FALSE;
}
@@ -2367,8 +2367,7 @@ INT16 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 bAimTime,
// if attacking a new target (or if the specific target is uncertain)
// Added check if the weapon is throwing knife/melee weapons - otherwise it would add APs for change target on cursor but not actually deduct them afterwards - SANDRO
if ( ubForceRaiseGunCost || (( sGridNo != pSoldier->sLastTarget ) && !Item[usUBItem].rocketlauncher && ( Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE )&&
( Item[ usUBItem ].usItemClass != IC_PUNCH ) && ( Item[ usUBItem ].usItemClass != IC_BLADE ) ) )
if ( ubForceRaiseGunCost == TRUE || (( sGridNo != pSoldier->sLastTarget ) && !Item[usUBItem].rocketlauncher && ( Item[ usUBItem ].usItemClass != IC_THROWING_KNIFE ) && ( Item[ usUBItem ].usItemClass != IC_PUNCH ) && ( Item[ usUBItem ].usItemClass != IC_BLADE ) ) )//dnl ch69 140913
{
if ( pSoldier->IsValidAlternativeFireMode( bAimTime, sGridNo ) )
bAPCost += (APBPConstants[AP_CHANGE_TARGET] / 2);
@@ -3813,15 +3812,15 @@ INT16 GetAPsToJumpOver( SOLDIERTYPE *pSoldier )
// HEADROCK HAM 3.6: Calculate the actual AP cost to add this many Extra Aiming levels, taking into account
// APBP Constants and extra game features.
INT32 CalcAPCostForAiming( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAimTime, UINT8 ubAddRaiseGunCost )//dnl ch64 310813
INT32 CalcAPCostForAiming( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAimTime )
{
INT16 sAPCost = 0;
UINT16 usItemNum = pSoldier->inv[HANDPOS].usItem;
Assert(pSoldier != NULL);
Assert(&pSoldier->inv[HANDPOS] != NULL);
if (ubAddRaiseGunCost && !TANK(pSoldier) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE )))//dnl ch64 310813
#ifndef dnlCALCBESTSHOT//dnl ch69 150913 if this is turn on we get incorrect APs as MinAPsToShootOrStab always include raise gun cost
if (!TANK(pSoldier) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE )))//dnl ch64 310813
{
// Weapon not ready, check aiming from hip, else add raise gun cost
//if (!pSoldier->IsValidShotFromHip(bAimTime,sTargetGridNo))
@@ -3829,8 +3828,7 @@ INT32 CalcAPCostForAiming( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAim
sAPCost += CalculateRaiseGunCost( pSoldier, TRUE, sTargetGridNo, bAimTime );
}
}
#endif
if (gGameExternalOptions.fIncreasedAimingCost )
{
// HEADROCK HAM B2.6: Changed the number of APs to attack when aiming.
+1 -1
View File
@@ -368,7 +368,7 @@ UINT16 GetAPsToReloadRobot( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pRobot );
INT16 GetAPsToReloadGunWithAmmo( SOLDIERTYPE *pSoldier, OBJECTTYPE * pGun, OBJECTTYPE * pAmmo, BOOLEAN usAllAPs = TRUE );
INT16 GetAPsToAutoReload( SOLDIERTYPE * pSoldier );
INT32 CalcAPCostForAiming( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAimTime, UINT8 ubAddRaiseGunCost=TRUE );//dnl ch64 310813
INT32 CalcAPCostForAiming( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAimTime );
INT8 CalcAimingLevelsAvailableWithAP( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, INT8 bAPsLeft );
// SANDRO - added these:
+24 -19
View File
@@ -126,25 +126,32 @@ typedef struct
} THREATTYPE;
// define for bAimTime for bursting
#define BURSTING 20
#define AUTOFIRING 21
//#define BURSTING 20
//#define AUTOFIRING 21
typedef struct
//dnl ch69 140913
#define dnlCALCBESTSHOT// old CalcBestShot simply became inadequate and buggier for so many add ons to game, so this one is alternative option which calculate all turning, stance and raising gun cost for different stances and all available scopes
class ATTACKTYPE
{
UINT8 ubPossible; // is this attack form possible? T/F
UINT8 ubOpponent; // which soldier is the victim?
//CHANGED STRUCTURE VALUE BY GOTTHARD 7/14/07
INT16 ubAimTime; // how many extra APs to spend on aiming
INT16 ubChanceToReallyHit; // chance to hit * chance to get through cover
//END STRUCTURE CHANGE BY GOTTHARD 7/14/07
INT32 iAttackValue; // relative worthiness of this type of attack
INT32 sTarget; // target gridno of this attack
INT8 bTargetLevel; // target level of this attack
UINT8 ubFriendlyFireChance; // highest chance to hit someone of our guys //dnl ch61 180813
INT16 ubAPCost; // how many APs the attack will use up
INT8 bWeaponIn; // the inv slot of the weapon in question
INT8 bScopeMode; // SANDRO: added for scope mode and alternative weapon holding
} ATTACKTYPE;
public:
UINT8 ubPossible; // is this attack form possible
UINT8 ubOpponent; // which soldier is the victim
//CHANGED STRUCTURE VALUE BY GOTTHARD 7/14/07
INT16 ubAimTime; // how many extra APs to spend on aiming
INT16 ubChanceToReallyHit; // chance to hit * chance to get through cover
//END STRUCTURE CHANGE BY GOTTHARD 7/14/07
INT16 ubAPCost; // how many APs the attack will use up
INT32 iAttackValue; // relative worthiness of this type of attack
INT32 sTarget; // target gridno of this attack
INT8 bTargetLevel; // target level of this attack
INT8 bWeaponIn; // the inv slot of the weapon in question
INT8 bScopeMode; // SANDRO: added for scope mode and alternative weapon holding
UINT8 ubStance; // recommended stance for attack
UINT8 ubFriendlyFireChance; // highest chance to hit someone of our guys //dnl ch61 180813
ATTACKTYPE(void){ InitAttackType(this); }
void InitAttackType(ATTACKTYPE *pAttack);
};
//dnl ch61 180813
#define MAXUNDERFIRE 100
@@ -239,8 +246,6 @@ BOOLEAN InGas( SOLDIERTYPE *pSoldier, INT32 sGridNo );
BOOLEAN InGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo );
BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo );
void InitAttackType(ATTACKTYPE *pAttack);
INT32 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, INT16 bReserveAPs, INT8 bAction, INT8 fFlags );
int LegalNPCDestination(SOLDIERTYPE *pSoldier, INT32 sGridno, UINT8 ubPathMode, UINT8 ubWaterOK, UINT8 fFlags);
+6 -8
View File
@@ -2353,16 +2353,14 @@ void CheckForChangingOrders(SOLDIERTYPE *pSoldier)
}
}
void InitAttackType(ATTACKTYPE *pAttack)
void ATTACKTYPE::InitAttackType(ATTACKTYPE *pAttack)//dnl ch69 140913
{
// initialize the given bestAttack structure fields to their default values
pAttack->ubPossible = FALSE;
pAttack->ubOpponent = NOBODY;
pAttack->ubAimTime = 0;
pAttack->ubChanceToReallyHit = 0;
pAttack->sTarget = NOWHERE;
pAttack->iAttackValue = 0;
pAttack->ubAPCost = 0;
memset(pAttack, 0, sizeof(ATTACKTYPE));
pAttack->ubOpponent = NOBODY;
pAttack->sTarget = NOWHERE;
pAttack->bWeaponIn = NO_SLOT;
pAttack->ubStance = STANDING;
}
void HandleInitialRedAlert( INT8 bTeam, UINT8 ubCommunicate)
+179 -27
View File
@@ -23,6 +23,7 @@
#include "Sound Control.h"
#include "message.h"
#include "Vehicles.h"
#include "Soldier Functions.h"//dnl ch69 140913
#endif
extern INT16 DirIncrementer[8];
@@ -156,28 +157,32 @@ void ResetWeaponMode( SOLDIERTYPE * pSoldier )
void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUnseen)
{
UINT32 uiLoop;
INT32 iAttackValue;
INT32 iThreatValue;
INT32 iHitRate,iBestHitRate,iPercentBetter;
INT32 iEstDamage;
UINT8 ubMaxPossibleAimTime;
INT16 ubAimTime,ubMinAPcost,ubRawAPCost;
UINT8 ubChanceToReallyHit = 0;
INT16 ubChanceToHit,ubChanceToHit2,ubBestAimTime,ubChanceToGetThrough,ubBestChanceToHit;
SOLDIERTYPE *pOpponent;
//INT16 ubBurstAPs;//dnl ch64 270813
INT32 iAttackValue, iThreatValue, iHitRate, iBestHitRate, iPercentBetter, iEstDamage, iTrueLastTarget;
UINT16 usTrueState, usTurningCost, usRaiseGunCost;
INT16 ubAimTime, ubMinAPcost, ubRawAPCost, sBestAPcost, ubChanceToHit, ubBestAimTime, ubChanceToGetThrough, ubBestChanceToHit, sStanceAPcost;
BOOLEAN fAddingTurningCost, fAddingRaiseGunCost;
UINT8 ubMaxPossibleAimTime, ubStance, ubBestStance, ubChanceToReallyHit;
INT8 bScopeMode;
SOLDIERTYPE *pOpponent;
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"CalcBestShot");
ubBestChanceToHit = ubBestAimTime = ubChanceToHit = 0;
ubBestChanceToHit = ubBestAimTime = ubChanceToHit = ubChanceToReallyHit = 0;
pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem;
pSoldier->bWeaponMode = WM_NORMAL;
#ifdef dnlCALCBESTSHOT//dnl ch69 130913 rather setup available scopes here then in later inside loop
std::map<INT8, OBJECTTYPE*> ObjList;
GetScopeLists(&pSoldier->inv[HANDPOS], ObjList);
pSoldier->bScopeMode = USE_BEST_SCOPE;
pSoldier->bDoBurst = 0;
pSoldier->bDoAutofire = 0;
#else
INT16 ubBurstAPs, ubChanceToHit2;
#endif
//ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints( ), &(pSoldier->inv[HANDPOS]), pSoldier );//dnl ch64 270813
InitAttackType(pBestShot); // set all structure fields to defaults
//InitAttackType(pBestShot); // set all structure fields to defaults//dnl ch69 150913 already initialize from class constructor
// hang a pointer into active soldier's personal opponent list
//pbPersOL = &(pSoldier->aiData.bOppList[0]);
@@ -218,7 +223,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
#ifdef DEBUGATTACKS
DebugAI( String( "%s sees %s at gridno %d\n",pSoldier->GetName(),ExtMen[pOpponent->ubID].GetName(),pOpponent->sGridNo ) );
#endif
#ifndef dnlCALCBESTSHOT//dnl ch69 140913
// calculate minimum action points required to shoot at this opponent
// if ( !Weapon[pSoldier->usAttackingWeapon].NoSemiAuto )
// SANDRO - calculate this with the alternative mode, as it is faster, decide the actual bScopeMode later
@@ -235,7 +240,9 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
// ubMinAPcost = CalcAPsToAutofire( pSoldier->CalcActionPoints( ), &(pSoldier->inv[HANDPOS]), 3 );
//NumMessage("MinAPcost to shoot this opponent = ",ubMinAPcost);
#else
ubMinAPcost = MinAPsToAttack(pSoldier, pOpponent->sGridNo, DONTADDTURNCOST, 0);// later will be decide if shoot is possible this here is just best guess so ignore turnover
#endif
// if we don't have enough APs left to shoot even a snap-shot at this guy
if (ubMinAPcost > pSoldier->bActionPoints)
continue; // next opponent
@@ -298,7 +305,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
//}
iBestHitRate = 0; // reset best hit rate to minimum
#ifndef dnlCALCBESTSHOT//dnl ch69 130913 although there is nothing wrong with code unfortunately below and later in DecideAction is not use properly with missing conditions so AI get invalid action handling due to improper APs calculation, also try to optimize a bit to get less AICalcChanceToHitGun calls and to use with different stance decisions
// SANDRO: decide here, whether to use the alternative holding or normal holding
bScopeMode = USE_BEST_SCOPE;
if ( gGameExternalOptions.ubAllowAlternativeWeaponHolding )
@@ -437,7 +444,144 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
ubBestAimTime = ubAimTime;
ubBestChanceToHit = ubChanceToHit;
}
#else
//dnl ch69 130913 Hoping to optimize
// consider alternate holding mode and different scopes
for(pSoldier->bScopeMode=(gGameExternalOptions.ubAllowAlternativeWeaponHolding?USE_ALT_WEAPON_HOLD:USE_BEST_SCOPE); pSoldier->bScopeMode<=(gGameExternalOptions.fScopeModes?NUM_SCOPE_MODES-1:USE_BEST_SCOPE); pSoldier->bScopeMode++)
{
if(pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || (pSoldier->bScopeMode >= USE_BEST_SCOPE && ObjList[pSoldier->bScopeMode] != NULL))
{
usTrueState = pSoldier->usAnimState;// because is used in CalculateRaiseGunCost, CalcAimingLevelsAvailableWithAP, CalculateTurningCost
iTrueLastTarget = pSoldier->sLastTarget;// because is used in MinAPsToShootOrStab
ubStance = ANIM_STAND;
if(IsValidStance(pSoldier, ubStance))
{
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
if(sStanceAPcost)// Going up so first is stance change then turnover, do animation change before APs calculation
{
pSoldier->usAnimState = STANDING;
pSoldier->sLastTarget = NOWHERE;
}
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
//ubMinAPcost = MinAPsToAttack(pSoldier, pOpponent->sGridNo, ADDTURNCOST, 0) + sStanceAPcost;
if(pSoldier->bActionPoints-ubMinAPcost >= 0)
{
// calc next attack's minimum shooting cost (excludes readying & turning & raise gun)
//ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
for(ubAimTime=APBPConstants[AP_MIN_AIM_ATTACK]; ubAimTime<=ubMaxPossibleAimTime; ubAimTime++)
{
ubChanceToHit = AICalcChanceToHitGun(pSoldier, pOpponent->sGridNo, ubAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, STANDING);
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
//SendFmtMsg("CalcBestShot_S=%d hr=%d at=%d sm=%d op=%d/%d aps=%d,%d,%d,%d,%d", ubChanceToHit, iHitRate, ubAimTime, pSoldier->bScopeMode, pOpponent->ubID, pOpponent->sGridNo, ubMinAPcost, ubRawAPCost, sStanceAPcost, usTurningCost, usRaiseGunCost);
if(iHitRate > iBestHitRate)
{
iBestHitRate = iHitRate;
ubBestAimTime = ubAimTime;
ubBestChanceToHit = ubChanceToHit;
bScopeMode = pSoldier->bScopeMode;
sBestAPcost = ubMinAPcost;
ubBestStance = ubStance;
}
}
}
pSoldier->usAnimState = usTrueState;
pSoldier->sLastTarget = iTrueLastTarget;
}
ubStance = ANIM_CROUCH;
if(IsValidStance(pSoldier, ubStance) && pSoldier->bScopeMode != USE_ALT_WEAPON_HOLD)
{
usTurningCost = 32767;
if(gAnimControl[pSoldier->usAnimState].ubEndHeight > ubStance)// Going down
{
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
}
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
if(sStanceAPcost)
{
pSoldier->usAnimState = CROUCHING;
pSoldier->sLastTarget = NOWHERE;
fAddingRaiseGunCost = TRUE;
}
if(usTurningCost == 32767)// Going up or same
{
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
}
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
//ubMinAPcost = MinAPsToAttack(pSoldier, pOpponent->sGridNo, ADDTURNCOST, 0) + sStanceAPcost;
if(pSoldier->bActionPoints-ubMinAPcost >= 0)
{
//ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
for(ubAimTime=APBPConstants[AP_MIN_AIM_ATTACK]; ubAimTime<=ubMaxPossibleAimTime; ubAimTime++)
{
ubChanceToHit = AICalcChanceToHitGun(pSoldier, pOpponent->sGridNo, ubAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, CROUCHING);
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
//SendFmtMsg("CalcBestShot_C=%d hr=%d at=%d sm=%d op=%d/%d aps=%d,%d,%d,%d,%d", ubChanceToHit, iHitRate, ubAimTime, pSoldier->bScopeMode, pOpponent->ubID, pOpponent->sGridNo, ubMinAPcost, ubRawAPCost, sStanceAPcost, usTurningCost, usRaiseGunCost);
if(iHitRate > iBestHitRate)
{
iBestHitRate = iHitRate;
ubBestAimTime = ubAimTime;
ubBestChanceToHit = ubChanceToHit;
bScopeMode = pSoldier->bScopeMode;
sBestAPcost = ubMinAPcost;
ubBestStance = ubStance;
}
}
}
pSoldier->usAnimState = usTrueState;
pSoldier->sLastTarget = iTrueLastTarget;
}
ubStance = ANIM_PRONE;
if(IsValidStance(pSoldier, ubStance) && pSoldier->bScopeMode != USE_ALT_WEAPON_HOLD)
{
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
if(sStanceAPcost)// Going down so first is turnover then change stance, do APs calculation before animation change
{
pSoldier->usAnimState = PRONE;
pSoldier->sLastTarget = NOWHERE;
fAddingRaiseGunCost = TRUE;
}
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
//ubMinAPcost = MinAPsToAttack(pSoldier, pOpponent->sGridNo, ADDTURNCOST, 0) + sStanceAPcost;
if(pSoldier->bActionPoints-ubMinAPcost >= 0)
{
//ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
for(ubAimTime=APBPConstants[AP_MIN_AIM_ATTACK]; ubAimTime<=ubMaxPossibleAimTime; ubAimTime++)
{
ubChanceToHit = AICalcChanceToHitGun(pSoldier, pOpponent->sGridNo, ubAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, PRONE);
iHitRate = ((pSoldier->bActionPoints - (ubMinAPcost - ubRawAPCost)) * ubChanceToHit) / (ubRawAPCost + ubAimTime * APBPConstants[AP_CLICK_AIM]);
//SendFmtMsg("CalcBestShot_P=%d hr=%d at=%d sm=%d op=%d/%d aps=%d,%d,%d,%d,%d", ubChanceToHit, iHitRate, ubAimTime, pSoldier->bScopeMode, pOpponent->ubID, pOpponent->sGridNo, ubMinAPcost, ubRawAPCost, sStanceAPcost, usTurningCost, usRaiseGunCost);
if(iHitRate > iBestHitRate)
{
iBestHitRate = iHitRate;
ubBestAimTime = ubAimTime;
ubBestChanceToHit = ubChanceToHit;
bScopeMode = pSoldier->bScopeMode;
sBestAPcost = ubMinAPcost;
ubBestStance = ubStance;
}
}
}
pSoldier->usAnimState = usTrueState;
pSoldier->sLastTarget = iTrueLastTarget;
}
}
}
#endif
// if we can't get any kind of hit rate at all
if (iBestHitRate == 0)
continue; // next opponent
@@ -513,18 +657,24 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot, BOOLEAN shootUns
pBestShot->ubPossible = TRUE;
pBestShot->ubOpponent = pOpponent->ubID;
pBestShot->ubAimTime = ubBestAimTime;
pBestShot->ubChanceToReallyHit = ubChanceToReallyHit;
pBestShot->sTarget = pOpponent->sGridNo;
pBestShot->bTargetLevel = pOpponent->pathing.bLevel;
pBestShot->iAttackValue = iAttackValue;
pBestShot->ubAPCost = ubMinAPcost;
pBestShot->bScopeMode = bScopeMode;
pBestShot->ubChanceToReallyHit = ubChanceToReallyHit;
pBestShot->sTarget = pOpponent->sGridNo;
pBestShot->bTargetLevel = pOpponent->pathing.bLevel;
pBestShot->iAttackValue = iAttackValue;
#ifndef dnlCALCBESTSHOT//dnl ch69 140913
pBestShot->ubAPCost = ubMinAPcost;
#else
pBestShot->ubAPCost = sBestAPcost;
pBestShot->ubStance = ubBestStance;
#endif
pBestShot->bScopeMode = bScopeMode;
if(gUnderFire.Count(pSoldier->bTeam))//dnl ch61 180813
pBestShot->ubFriendlyFireChance = gUnderFire.Chance(pSoldier->bTeam);
else
pBestShot->ubFriendlyFireChance = 0;
}
}
//if(pBestShot->ubPossible)SendFmtMsg("CalcBestShot;\r\n ID=%d Loc=%d APs=%d Ac=%d AcData=%d Al=%d, SM=%d, LAc=%d, NAc=%d AT=%d\r\n AP?=%d,%d,%d/%d BS=%d", pSoldier->ubID, pSoldier->sGridNo, pSoldier->bActionPoints, pSoldier->aiData.bAction, pSoldier->aiData.usActionData, pSoldier->aiData.bAlertStatus, pBestShot->bScopeMode, pSoldier->aiData.bLastAction, pSoldier->aiData.bNextAction, pBestShot->ubAimTime, pBestShot->ubAPCost, CalcAPCostForAiming(pSoldier, pBestShot->sTarget, (INT8)pBestShot->ubAimTime), CalcTotalAPsToAttack(pSoldier, pBestShot->sTarget, TRUE, pBestShot->ubAimTime), CalcTotalAPsToAttack(pSoldier, pBestShot->sTarget, FALSE, pBestShot->ubAimTime), pBestShot->ubStance);
pSoldier->bScopeMode = USE_BEST_SCOPE; // better reset this back
}
@@ -942,7 +1092,7 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
}
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: about to initattacktype");
InitAttackType(pBestThrow); // set all structure fields to defaults
//InitAttackType(pBestThrow); // set all structure fields to defaults//dnl ch69 150913
// look at the squares near each known opponent and try to find the one
// place where a tossed projectile would do the most harm to the opponents
@@ -1230,6 +1380,7 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
{
ubRawAPCost = MinAPsToShootOrStab( pSoldier, sGridNo,ubMaxPossibleAimTime,FALSE);
ubChanceToHit = (UINT8) AICalcChanceToHitGun(pSoldier, sGridNo, ubMaxPossibleAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, STANDING);//dnl ch59 130813
//SendFmtMsg("CalcBestThrow=%d APs=%d mat=%d gno=%d EXPGUN!!!", ubChanceToHit, ubRawAPCost, ubMaxPossibleAimTime, sGridNo);
}
else
{
@@ -1239,6 +1390,7 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
DebugMsg(TOPIC_JA2 , DBG_LEVEL_3 , String("Raw AP Cost = %d",ubRawAPCost ));
ubChanceToHit = (UINT8) CalcThrownChanceToHit( pSoldier, sGridNo, ubMaxPossibleAimTime, AIM_SHOT_TORSO );
DebugMsg(TOPIC_JA2 , DBG_LEVEL_3 , String("Chance to hit = %d",ubChanceToHit ));
//SendFmtMsg("CalcBestThrow=%d APs=%d mat=%d gno=%d", ubChanceToHit, ubRawAPCost, ubMaxPossibleAimTime, sGridNo);
}
// mortars are inherently quite inaccurate, don't get proximity bonus
@@ -1292,7 +1444,7 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
pBestThrow->ubChanceToReallyHit = ubChanceToReallyHit;
pBestThrow->sTarget = sGridNo;
pBestThrow->iAttackValue = iAttackValue;
pBestThrow->ubAPCost = ubMinAPcost + CalcAPCostForAiming(pSoldier, sGridNo, ubMaxPossibleAimTime, FALSE);//dnl ch64 310813
pBestThrow->ubAPCost = ubMinAPcost + CalcAPCostForAiming(pSoldier, sGridNo, ubMaxPossibleAimTime);//dnl ch64 310813
pBestThrow->bTargetLevel = bOpponentLevel[ubLoop];
//sprintf(tempstr,"new best THROW AttackValue = %d at grid #%d",iAttackValue/100000,gridno);
@@ -1341,7 +1493,7 @@ void CalcBestStab(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab, BOOLEAN fBladeAt
SOLDIERTYPE *pOpponent;
UINT16 usTrueMovementMode;
INT16 ubBestChanceToHit;
InitAttackType(pBestStab); // set all structure fields to defaults
//InitAttackType(pBestStab); // set all structure fields to defaults//dnl ch69 150913
pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem;
@@ -1562,7 +1714,7 @@ void CalcTentacleAttack(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab )
SOLDIERTYPE *pOpponent;
InitAttackType(pBestStab); // set all structure fields to defaults
//InitAttackType(pBestStab); // set all structure fields to defaults//dnl ch69 150913
pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem;
+1 -1
View File
@@ -916,7 +916,7 @@ INT8 CreatureDecideActionBlack( SOLDIERTYPE * pSoldier )
INT8 bCanAttack;
INT8 bSpitIn, bWeaponIn;
UINT32 uiChance;
ATTACKTYPE BestShot = {}, BestStab = {}, BestAttack = {}, CurrStab = {};
ATTACKTYPE BestShot, BestStab, BestAttack, CurrStab;//dnl ch69 150913
BOOLEAN fRunAway = FALSE;
BOOLEAN fChangeLevel;
+71 -22
View File
@@ -4014,7 +4014,7 @@ INT16 ubMinAPCost;
return( ZombieDecideActionBlack(pSoldier) );
#endif
ATTACKTYPE BestShot = {},BestThrow = {},BestStab = {},BestAttack = {};
ATTACKTYPE BestShot, BestThrow, BestStab ,BestAttack;//dnl ch69 150913
BOOLEAN fCivilian = (PTR_CIVILIAN && (pSoldier->ubCivilianGroup == NON_CIV_GROUP || pSoldier->aiData.bNeutral || (pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) );
UINT8 ubBestStance = 1, ubStanceCost;
BOOLEAN fChangeStanceFirst; // before firing
@@ -4849,7 +4849,7 @@ INT16 ubMinAPCost;
}
// NB a desire of 4 or more is only achievable by brave/aggressive guys with high morale
UINT16 usRange = GetModifiedGunRange(pSoldier->inv[ BestAttack.bWeaponIn ].usItem);
UINT16 usRange = BestAttack.bWeaponIn==NO_SLOT ? 0 : GetModifiedGunRange(pSoldier->inv[BestAttack.bWeaponIn].usItem);//dnl ch69 150913
if ( (pSoldier->bActionPoints == pSoldier->bInitialActionPoints) &&
(ubBestAttackAction == AI_ACTION_FIRE_GUN) &&
@@ -5001,6 +5001,7 @@ INT16 ubMinAPCost;
if (ubBestAttackAction == AI_ACTION_FIRE_GUN)
{
#ifndef dnlCALCBESTSHOT//dnl ch69 140913
// Do we need to change stance? NB We'll have to ready our gun again
if ( !TANK( pSoldier ) && ( pSoldier->bActionPoints >= BestAttack.ubAPCost + GetAPsCrouch( pSoldier, TRUE) + MinAPsToAttack( pSoldier, BestAttack.sTarget, ADDTURNCOST,0, 1 ) ) )
{
@@ -5076,6 +5077,44 @@ INT16 ubMinAPCost;
}
}
}
#else
if(!TANK(pSoldier))
{
// first get the direction, as we will need to pass that in to ShootingStanceChange
bDirection = atan8(CenterX(pSoldier->sGridNo),CenterY(pSoldier->sGridNo),CenterX(BestAttack.sTarget),CenterY(BestAttack.sTarget));
if(gAnimControl[pSoldier->usAnimState].ubEndHeight > BestAttack.ubStance)// Going down
{
// First change direction
if(pSoldier->ubDirection != bDirection && pSoldier->InternalIsValidStance(bDirection, gAnimControl[pSoldier->usAnimState].ubEndHeight))
{
// we're not facing towards him, so turn first!
pSoldier->aiData.usActionData = bDirection;
return(AI_ACTION_CHANGE_FACING);
}
else
{
ubBestStance = BestAttack.ubStance;
fChangeStanceFirst = TRUE;
}
}
else if(gAnimControl[pSoldier->usAnimState].ubEndHeight < BestAttack.ubStance)// Going up
{
// First change stance
ubBestStance = BestAttack.ubStance;
fChangeStanceFirst = TRUE;
}
else
{
// Change facing
if(pSoldier->ubDirection != bDirection && pSoldier->InternalIsValidStance(bDirection, gAnimControl[pSoldier->usAnimState].ubEndHeight))
{
// we're not facing towards him, so turn first!
pSoldier->aiData.usActionData = bDirection;
return(AI_ACTION_CHANGE_FACING);
}
}
}
#endif
//////////////////////////////////////////////////////////////////////////
// IF ENOUGH APs TO BURST, RANDOM CHANCE OF DOING SO
@@ -5143,14 +5182,13 @@ INT16 ubMinAPCost;
if ( (INT32) PreRandom( 100 ) < iChance)
{
BestAttack.ubAPCost = BestAttack.ubAPCost + ubBurstAPs;
BestAttack.ubAPCost += ubBurstAPs + sActualAimTime;//dnl??? ch58 130913
// check for spread burst possibilities
if (pSoldier->aiData.bAttitude != ATTACKSLAYONLY)
{
CalcSpreadBurst( pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel );
}
//dnl ch58 180813 After HAM 4 BURSTING is not in use any more, for burst bAimTime and bDoAutofire must be set to 0
pSoldier->aiData.bAimTime = 0;
//dnl ch58 130913 return aiming for burst
pSoldier->bDoBurst = 1;
pSoldier->bDoAutofire = 0;
}
@@ -5163,7 +5201,7 @@ INT16 ubMinAPCost;
!pSoldier->bDoBurst ) || Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto) )
{
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: ENOUGH APs TO AUTOFIRE, RANDOM CHANCE OF DOING SO");
L_NEWAIM:
FLOAT dTotalRecoil = 0.0f;
pSoldier->bDoAutofire = 0;
if(UsingNewCTHSystem() == true){
@@ -5182,15 +5220,21 @@ INT16 ubMinAPCost;
pSoldier->bDoAutofire++;
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
}
while( pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs && pSoldier->inv[ BestAttack.bWeaponIn ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && GetAutoPenalty(&pSoldier->inv[ BestAttack.bWeaponIn ], gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)*pSoldier->bDoAutofire <= 80);//dnl ch64 260813 pSoldier->ubAttackingHand is wrong because decision is to use BestAttack.bWeaponIn
while( pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs + sActualAimTime && pSoldier->inv[ BestAttack.bWeaponIn ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && GetAutoPenalty(&pSoldier->inv[ BestAttack.bWeaponIn ], gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)*pSoldier->bDoAutofire <= 80);//dnl ch64 130913 pSoldier->ubAttackingHand is wrong because decision is to use BestAttack.bWeaponIn, also missing sActualAimTime
}
pSoldier->bDoAutofire--;
if(!UsingNewCTHSystem() && pSoldier->bDoAutofire < 3 && pSoldier->aiData.bAimTime > 0 && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft >= 3)//dnl ch69 130913 let try increase autofire rate for aim cost
{
pSoldier->aiData.bAimTime--;
sActualAimTime = CalcAPCostForAiming(pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime);
goto L_NEWAIM;
}
if (pSoldier->bDoAutofire > 0)
{
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
if (pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs )
if (pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimTime + ubBurstAPs )
{
// Base chance of bursting is 25% if best shot was +0 aim, down to 8% at +4
if ( TANK( pSoldier ) )
@@ -5243,21 +5287,28 @@ INT16 ubMinAPCost;
if ((INT32) PreRandom( 100 ) < iChance || Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto)
{
//dnl ch58 180813 After HAM 4 AUTOFIRING and sActualAimTime is not used in burst or autofire mode calculations and pSoldier->aiData.bAimTime must be 0
pSoldier->aiData.bAimTime = 0;
//dnl ch69 140913 return aiming for autofire with halfautofire fix
pSoldier->bDoBurst = 1;
INT16 ubHalfBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &pSoldier->inv[BestAttack.bWeaponIn], 4, pSoldier);
if(Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto)
iChance = 35;
if(pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs) && PreRandom(100) < iChance)
INT16 ubHalfBurstAPs = 256;
if(pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft < 4)
iChance = 0;
else
{
ubHalfBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &pSoldier->inv[BestAttack.bWeaponIn], 4, pSoldier);
if(Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto)
iChance = 35;
}
if((INT32)PreRandom(100) < iChance && pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimTime))
{
// Try short autofire to enhance chance of hitting
pSoldier->bDoAutofire = 4;
BestAttack.ubAPCost += ubHalfBurstAPs;
BestAttack.ubAPCost += ubHalfBurstAPs + sActualAimTime;
//SendFmtMsg("HALF-Auto=%d ubAPCost=%d iChance=%d ubBurstAPs=%d,%d", pSoldier->bDoAutofire, BestAttack.ubAPCost, iChance, ubHalfBurstAPs, sActualAimTime);
}
else
{
BestAttack.ubAPCost += ubBurstAPs;
BestAttack.ubAPCost += ubBurstAPs + sActualAimTime;
//SendFmtMsg("FULL-Auto=%d ubAPCost=%d iChance=%d ubBurstAPs=%d,%d", pSoldier->bDoAutofire, BestAttack.ubAPCost, iChance, ubBurstAPs, sActualAimTime);
}
}
else
@@ -5485,8 +5536,7 @@ INT16 ubMinAPCost;
{
CalcSpreadBurst( pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel );
}
//dnl ch58 200813 After HAM 4 BURSTING is not in use any more, for burst bAimTime and bDoAutofire must be set to 0
pSoldier->aiData.bAimTime = 0;
//dnl ch58 140913 After HAM 4 BURSTING is not in use any more, for burst bDoAutofire must be set to 0
pSoldier->bDoBurst = 1;
pSoldier->bDoAutofire = 0;
}
@@ -7449,7 +7499,7 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
#endif
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: soldier = %d, orders = %d, attitude = %d",pSoldier->ubID,pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude));
ATTACKTYPE BestStab = {},BestAttack = {};
ATTACKTYPE BestStab, BestAttack;//dnl ch69 150913
UINT8 ubBestStance = 1;
BOOLEAN fChangeStanceFirst; // before firing
INT16 ubBurstAPs;
@@ -7765,7 +7815,7 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
}
// NB a desire of 4 or more is only achievable by brave/aggressive guys with high morale
UINT16 usRange = GetModifiedGunRange(pSoldier->inv[ BestAttack.bWeaponIn ].usItem);
//UINT16 usRange = GetModifiedGunRange(pSoldier->inv[ BestAttack.bWeaponIn ].usItem);//dnl ch69 150913 not in use
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER)");
//////////////////////////////////////////////////////////////////////////
@@ -7965,8 +8015,7 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
{
CalcSpreadBurst( pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel );
}
//dnl ch58 200813 After HAM 4 BURSTING is not in use any more, for burst bAimTime and bDoAutofire must be set to 0
pSoldier->aiData.bAimTime = 0;
//dnl ch58 140913 After HAM 4 BURSTING is not in use any more, for burst bDoAutofire must be set to 0
pSoldier->bDoBurst = 1;
pSoldier->bDoAutofire = 0;
}