mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
CalcBestThrow:
- for mortars and grenade launchers, first try to find non smoke grenade/shell - don't use mortar in building or underground - don't use grenade launcher underground - blinded soldier cannot use mortar/grenade launcher - when calculating safety margin, correctly use BuddyItem defined for single shot RPG - limit ubSafetyMargin to TACTICAL_RANGE / 2 in case radius set too high in XML - use flares only at night - use ValidOpponent() check to determine opponent - use new knowledge functions - allow attacking recently seen/heard opponents with mortar/grenade launcher - improved code to spare grenade, don't spare: non lethal grenades, if some friends killed, when under attack, STATIONART/SNIPER, when on the roof, when there are friends under attack nearby - increase possible distance from opponent when opponent in a building or when using gas grenade - improved check for gas/smoke when using gas/smoke grenade - don't use smoke grenade if there's already smoke nearby (it can spread to this tile later) - improved code for AP to attack calculation - when attacking enemy in a building with mortar, allow shooting on the roof above the enemy, hoping that roof will collapse - limit RPG use, unless can hit several enemies, shooting at tank or opponent is in a room - soldiers should use RPGs against tanks/jeeps more often - modify smoke attack value depending on range (less effective if too far from opponent) Red AI: - improved code to prepare throw attack - added code to change stance before throwing if needed Black AI: - simplified code to change stance before shooting, always change stance first if needed - improved code to prepare attack, added code to change stance before throwing if needed CalcBestShot: - always change stance first, then turn - don't allow going prone and turning at the same time New AI functions: - NightLight - DuskLight - InSmokeNearby - CountTeamUnderAttack - SectorCurfew - TeamPercentKilled - TeamHighPercentKilled - ArmyPercentKilled - ArmyPercentKilledTolerance - Knowledge - KnownLocation - KnownLevel - UsePersonalKnowledge - PersonalKnowledge - KnownPersonalLocation - KnownPersonalLevel - PublicKnowledge - KnownPublicLocation - KnownPublicLevel git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8791 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
+277
-1
@@ -45,7 +45,9 @@
|
||||
// InWaterOrGas - gas stuff
|
||||
// RoamingRange - point patrol stuff
|
||||
|
||||
extern UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady, BOOLEAN fHipStance );
|
||||
// sevenfm
|
||||
extern UINT16 PickSoldierReadyAnimation(SOLDIERTYPE *pSoldier, BOOLEAN fEndReady, BOOLEAN fHipStance);
|
||||
extern SECTOR_EXT_DATA SectorExternalData[256][4];
|
||||
|
||||
UINT8 Urgency[NUM_STATUS_STATES][NUM_MORALE_STATES] =
|
||||
{
|
||||
@@ -4067,6 +4069,34 @@ UINT8 CountFriendsBlack( SOLDIERTYPE *pSoldier, INT32 sClosestOpponent )
|
||||
return ubFriendCount;
|
||||
}
|
||||
|
||||
// count friends under fire or with shock
|
||||
UINT8 CountTeamUnderAttack(INT8 bTeam, INT32 sGridNo, INT16 sDistance)
|
||||
{
|
||||
SOLDIERTYPE * pFriend;
|
||||
UINT8 ubFriendCount = 0;
|
||||
|
||||
// safety check
|
||||
if (bTeam >= MAXTEAMS)
|
||||
return 0;
|
||||
|
||||
// Run through each friendly.
|
||||
for (UINT8 iCounter = gTacticalStatus.Team[bTeam].bFirstID; iCounter <= gTacticalStatus.Team[bTeam].bLastID; iCounter++)
|
||||
{
|
||||
pFriend = MercPtrs[iCounter];
|
||||
|
||||
if (pFriend &&
|
||||
pFriend->bActive &&
|
||||
pFriend->stats.bLife >= OKLIFE &&
|
||||
PythSpacesAway(sGridNo, pFriend->sGridNo) <= sDistance &&
|
||||
(pFriend->aiData.bUnderFire || pFriend->aiData.bShock > 0))
|
||||
{
|
||||
ubFriendCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return ubFriendCount;
|
||||
}
|
||||
|
||||
// check if we have a prone sight cover from known enemies at spot
|
||||
BOOLEAN ProneSightCoverAtSpot(SOLDIERTYPE *pSoldier, INT32 sSpot, BOOLEAN fUnlimited)
|
||||
{
|
||||
@@ -5310,6 +5340,41 @@ BOOLEAN InSmoke(INT32 sGridNo, INT8 bLevel)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN InSmokeNearby(INT32 sGridNo, INT8 bLevel)
|
||||
{
|
||||
if (TileIsOutOfBounds(sGridNo))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gpWorldLevelData[sGridNo].ubExtFlags[bLevel] & (MAPELEMENT_EXT_SMOKE))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UINT8 ubDirection;
|
||||
INT32 sTempGridNo;
|
||||
UINT8 ubMovementCost;
|
||||
|
||||
// check adjacent reachable tiles
|
||||
for (ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ubDirection++)
|
||||
{
|
||||
sTempGridNo = NewGridNo(sGridNo, DirectionInc(ubDirection));
|
||||
|
||||
if (sTempGridNo != sGridNo)
|
||||
{
|
||||
ubMovementCost = gubWorldMovementCosts[sTempGridNo][ubDirection][bLevel];
|
||||
|
||||
if (ubMovementCost < TRAVELCOST_BLOCKED &&
|
||||
gpWorldLevelData[sTempGridNo].ubExtFlags[bLevel] & (MAPELEMENT_EXT_SMOKE))
|
||||
{
|
||||
return(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN CorpseWarning(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel)
|
||||
{
|
||||
CHECKF(pSoldier);
|
||||
@@ -5505,3 +5570,214 @@ BOOLEAN AICheckShortWeaponRange(SOLDIERTYPE *pSoldier)
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN NightLight(void)
|
||||
{
|
||||
//if( GetTimeOfDayAmbientLightLevel() >= NORMAL_LIGHTLEVEL_DAY + 2 )
|
||||
if (gubEnvLightValue >= NORMAL_LIGHTLEVEL_NIGHT - 3)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN DuskLight(void)
|
||||
{
|
||||
// average between day and night
|
||||
if (gubEnvLightValue >= (NORMAL_LIGHTLEVEL_NIGHT + NORMAL_LIGHTLEVEL_DAY) / 2)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN UsePersonalKnowledge(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
INT8 bPersonalKnowledge;
|
||||
INT8 bPublicKnowledge;
|
||||
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bPersonalKnowledge = PersonalKnowledge(pSoldier, ubOpponentID);
|
||||
bPublicKnowledge = PublicKnowledge(pSoldier->bTeam, ubOpponentID);
|
||||
|
||||
if (gubKnowledgeValue[bPublicKnowledge - OLDEST_HEARD_VALUE][bPersonalKnowledge - OLDEST_HEARD_VALUE] > 0 ||
|
||||
bPersonalKnowledge != NOT_HEARD_OR_SEEN &&
|
||||
TileIsOutOfBounds(KnownPublicLocation(pSoldier->bTeam, ubOpponentID)) &&
|
||||
!TileIsOutOfBounds(KnownPersonalLocation(pSoldier, ubOpponentID)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INT8 Knowledge(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return NOT_HEARD_OR_SEEN;
|
||||
}
|
||||
|
||||
if (UsePersonalKnowledge(pSoldier, ubOpponentID))
|
||||
{
|
||||
return PersonalKnowledge(pSoldier, ubOpponentID);
|
||||
}
|
||||
|
||||
return PublicKnowledge(pSoldier->bTeam, ubOpponentID);
|
||||
}
|
||||
|
||||
INT32 KnownLocation(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return NOWHERE;
|
||||
}
|
||||
|
||||
if (UsePersonalKnowledge(pSoldier, ubOpponentID))
|
||||
{
|
||||
return KnownPersonalLocation(pSoldier, ubOpponentID);
|
||||
}
|
||||
|
||||
return KnownPublicLocation(pSoldier->bTeam, ubOpponentID);
|
||||
}
|
||||
|
||||
INT8 KnownLevel(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (UsePersonalKnowledge(pSoldier, ubOpponentID))
|
||||
{
|
||||
return KnownPersonalLevel(pSoldier, ubOpponentID);
|
||||
}
|
||||
|
||||
return KnownPublicLevel(pSoldier->bTeam, ubOpponentID);
|
||||
}
|
||||
|
||||
INT8 PersonalKnowledge(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return NOT_HEARD_OR_SEEN;
|
||||
}
|
||||
|
||||
return pSoldier->aiData.bOppList[ubOpponentID];
|
||||
}
|
||||
|
||||
INT32 KnownPersonalLocation(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return NOWHERE;
|
||||
}
|
||||
/*if(PersonalKnowledge(pSoldier, ubOpponentID) == NOT_HEARD_OR_SEEN)
|
||||
{
|
||||
return NOWHERE;
|
||||
}*/
|
||||
|
||||
return gsLastKnownOppLoc[pSoldier->ubID][ubOpponentID];
|
||||
}
|
||||
|
||||
INT8 KnownPersonalLevel(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID)
|
||||
{
|
||||
if (!pSoldier || ubOpponentID == NOBODY)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gbLastKnownOppLevel[pSoldier->ubID][ubOpponentID];
|
||||
}
|
||||
|
||||
INT8 PublicKnowledge(UINT8 bTeam, UINT8 ubOpponentID)
|
||||
{
|
||||
if (bTeam >= MAXTEAMS || ubOpponentID == NOBODY)
|
||||
{
|
||||
return NOT_HEARD_OR_SEEN;
|
||||
}
|
||||
|
||||
return gbPublicOpplist[bTeam][ubOpponentID];
|
||||
}
|
||||
|
||||
INT32 KnownPublicLocation(UINT8 bTeam, UINT8 ubOpponentID)
|
||||
{
|
||||
if (bTeam >= MAXTEAMS || ubOpponentID == NOBODY)
|
||||
{
|
||||
return NOWHERE;
|
||||
}
|
||||
/*if (PublicKnowledge(bTeam, ubOpponentID) == NOT_HEARD_OR_SEEN)
|
||||
{
|
||||
return NOWHERE;
|
||||
}*/
|
||||
|
||||
return gsPublicLastKnownOppLoc[bTeam][ubOpponentID];
|
||||
}
|
||||
|
||||
INT8 KnownPublicLevel(UINT8 bTeam, UINT8 ubOpponentID)
|
||||
{
|
||||
if (bTeam >= MAXTEAMS || ubOpponentID == NOBODY)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gbPublicLastKnownOppLevel[bTeam][ubOpponentID];
|
||||
}
|
||||
|
||||
UINT8 ArmyPercentKilled(void)
|
||||
{
|
||||
if (gTacticalStatus.Team[ENEMY_TEAM].bMenInSector + gTacticalStatus.ubArmyGuysKilled == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 100 * gTacticalStatus.ubArmyGuysKilled / (gTacticalStatus.Team[ENEMY_TEAM].bMenInSector + gTacticalStatus.ubArmyGuysKilled);
|
||||
}
|
||||
|
||||
UINT8 TeamPercentKilled(INT8 bTeam)
|
||||
{
|
||||
if (bTeam == ENEMY_TEAM)
|
||||
{
|
||||
return ArmyPercentKilled();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOLEAN TeamHighPercentKilled(INT8 bTeam)
|
||||
{
|
||||
if (bTeam == ENEMY_TEAM && ArmyPercentKilled() > ArmyPercentKilledTolerance())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// decide how many soldiers can be killed before alarm will be raised
|
||||
UINT8 ArmyPercentKilledTolerance(void)
|
||||
{
|
||||
// 50% at day, 25% at night, 25-33% for restricted sectors
|
||||
return 100 / (2 + SectorCurfew(TRUE));
|
||||
}
|
||||
|
||||
UINT8 SectorCurfew(BOOLEAN fNight)
|
||||
{
|
||||
UINT8 ubSectorId = SECTOR(gWorldSectorX, gWorldSectorY);
|
||||
UINT8 ubSectorData = 0;
|
||||
|
||||
ubSectorData = SectorExternalData[ubSectorId][gbWorldSectorZ].usCurfewValue;
|
||||
|
||||
if (fNight && NightLight()) // suspicious at night
|
||||
ubSectorData = max(ubSectorData, 1);
|
||||
|
||||
if (gbWorldSectorZ > 0) // underground we are always suspicious
|
||||
ubSectorData = max(ubSectorData, 2);
|
||||
|
||||
return ubSectorData;
|
||||
}
|
||||
|
||||
+346
-317
@@ -28,6 +28,10 @@
|
||||
#include "Town Militia.h" // added by Flugente
|
||||
#include "Queen Command.h" // added by Flugente
|
||||
#include "Explosion Control.h" // added by Flugente for GASMASK_MIN_STATUS
|
||||
// sevenfm
|
||||
#include "Isometric Utils.h"
|
||||
#include "Structure Wrap.h" // IsRoofPresentAtGridNo
|
||||
#include "Render Fun.h"
|
||||
#endif
|
||||
|
||||
// anv: for enemy taunts
|
||||
@@ -317,12 +321,15 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
{
|
||||
usTrueState = pSoldier->usAnimState;// because is used in CalculateRaiseGunCost, CalcAimingLevelsAvailableWithAP, CalculateTurningCost
|
||||
iTrueLastTarget = pSoldier->sLastTarget;// because is used in MinAPsToShootOrStab
|
||||
|
||||
// --------- Standing ---------
|
||||
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
|
||||
if(sStanceAPcost)
|
||||
{
|
||||
// Going up so first is stance change then turnover, do animation change before APs calculation
|
||||
pSoldier->usAnimState = STANDING;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
}
|
||||
@@ -360,29 +367,23 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pSoldier->usAnimState = usTrueState;
|
||||
pSoldier->sLastTarget = iTrueLastTarget;
|
||||
}
|
||||
|
||||
if ( pSoldier->bScopeMode == USE_ALT_WEAPON_HOLD || ARMED_VEHICLE( pSoldier ) || (Item[pSoldier->usAttackingWeapon].usItemClass & IC_THROWING_KNIFE) )
|
||||
continue;
|
||||
|
||||
// --------- Crouched ---------
|
||||
ubStance = ANIM_CROUCH;
|
||||
if(IsValidStance(pSoldier, ubStance))
|
||||
{
|
||||
usTurningCost = 32767;
|
||||
if(gAnimControl[pSoldier->usAnimState].ubEndHeight > ubStance)// Going down
|
||||
{
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
}
|
||||
// change stance then turn
|
||||
sStanceAPcost = GetAPsToChangeStance(pSoldier, ubStance);
|
||||
if(sStanceAPcost)
|
||||
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);
|
||||
}
|
||||
GetAPChargeForShootOrStabWRTGunRaises(pSoldier, pOpponent->sGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost, 0);
|
||||
usTurningCost = CalculateTurningCost(pSoldier, pSoldier->usAttackingWeapon, fAddingTurningCost);
|
||||
usRaiseGunCost = CalculateRaiseGunCost(pSoldier, fAddingRaiseGunCost, pOpponent->sGridNo, 0);
|
||||
if(fAddingTurningCost && fAddingRaiseGunCost)//dnl ch71 180913
|
||||
{
|
||||
@@ -393,6 +394,7 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
}
|
||||
ubRawAPCost = MinAPsToShootOrStab(pSoldier, pOpponent->sGridNo, 0, FALSE, 2);
|
||||
ubMinAPcost = ubRawAPCost + usTurningCost + sStanceAPcost + usRaiseGunCost;
|
||||
|
||||
if(pSoldier->bActionPoints-ubMinAPcost >= 0)
|
||||
{
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
|
||||
@@ -414,21 +416,31 @@ void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot)
|
||||
pSoldier->usAnimState = usTrueState;
|
||||
pSoldier->sLastTarget = iTrueLastTarget;
|
||||
}
|
||||
|
||||
// no prone stance if we have to change direction and stance at the same time
|
||||
if (pSoldier->ubDirection != AIDirection(pSoldier->sGridNo, pOpponent->sGridNo) &&
|
||||
gAnimControl[pSoldier->usAnimState].ubEndHeight > ANIM_PRONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// --------- Prone ---------
|
||||
ubStance = ANIM_PRONE;
|
||||
if(IsValidStance(pSoldier, ubStance))
|
||||
//if(IsValidStance(pSoldier, ubStance))
|
||||
if (pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, pOpponent->sGridNo), ubStance))
|
||||
{
|
||||
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
|
||||
if (sStanceAPcost)
|
||||
{
|
||||
pSoldier->usAnimState = PRONE;
|
||||
pSoldier->sLastTarget = NOWHERE;
|
||||
fAddingRaiseGunCost = TRUE;
|
||||
}
|
||||
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;
|
||||
|
||||
if(pSoldier->bActionPoints-ubMinAPcost >= 0)
|
||||
{
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, pOpponent->sGridNo, pSoldier->bActionPoints-ubMinAPcost);
|
||||
@@ -649,34 +661,39 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow");
|
||||
// September 9, 1998: added code for LAWs (CJC)
|
||||
UINT8 ubLoop, ubLoop2;
|
||||
INT32 iAttackValue;
|
||||
INT32 iHitRate, iThreatValue, iTotalThreatValue,iOppThreatValue[MAXMERCS];
|
||||
INT32 sGridNo, sEndGridNo, sFriendTile[MAXMERCS], sOpponentTile[MAXMERCS];
|
||||
UINT8 ubLoop, ubLoop2;
|
||||
INT32 iAttackValue;
|
||||
INT32 iHitRate, iThreatValue, iTotalThreatValue,iOppThreatValue[MAXMERCS];
|
||||
INT32 sGridNo, sEndGridNo, sFriendTile[MAXMERCS], sOpponentTile[MAXMERCS];
|
||||
INT8 bFriendLevel[MAXMERCS], bOpponentLevel[MAXMERCS];
|
||||
INT32 iEstDamage;
|
||||
UINT8 ubFriendCnt = 0,ubOpponentCnt = 0, ubOpponentID[MAXMERCS];
|
||||
UINT8 ubMaxPossibleAimTime;
|
||||
INT16 ubRawAPCost,ubMinAPcost;
|
||||
UINT8 ubChanceToHit,ubChanceToGetThrough,ubChanceToReallyHit;
|
||||
UINT32 uiPenalty;
|
||||
UINT8 ubSearchRange;
|
||||
UINT16 usOppDist;
|
||||
BOOLEAN fFriendsNearby;
|
||||
UINT16 usInHand, usGrenade;
|
||||
UINT8 ubOppsInRange, ubOppsAdjacent;
|
||||
BOOLEAN fSkipLocation;
|
||||
INT32 iEstDamage;
|
||||
UINT8 ubFriendCnt = 0,ubOpponentCnt = 0, ubOpponentID[MAXMERCS];
|
||||
UINT8 ubMaxPossibleAimTime;
|
||||
INT16 sRawAPCost, sMinAPcost;
|
||||
UINT8 ubChanceToHit,ubChanceToGetThrough,ubChanceToReallyHit;
|
||||
UINT32 uiPenalty;
|
||||
UINT8 ubSearchRange;
|
||||
UINT16 usOppDist;
|
||||
BOOLEAN fFriendsNearby;
|
||||
UINT16 usInHand, usGrenade;
|
||||
UINT8 ubOppsInRange, ubOppsAdjacent;
|
||||
BOOLEAN fSkipLocation;
|
||||
INT8 bPayloadPocket;
|
||||
INT8 bMaxLeft,bMaxRight,bMaxUp,bMaxDown,bXOffset,bYOffset;
|
||||
INT8 bPersOL, bPublOL;
|
||||
INT8 bPersonalKnowledge, bPublicKnowledge, bKnowledge;
|
||||
SOLDIERTYPE *pOpponent, *pFriend;
|
||||
static INT16 sExcludeTile[100]; // This array is for storing tiles that we have
|
||||
UINT8 ubNumExcludedTiles = 0; // already considered, to prevent duplication of effort
|
||||
UINT8 ubNumExcludedTiles = 0; // already considered, to prevent duplication of effort
|
||||
INT32 iTossRange;
|
||||
UINT8 ubSafetyMargin = 0;
|
||||
UINT8 ubDiff;
|
||||
INT8 bEndLevel;
|
||||
OBJECTTYPE *pObjGL = NULL;//dnl ch63 240813
|
||||
BOOLEAN fHandGrenade = FALSE;
|
||||
BOOLEAN fMortar = FALSE;
|
||||
BOOLEAN fCannon = FALSE;
|
||||
BOOLEAN fGrenadeLauncher = FALSE;
|
||||
BOOLEAN fRocketLauncher = FALSE;
|
||||
|
||||
usInHand = pSoldier->inv[HANDPOS].usItem;
|
||||
usGrenade = NOTHING;
|
||||
@@ -691,34 +708,42 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
usInHand = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS]);
|
||||
}
|
||||
|
||||
if ( EXPLOSIVE_GUN( usInHand ) )
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"explosive gun");
|
||||
iTossRange = GetModifiedGunRange(usInHand) / CELL_X_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"about to calcmaxtossrange");
|
||||
iTossRange = CalcMaxTossRange( pSoldier, usInHand, TRUE );
|
||||
}
|
||||
|
||||
// if he's got a MORTAR in his hand, make sure he has a MORTARSHELL avail.
|
||||
if (Item[usInHand].mortar )
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: buddy's got a mortar");
|
||||
// bPayloadPocket = FindObj( pSoldier, MORTAR_SHELL );
|
||||
bPayloadPocket = FindLaunchable ( pSoldier, usInHand );
|
||||
bPayloadPocket = FindNonSmokeLaunchable(pSoldier, usInHand);
|
||||
if (bPayloadPocket == NO_SLOT)
|
||||
{
|
||||
bPayloadPocket = FindLaunchable(pSoldier, usInHand);
|
||||
}
|
||||
if (bPayloadPocket == NO_SLOT)
|
||||
{
|
||||
return; // no shells, can't fire the MORTAR
|
||||
}
|
||||
ubSafetyMargin = (UINT8)Explosive[ Item[ pSoldier->inv[bPayloadPocket].usItem ].ubClassIndex ].ubRadius;
|
||||
// sevenfm: don't use mortar in building or underground
|
||||
if (IsRoofPresentAtGridNo(pSoldier->sGridNo) && pSoldier->pathing.bLevel == 0 || gfCaves || gfBasement)
|
||||
return;
|
||||
|
||||
if (pSoldier->bBlindedCounter > 0)
|
||||
return;
|
||||
|
||||
ubSafetyMargin = (UINT8)Explosive[Item[pSoldier->inv[bPayloadPocket].usItem].ubClassIndex].ubRadius;
|
||||
fMortar = TRUE;
|
||||
}
|
||||
// if he's got a GL in his hand, make sure he has some type of GRENADE avail.
|
||||
else if (Item[usInHand].grenadelauncher )
|
||||
{
|
||||
// use up pocket 2 first, they get left as drop items
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: buddy's got a GL");
|
||||
|
||||
// sevenfm: don't use grenade launcher underground
|
||||
if (gfCaves || gfBasement)
|
||||
return;
|
||||
|
||||
if (pSoldier->bBlindedCounter > 0)
|
||||
return;
|
||||
|
||||
//dnl ch63 240813 Check if grenade is already attach or find one in pockets
|
||||
bPayloadPocket = HANDPOS;
|
||||
pObjGL = FindAttachment_GrenadeLauncher(&pSoldier->inv[bPayloadPocket]);
|
||||
@@ -734,43 +759,66 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
usGrenade = pSoldier->inv[bPayloadPocket].usItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
fGrenadeLauncher = TRUE;
|
||||
}
|
||||
else if ( Item[usInHand].rocketlauncher )
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: buddy's got a rocket launcher");
|
||||
|
||||
if (pSoldier->bBlindedCounter > 0)
|
||||
return;
|
||||
|
||||
// put in hand
|
||||
bPayloadPocket = HANDPOS;//dnl ch63 240813
|
||||
if ( Item[usInHand].singleshotrocketlauncher )
|
||||
// as C1
|
||||
ubSafetyMargin = (UINT8)Explosive[ Item[ C1 ].ubClassIndex ].ubRadius;
|
||||
if (Item[usInHand].singleshotrocketlauncher)
|
||||
{
|
||||
// sevenfm: for single shot rocket launchers, use buddy item instead
|
||||
if (Item[usInHand].usBuddyItem && Item[Item[usInHand].usBuddyItem].usItemClass & IC_EXPLOSV)
|
||||
{
|
||||
usGrenade = Item[usInHand].usBuddyItem;
|
||||
ubSafetyMargin = (UINT8)Explosive[Item[Item[usInHand].usBuddyItem].ubClassIndex].ubRadius;
|
||||
}
|
||||
else
|
||||
{
|
||||
// as C1
|
||||
usGrenade = C1;
|
||||
ubSafetyMargin = (UINT8)Explosive[Item[C1].ubClassIndex].ubRadius;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bPayloadPocket = FindLaunchable ( pSoldier, usInHand );
|
||||
bPayloadPocket = FindNonSmokeLaunchable(pSoldier, usInHand);
|
||||
if (bPayloadPocket == NO_SLOT)
|
||||
{
|
||||
bPayloadPocket = FindLaunchable(pSoldier, usInHand);
|
||||
}
|
||||
if (bPayloadPocket == NO_SLOT)
|
||||
{
|
||||
return; // no ammo, can't fire
|
||||
}
|
||||
ubSafetyMargin = (UINT8)Explosive[ Item[ pSoldier->inv[bPayloadPocket].usItem ].ubClassIndex ].ubRadius;
|
||||
ubSafetyMargin = (UINT8)Explosive[Item[pSoldier->inv[bPayloadPocket].usItem].ubClassIndex].ubRadius;
|
||||
usGrenade = pSoldier->inv[bPayloadPocket].usItem;
|
||||
}
|
||||
fRocketLauncher = TRUE;
|
||||
}
|
||||
else if (Item[usInHand].cannon )
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: buddy's got a tank");
|
||||
bPayloadPocket = FindLaunchable ( pSoldier, usInHand );
|
||||
bPayloadPocket = FindNonSmokeLaunchable(pSoldier, usInHand);
|
||||
if (bPayloadPocket == NO_SLOT)
|
||||
{
|
||||
bPayloadPocket = FindLaunchable(pSoldier, usInHand);
|
||||
}
|
||||
if (bPayloadPocket == NO_SLOT)
|
||||
{
|
||||
return; // no ammo, can't fire
|
||||
}
|
||||
ubSafetyMargin = (UINT8)Explosive[ Item[ pSoldier->inv[bPayloadPocket].usItem ].ubClassIndex ].ubRadius;
|
||||
|
||||
|
||||
//bPayloadPocket = FindObj( pSoldier, TANK_SHELL );
|
||||
//if (bPayloadPocket == NO_SLOT)
|
||||
//{
|
||||
// return; // no grenades, can't fire the GLAUNCHER
|
||||
//}
|
||||
//ubSafetyMargin = Explosive[ Item[ TANK_SHELL ].ubClassIndex ].ubRadius;
|
||||
ubSafetyMargin = (UINT8)Explosive[Item[pSoldier->inv[bPayloadPocket].usItem].ubClassIndex].ubRadius;
|
||||
usGrenade = pSoldier->inv[bPayloadPocket].usItem;
|
||||
fCannon = TRUE;
|
||||
|
||||
}
|
||||
else
|
||||
@@ -786,6 +834,29 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
// JA2Gold: light isn't as nasty as explosives
|
||||
ubSafetyMargin /= 2;
|
||||
}
|
||||
fHandGrenade = TRUE;
|
||||
}
|
||||
|
||||
// sevenfm: limit ubSafetyMargin in case it is set too high in XML
|
||||
ubSafetyMargin = min(ubSafetyMargin, TACTICAL_RANGE / 2);
|
||||
|
||||
if (EXPLOSIVE_GUN(usInHand))
|
||||
{
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "explosive gun");
|
||||
iTossRange = GetModifiedGunRange(usInHand) / CELL_X_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "about to calcmaxtossrange");
|
||||
iTossRange = CalcMaxTossRange(pSoldier, usInHand, TRUE);
|
||||
}
|
||||
|
||||
// use flares only at night
|
||||
if (usGrenade != NOTHING &&
|
||||
Item[usGrenade].flare &&
|
||||
!NightLight())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ubDiff = SoldierDifficultyLevel( pSoldier );
|
||||
@@ -793,7 +864,6 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
// make a list of tiles one's friends are positioned in
|
||||
for (ubLoop = 0; ubLoop < guiNumMercSlots; ubLoop++)
|
||||
{
|
||||
|
||||
pFriend = MercSlots[ubLoop];
|
||||
|
||||
if ( !pFriend )
|
||||
@@ -843,44 +913,29 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
continue; // next soldier
|
||||
}
|
||||
|
||||
/*
|
||||
// if this soldier is inactive, at base, on assignment, or dead
|
||||
if (!pOpponent->bActive || !pOpponent->bInSector || !pOpponent->stats.bLife)
|
||||
continue; // next soldier
|
||||
*/
|
||||
if (!ValidOpponent(pSoldier, pOpponent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bPersOL = pSoldier->aiData.bOppList[pOpponent->ubID];
|
||||
bPublOL = gbPublicOpplist[pSoldier->bTeam][pOpponent->ubID];
|
||||
bPersonalKnowledge = PersonalKnowledge(pSoldier, pOpponent->ubID);
|
||||
bPublicKnowledge = PublicKnowledge(pSoldier->bTeam, pOpponent->ubID);
|
||||
bKnowledge = Knowledge(pSoldier, pOpponent->ubID);
|
||||
|
||||
//bPersonalKnowledge = pSoldier->aiData.bOppList[pOpponent->ubID];
|
||||
//bPublicKnowledge = gbPublicOpplist[pSoldier->bTeam][pOpponent->ubID];
|
||||
|
||||
// we know nothing about this opponent
|
||||
if (bPersOL == NOT_HEARD_OR_SEEN && bPublOL == NOT_HEARD_OR_SEEN)
|
||||
if (bPersonalKnowledge == NOT_HEARD_OR_SEEN && bPublicKnowledge == NOT_HEARD_OR_SEEN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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 soldier
|
||||
}
|
||||
|
||||
// silversurfer: ignore empty vehicles
|
||||
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)
|
||||
{
|
||||
continue; // next opponent
|
||||
}
|
||||
|
||||
// sevenfm: additional restrictions
|
||||
|
||||
// blinded soldier can only attack recently seen/heard opponents
|
||||
if (pSoldier->bBlindedCounter > 0 &&
|
||||
bPersOL != SEEN_CURRENTLY &&
|
||||
bPersOL != SEEN_THIS_TURN &&
|
||||
bPersOL != HEARD_THIS_TURN)
|
||||
bPersonalKnowledge != SEEN_CURRENTLY &&
|
||||
bPersonalKnowledge != SEEN_THIS_TURN &&
|
||||
bPersonalKnowledge != HEARD_THIS_TURN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -952,100 +1007,92 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((Item[usInHand].mortar ) || (Item[usInHand].grenadelauncher ) || (Item[usInHand].cannon ) )
|
||||
|
||||
if (Item[usInHand].mortar)
|
||||
{
|
||||
// allow long range firing, where target doesn't PERSONALLY see opponent
|
||||
if ((bPersOL != SEEN_CURRENTLY) && (bPublOL != SEEN_CURRENTLY))
|
||||
// active KNOWN opponent, remember where he is so that we DO blow him up!
|
||||
if (bPersonalKnowledge == SEEN_CURRENTLY ||
|
||||
bPublicKnowledge == SEEN_CURRENTLY)
|
||||
{
|
||||
//DebugShot( pSoldier, String("opponent is seen currently, use exact location"));
|
||||
sOpponentTile[ubOpponentCnt] = pOpponent->sGridNo;
|
||||
bOpponentLevel[ubOpponentCnt] = pOpponent->pathing.bLevel;
|
||||
}
|
||||
else if ((bKnowledge == SEEN_THIS_TURN || bKnowledge == SEEN_LAST_TURN || bKnowledge == HEARD_THIS_TURN || bKnowledge == HEARD_LAST_TURN || bKnowledge == HEARD_2_TURNS_AGO) &&
|
||||
//(AICheckIsSniper(pOpponent) || AICheckIsMortarOperator(pOpponent) || AICheckIsRadioOperator(pOpponent) || TeamHighPercentKilled(pSoldier->bTeam) || fSectorCurFew || fSectorAttack) &&
|
||||
!TileIsOutOfBounds(KnownLocation(pSoldier, pOpponent->ubID)))
|
||||
{
|
||||
sOpponentTile[ubOpponentCnt] = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bOpponentLevel[ubOpponentCnt] = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // next soldier
|
||||
}
|
||||
// active KNOWN opponent, remember where he is so that we DO blow him up!
|
||||
sOpponentTile[ubOpponentCnt] = pOpponent->sGridNo;
|
||||
bOpponentLevel[ubOpponentCnt] = pOpponent->pathing.bLevel;
|
||||
}
|
||||
else
|
||||
else if (Item[usInHand].rocketlauncher)
|
||||
{
|
||||
/*
|
||||
if (bPersOL != SEEN_CURRENTLY && bPersOL != SEEN_LAST_TURN) // if not in sight right now
|
||||
{
|
||||
continue; // next soldier
|
||||
}
|
||||
*/
|
||||
if (bPersOL == SEEN_CURRENTLY)
|
||||
if (bPersonalKnowledge == SEEN_CURRENTLY || bPublicKnowledge == SEEN_CURRENTLY)
|
||||
{
|
||||
// active KNOWN opponent, remember where he is so that we DO blow him up!
|
||||
sOpponentTile[ubOpponentCnt] = pOpponent->sGridNo;
|
||||
bOpponentLevel[ubOpponentCnt] = pOpponent->pathing.bLevel;
|
||||
}
|
||||
else if (bPersOL == SEEN_LAST_TURN)
|
||||
// check if opponent is in a building and was seen/heard recently
|
||||
else if (pSoldier->bTeam == ENEMY_TEAM &&
|
||||
(bKnowledge == SEEN_THIS_TURN || bKnowledge == SEEN_LAST_TURN || bKnowledge == HEARD_THIS_TURN || bKnowledge == HEARD_LAST_TURN) &&
|
||||
!TileIsOutOfBounds(KnownLocation(pSoldier, pOpponent->ubID)) &&
|
||||
InARoom(KnownLocation(pSoldier, pOpponent->ubID), NULL))
|
||||
{
|
||||
// cheat; only allow throw if person is REALLY within 2 tiles of where last seen
|
||||
|
||||
// JA2Gold: UB checks were screwed up
|
||||
/*
|
||||
if ( pOpponent->sGridNo == gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ] )
|
||||
{
|
||||
sOpponentTile[ubOpponentCnt] = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bOpponentLevel[ubOpponentCnt] = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else */
|
||||
if ( !CloseEnoughForGrenadeToss( pOpponent->sGridNo, gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sOpponentTile[ubOpponentCnt] = gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ];
|
||||
bOpponentLevel[ubOpponentCnt] = gbLastKnownOppLevel[ pSoldier->ubID ][ pOpponent->ubID ];
|
||||
|
||||
// JA2Gold: commented out
|
||||
/*
|
||||
if ( SpacesAway( pOpponent->sGridNo, gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ] ) > 2 )
|
||||
{
|
||||
}
|
||||
}
|
||||
else if( Item[usInHand].grenadelauncher )
|
||||
{
|
||||
if (bPersonalKnowledge == SEEN_CURRENTLY || bPublicKnowledge == SEEN_CURRENTLY)
|
||||
{
|
||||
// active KNOWN opponent, remember where he is so that we DO blow him up!
|
||||
sOpponentTile[ubOpponentCnt] = pOpponent->sGridNo;
|
||||
bOpponentLevel[ubOpponentCnt] = pOpponent->pathing.bLevel;
|
||||
}
|
||||
else if ((bKnowledge == SEEN_THIS_TURN || bKnowledge == SEEN_LAST_TURN || bKnowledge == HEARD_THIS_TURN || bKnowledge == HEARD_LAST_TURN || bKnowledge == HEARD_2_TURNS_AGO) &&
|
||||
!TileIsOutOfBounds(KnownLocation(pSoldier, pOpponent->ubID)))
|
||||
{
|
||||
sOpponentTile[ubOpponentCnt] = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bOpponentLevel[ubOpponentCnt] = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sOpponentTile[ubOpponentCnt] = gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ];
|
||||
bOpponentLevel[ubOpponentCnt] = gbLastKnownOppLevel[ pSoldier->ubID ][ pOpponent->ubID ];
|
||||
*/
|
||||
}
|
||||
else if (bPersOL == HEARD_LAST_TURN )
|
||||
}
|
||||
else
|
||||
{
|
||||
// hand grenade
|
||||
if (bPersonalKnowledge == SEEN_CURRENTLY || bPublicKnowledge == SEEN_CURRENTLY)
|
||||
{
|
||||
// cheat; only allow throw if person is REALLY within 2 tiles of where last seen
|
||||
|
||||
// screen out some ppl who have thrown
|
||||
if ( PreRandom( 3 ) == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Weird detail: if the opponent is in the same location then they may have closed a door on us.
|
||||
// In which case, don't throw!
|
||||
|
||||
// JA2Gold: UB checks were screwed up
|
||||
/*
|
||||
if ( pOpponent->sGridNo == gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ] )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
*/
|
||||
if ( !CloseEnoughForGrenadeToss( pOpponent->sGridNo, gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ( !Item[usGrenade].flare && !pSoldier->aiData.bUnderFire && pSoldier->aiData.bShock == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sOpponentTile[ubOpponentCnt] = gsLastKnownOppLoc[ pSoldier->ubID ][ pOpponent->ubID ];
|
||||
bOpponentLevel[ubOpponentCnt] = gbLastKnownOppLevel[ pSoldier->ubID ][ pOpponent->ubID ];
|
||||
// active KNOWN opponent, remember where he is so that we DO blow him up!
|
||||
sOpponentTile[ubOpponentCnt] = pOpponent->sGridNo;
|
||||
bOpponentLevel[ubOpponentCnt] = pOpponent->pathing.bLevel;
|
||||
}
|
||||
else if ((bKnowledge == SEEN_THIS_TURN || bKnowledge == SEEN_LAST_TURN || bKnowledge == HEARD_THIS_TURN || bKnowledge == HEARD_LAST_TURN) &&
|
||||
!TileIsOutOfBounds(KnownLocation(pSoldier, pOpponent->ubID)) &&
|
||||
CloseEnoughForGrenadeToss(pOpponent->sGridNo, KnownLocation(pSoldier, pOpponent->ubID)) &&
|
||||
(usGrenade != NOTHING && Item[usGrenade].flare || pSoldier->aiData.bUnderFire || pSoldier->aiData.bShock))
|
||||
{
|
||||
sOpponentTile[ubOpponentCnt] = KnownLocation(pSoldier, pOpponent->ubID);
|
||||
bOpponentLevel[ubOpponentCnt] = KnownLevel(pSoldier, pOpponent->ubID);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// also remember who he is (which soldier #)
|
||||
ubOpponentID[ubOpponentCnt] = pOpponent->ubID;
|
||||
@@ -1056,35 +1103,53 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
ubOpponentCnt++;
|
||||
}
|
||||
|
||||
//NumMessage("ubOpponentCnt = ",ubOpponentCnt);
|
||||
|
||||
|
||||
// this is try to minimize enemies wasting their (limited) toss attacks, with the exception of break lights
|
||||
if ( !Item[usGrenade].flare )
|
||||
BOOLEAN fSpare = TRUE;
|
||||
|
||||
// tanks don't spare
|
||||
if (fCannon)
|
||||
{
|
||||
switch( ubDiff )
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
// they won't use them until they have 2+ opponents as long as half life left
|
||||
// anv: tanks don't care
|
||||
if ( ( ubOpponentCnt < 2 ) && ( pSoldier->stats.bLife > pSoldier->stats.bLifeMax / 2 ) &&
|
||||
(!gGameExternalOptions.fEnemyTanksDontSpareShells || !ARMED_VEHICLE( pSoldier )) && !gGameExternalOptions.fEnemiesDontSpareLaunchables )
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// they won't use them until they have 2+ opponents as long as 3/4 life left
|
||||
if ( (ubOpponentCnt < 2) && ( pSoldier->stats.bLife > (pSoldier->stats.bLifeMax / 4) * 3 ) &&
|
||||
(!gGameExternalOptions.fEnemyTanksDontSpareShells || !ARMED_VEHICLE( pSoldier )) && !gGameExternalOptions.fEnemiesDontSpareLaunchables )
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fSpare = FALSE;
|
||||
}
|
||||
|
||||
// don't spare rocket launchers here, will check later, including check for tank target
|
||||
if (fRocketLauncher)
|
||||
{
|
||||
fSpare = FALSE;
|
||||
}
|
||||
|
||||
// don't spare non lethal grenades
|
||||
if (usGrenade != NOTHING &&
|
||||
(Item[usGrenade].flare ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_STUN ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_SMOKE ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_TEARGAS ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_FLASHBANG && NightLight()))
|
||||
{
|
||||
fSpare = FALSE;
|
||||
}
|
||||
|
||||
// chance if some friends are killed
|
||||
if (Chance(TeamPercentKilled(pSoldier->bTeam)) && Chance(ubDiff * 10))
|
||||
{
|
||||
fSpare = FALSE;
|
||||
}
|
||||
|
||||
// don't spare when soldier is under attack or too many killed
|
||||
if (pSoldier->aiData.bUnderFire ||
|
||||
TeamHighPercentKilled(pSoldier->bTeam) ||
|
||||
CountTeamUnderAttack(pSoldier->bTeam, pSoldier->sGridNo, TACTICAL_RANGE / 2) > 0 ||
|
||||
pSoldier->aiData.bOrders == STATIONARY ||
|
||||
pSoldier->aiData.bOrders == SNIPER ||
|
||||
pSoldier->pathing.bLevel > 0 && pSoldier->aiData.bAlertStatus == STATUS_RED && fHandGrenade)
|
||||
{
|
||||
fSpare = FALSE;
|
||||
}
|
||||
|
||||
// need 3 opponents for 0 difficulty, 1 opponent for max difficulty
|
||||
if (fSpare && ubOpponentCnt < 3 - ubDiff / 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: about to initattacktype");
|
||||
@@ -1100,19 +1165,24 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
// search all tiles within 2 squares of this opponent
|
||||
ubSearchRange = MAX_TOSS_SEARCH_DIST;
|
||||
|
||||
// sevenfm: increase possible distance from opponent when opponent in a building or when using gas grenades
|
||||
if (gpWorldLevelData[sOpponentTile[ubLoop]].ubTerrainID == FLAT_FLOOR ||
|
||||
usGrenade != NOTHING &&
|
||||
(Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_TEARGAS ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_MUSTGAS))
|
||||
{
|
||||
ubSearchRange++;
|
||||
}
|
||||
|
||||
// determine maximum horizontal limits
|
||||
//bMaxLeft = min(ubSearchRange,(sOpponentTile[ubLoop] % MAXCOL));
|
||||
bMaxLeft = ubSearchRange;
|
||||
//NumMessage("bMaxLeft = ",bMaxLeft);
|
||||
//bMaxRight = min(ubSearchRange,MAXCOL - ((sOpponentTile[ubLoop] % MAXCOL) + 1));
|
||||
bMaxRight = ubSearchRange;
|
||||
//NumMessage("bMaxRight = ",bMaxRight);
|
||||
|
||||
// determine maximum vertical limits
|
||||
bMaxUp = ubSearchRange;
|
||||
//NumMessage("bMaxUp = ",bMaxUp);
|
||||
bMaxDown = ubSearchRange;
|
||||
//NumMessage("bMaxDown = ",bMaxDown);
|
||||
|
||||
// evaluate every tile for its opponent-damaging potential
|
||||
for (bYOffset = -bMaxUp; bYOffset <= bMaxDown; bYOffset++)
|
||||
@@ -1141,45 +1211,26 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
}
|
||||
|
||||
// if considering a gas/smoke grenade, check to see if there is such stuff already there!
|
||||
if ( usGrenade )
|
||||
if (usGrenade &&
|
||||
(Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_TEARGAS ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_MUSTGAS ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_BURNABLEGAS ||
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_SMOKE) &&
|
||||
(gpWorldLevelData[sGridNo].ubExtFlags[bOpponentLevel[ubLoop]] & MAPELEMENT_EXT_SMOKE ||
|
||||
gpWorldLevelData[sGridNo].ubExtFlags[bOpponentLevel[ubLoop]] & MAPELEMENT_EXT_TEARGAS ||
|
||||
gpWorldLevelData[sGridNo].ubExtFlags[bOpponentLevel[ubLoop]] & MAPELEMENT_EXT_MUSTARDGAS ||
|
||||
gpWorldLevelData[sGridNo].ubExtFlags[bOpponentLevel[ubLoop]] & MAPELEMENT_EXT_BURNABLEGAS))
|
||||
{
|
||||
// if ( gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & MAPELEMENT_EXT_SMOKE ||
|
||||
// gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & (MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS) ||
|
||||
// gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & MAPELEMENT_EXT_MUSTARDGAS)
|
||||
if ( gpWorldLevelData[sGridNo].ubExtFlags[bOpponentLevel[ubLoop]] & (MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_DEBRIS_SMOKE) ||
|
||||
gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & (MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS | MAPELEMENT_EXT_BURNABLEGAS) ||
|
||||
gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & MAPELEMENT_EXT_MUSTARDGAS || gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & MAPELEMENT_EXT_BURNABLEGAS)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//switch( usGrenade )
|
||||
//{
|
||||
// case SMOKE_GRENADE:
|
||||
// case GL_SMOKE_GRENADE:
|
||||
// // skip smoke
|
||||
// if ( gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & MAPELEMENT_EXT_SMOKE )
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
// case TEARGAS_GRENADE:
|
||||
// // skip tear and mustard gas
|
||||
// if ( gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & (MAPELEMENT_EXT_TEARGAS | MAPELEMENT_EXT_MUSTARDGAS) )
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
// case MUSTARD_GRENADE:
|
||||
// // skip mustard gas
|
||||
// if ( gpWorldLevelData[ sGridNo ].ubExtFlags[ bOpponentLevel[ubLoop] ] & MAPELEMENT_EXT_MUSTARDGAS )
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
//}
|
||||
// don't use smoke grenade if there's already smoke nearby
|
||||
if (usGrenade &&
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_SMOKE &&
|
||||
InSmokeNearby(sGridNo, bOpponentLevel[ubLoop]))
|
||||
{
|
||||
//DebugShot(pSoldier, String("smoke grenade, found smoke nearby - skip"));
|
||||
continue;
|
||||
}
|
||||
|
||||
fSkipLocation = FALSE;
|
||||
@@ -1199,12 +1250,12 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
}
|
||||
|
||||
// calculate minimum action points required to throw at this gridno
|
||||
ubMinAPcost = MinAPsToAttack(pSoldier,sGridNo,ADDTURNCOST,0);
|
||||
DebugMsg(TOPIC_JA2 , DBG_LEVEL_3 , String("MinAPcost to attack = %d",ubMinAPcost));
|
||||
sMinAPcost = MinAPsToAttack(pSoldier,sGridNo,ADDTURNCOST,0);
|
||||
DebugMsg(TOPIC_JA2 , DBG_LEVEL_3 , String("MinAPcost to attack = %d",sMinAPcost));
|
||||
|
||||
// if we don't have enough APs left to throw even without aiming
|
||||
DebugMsg(TOPIC_JA2 , DBG_LEVEL_3 , String("Soldier's action points = %d",pSoldier->bActionPoints ));
|
||||
if (ubMinAPcost > pSoldier->bActionPoints)
|
||||
if (sMinAPcost > pSoldier->bActionPoints)
|
||||
continue; // next gridno
|
||||
|
||||
// check whether there are any friends standing near this gridno
|
||||
@@ -1227,9 +1278,6 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
iTotalThreatValue = 0;
|
||||
ubOppsInRange = 0;
|
||||
ubOppsAdjacent = 0;
|
||||
// skip this location unless it's right on top of an enemy or
|
||||
// adjacent to more than 1
|
||||
fSkipLocation = TRUE;
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: checking opponents");
|
||||
for (ubLoop2 = 0; ubLoop2 < ubOpponentCnt; ubLoop2++)
|
||||
@@ -1244,19 +1292,12 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
|
||||
// estimate how much damage this tossed item would do to him
|
||||
iEstDamage = EstimateThrowDamage(pSoldier,bPayloadPocket,MercPtrs[ubOpponentID[ubLoop2]],sGridNo);
|
||||
//NumMessage("THROW EstDamage = ",iEstDamage);
|
||||
|
||||
if (usOppDist)
|
||||
{
|
||||
// reduce the estimated damage for his distance from gridno
|
||||
// use 100% at range 0, 80% at range 1, and 60% at range 2, etc.
|
||||
iEstDamage = (iEstDamage * (100 - (20 * usOppDist))) / 100;
|
||||
//NumMessage("THROW reduced usEstDamage = ",usEstDamage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// throwing right on top of someone... always consider this
|
||||
fSkipLocation = FALSE;
|
||||
}
|
||||
|
||||
// add the product of his threat value & damage caused to total
|
||||
@@ -1269,39 +1310,16 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
if (usOppDist < 2)
|
||||
{
|
||||
ubOppsAdjacent++;
|
||||
if (ubOppsAdjacent > 1 || Item[usGrenade].flare )
|
||||
{
|
||||
fSkipLocation = FALSE;
|
||||
// add to exclusion list so we don't consider it again
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// JA2Gold
|
||||
if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_EASY )
|
||||
if (ubOppsInRange == 0)
|
||||
{
|
||||
if (fSkipLocation)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ubOppsInRange == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//Only use it if we are in a surface sector ( basement will be hard enough, plus more chances of mercs being clumped together )
|
||||
else if( gbWorldSectorZ > 0 && fSkipLocation )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// JA2Gold change to >=
|
||||
if (ubOppsAdjacent >= 1 && ubNumExcludedTiles < 100)
|
||||
{
|
||||
// add to exclusion list so we don't calculate for this location twice
|
||||
@@ -1344,12 +1362,22 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
{
|
||||
ubChanceToGetThrough = 100 - (UINT8) uiPenalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (Item[usInHand].mortar &&
|
||||
usGrenade != NOTHING &&
|
||||
Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_NORMAL &&
|
||||
bOpponentLevel[ubLoop] == 0 &&
|
||||
InARoom(sOpponentTile[ubLoop], NULL) &&
|
||||
bEndLevel == 1 &&
|
||||
gGameExternalOptions.fRoofCollapse &&
|
||||
PythSpacesAway(sGridNo, sEndGridNo) < (INT16)(TACTICAL_RANGE / 4))
|
||||
{
|
||||
// calc new chance to hit roof above spot
|
||||
ubChanceToGetThrough = 100 * CalculateLaunchItemChanceToGetThrough(pSoldier, (pObjGL ? pObjGL : &pSoldier->inv[bPayloadPocket]), sGridNo, 1, 0, &sEndGridNo, TRUE, &bEndLevel, FALSE);
|
||||
}
|
||||
|
||||
// if still cannot hit, skip location
|
||||
if (ubChanceToGetThrough == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1361,38 +1389,35 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
|
||||
// this is try to minimize enemies wasting their (few) mortar shells or LAWs
|
||||
// they won't use them on less than 2 targets as long as half life left
|
||||
if ((Item[usInHand].mortar || Item[usInHand].rocketlauncher ) && (ubOppsInRange < 2) &&
|
||||
(pSoldier->stats.bLife >( pSoldier->stats.bLifeMax / 2 )) && (!gGameExternalOptions.fEnemyTanksDontSpareShells || !ARMED_VEHICLE( pSoldier )) && !gGameExternalOptions.fEnemiesDontSpareLaunchables )
|
||||
if ((Item[usInHand].mortar || Item[usInHand].rocketlauncher) && (ubOppsInRange < 2) &&
|
||||
(!gGameExternalOptions.fEnemyTanksDontSpareShells || !ARMED_VEHICLE(pSoldier)) &&
|
||||
!gGameExternalOptions.fEnemiesDontSpareLaunchables)
|
||||
{
|
||||
continue; // next gridno
|
||||
}
|
||||
|
||||
// calculate the maximum possible aiming time
|
||||
// HEADROCK HAM 4: Required for new Aiming Level Limits function
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sGridNo, pSoldier->bActionPoints-ubMinAPcost);//dnl ch63 250813
|
||||
DebugMsg(TOPIC_JA2 , DBG_LEVEL_3 , String("Max Possible Aim Time = %d",ubMaxPossibleAimTime ));
|
||||
|
||||
// calc next attack's minimum AP cost (excludes readying & turning)
|
||||
|
||||
// since grenades & shells are far too valuable to waste, ALWAYS
|
||||
// aim for the longest time possible!
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: checking chance to hit");
|
||||
if ( EXPLOSIVE_GUN( usInHand ) )
|
||||
// limit RPG use, unless can hit several enemies, shooting at tank or opponent is in a room
|
||||
if (Item[usInHand].rocketlauncher &&
|
||||
ubOppsInRange < 2 &&
|
||||
!ARMED_VEHICLE(MercPtrs[ubOpponentID[ubLoop]]) &&
|
||||
!InARoom(sOpponentTile[ubLoop], NULL))
|
||||
{
|
||||
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);
|
||||
continue; // next gridno
|
||||
}
|
||||
|
||||
if (EXPLOSIVE_GUN(usInHand))
|
||||
{
|
||||
// calculate the maximum possible aiming time
|
||||
ubMaxPossibleAimTime = CalcAimingLevelsAvailableWithAP(pSoldier, sGridNo, pSoldier->bActionPoints - sMinAPcost);//dnl ch63 250813
|
||||
sRawAPCost = MinAPsToShootOrStab(pSoldier, sGridNo, ubMaxPossibleAimTime, FALSE);
|
||||
ubChanceToHit = (UINT8)AICalcChanceToHitGun(pSoldier, sGridNo, ubMaxPossibleAimTime, AIM_SHOT_TORSO, pOpponent->pathing.bLevel, STANDING);//dnl ch59 130813
|
||||
}
|
||||
else
|
||||
{
|
||||
ubMaxPossibleAimTime = (UINT8)APBPConstants[AP_MIN_AIM_ATTACK];//dnl ch63 240813
|
||||
// NB grenade launcher is NOT a direct fire weapon!
|
||||
ubRawAPCost = (UINT8) MinAPsToThrow( pSoldier, sGridNo, FALSE );
|
||||
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);
|
||||
// no aiming for thrown items
|
||||
ubMaxPossibleAimTime = 0;
|
||||
sRawAPCost = (UINT8)MinAPsToThrow(pSoldier, sGridNo, FALSE);
|
||||
ubChanceToHit = (UINT8)CalcThrownChanceToHit(pSoldier, sGridNo, ubMaxPossibleAimTime, AIM_SHOT_TORSO);
|
||||
}
|
||||
|
||||
// no bonus for RPGs as they can miss too much
|
||||
@@ -1403,19 +1428,14 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
ubChanceToHit += (ubChanceToHit / 2);
|
||||
|
||||
// still can't let it go over 100% chance, though
|
||||
if (ubChanceToHit > 100)
|
||||
{
|
||||
ubChanceToHit = 100;
|
||||
}
|
||||
ubChanceToHit = min(ubChanceToHit, 100);
|
||||
}
|
||||
|
||||
if (ubRawAPCost < 1)
|
||||
ubRawAPCost = ubMinAPcost;
|
||||
if (sRawAPCost < 1)
|
||||
sRawAPCost = sMinAPcost;
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("calcbestthrow: checking hit rate: ubRawAPCost %d, ubMaxPossibleAimTime %d", ubRawAPCost, ubMaxPossibleAimTime ));
|
||||
iHitRate = (pSoldier->bActionPoints * ubChanceToHit) / (ubRawAPCost + ubMaxPossibleAimTime);
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: checked hit rate");
|
||||
//NumMessage("iHitRate = ",iHitRate);
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("calcbestthrow: checking hit rate: ubRawAPCost %d, ubMaxPossibleAimTime %d", sRawAPCost, ubMaxPossibleAimTime ));
|
||||
iHitRate = (pSoldier->bActionPoints * ubChanceToHit) / (sRawAPCost + ubMaxPossibleAimTime * APBPConstants[AP_CLICK_AIM]);
|
||||
|
||||
// calculate chance to REALLY hit: throw accurately AND get past cover
|
||||
ubChanceToReallyHit = (ubChanceToHit * ubChanceToGetThrough) / 100;
|
||||
@@ -1429,7 +1449,12 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
// typical attack value here should be about 500 thousand
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"calcbestthrow: checking attack value");
|
||||
iAttackValue = (iHitRate * ubChanceToReallyHit * iTotalThreatValue) / 1000;
|
||||
//NumMessage("THROW AttackValue = ",iAttackValue / 1000);
|
||||
|
||||
// modify smoke attack value depending on range
|
||||
if (usGrenade && Explosive[Item[usGrenade].ubClassIndex].ubType == EXPLOSV_SMOKE && PythSpacesAway(pSoldier->sGridNo, sGridNo) < (INT16)TACTICAL_RANGE)
|
||||
{
|
||||
iAttackValue = iAttackValue * PythSpacesAway(pSoldier->sGridNo, sGridNo) / TACTICAL_RANGE;
|
||||
}
|
||||
|
||||
// unlike SHOOTing and STABbing, find strictly the highest attackValue
|
||||
if (iAttackValue > pBestThrow->iAttackValue)
|
||||
@@ -1445,11 +1470,15 @@ void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow)
|
||||
pBestThrow->ubChanceToReallyHit = ubChanceToReallyHit;
|
||||
pBestThrow->sTarget = sGridNo;
|
||||
pBestThrow->iAttackValue = iAttackValue;
|
||||
pBestThrow->ubAPCost = ubMinAPcost + CalcAPCostForAiming(pSoldier, sGridNo, ubMaxPossibleAimTime);//dnl ch64 310813
|
||||
//pBestThrow->ubAPCost = sMinAPcost + CalcAPCostForAiming(pSoldier, sGridNo, ubMaxPossibleAimTime);//dnl ch64 310813
|
||||
pBestThrow->ubAPCost = MinAPsToAttack(pSoldier, sGridNo, ADDTURNCOST, ubMaxPossibleAimTime);
|
||||
pBestThrow->bTargetLevel = bOpponentLevel[ubLoop];
|
||||
// set current stance
|
||||
pBestThrow->ubStance = gAnimControl[pSoldier->usAnimState].ubEndHeight;
|
||||
|
||||
//sprintf(tempstr,"new best THROW AttackValue = %d at grid #%d",iAttackValue/100000,gridno);
|
||||
//PopMessage(tempstr);
|
||||
// bWeaponIn
|
||||
// bScopeMode
|
||||
// ubFriendlyFireChance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+155
-197
@@ -2660,85 +2660,121 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// IF POSSIBLE, FIRE LONG RANGE WEAPONS AT TARGETS REPORTED BY RADIO
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//if(!is_networked)//hayden
|
||||
//{
|
||||
|
||||
// can't do this in realtime, because the player could be shooting a gun or whatever at the same time!
|
||||
if (gfTurnBasedAI && !fCivilian && !bInWater && !bInGas && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && (CanNPCAttack(pSoldier) == TRUE))
|
||||
if (gfTurnBasedAI &&
|
||||
!fCivilian &&
|
||||
!bInWater &&
|
||||
!bInGas &&
|
||||
pSoldier->CheckInitialAP() &&
|
||||
!pSoldier->IsFlanking() &&
|
||||
!(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) &&
|
||||
(CanNPCAttack(pSoldier) == TRUE))
|
||||
{
|
||||
BestThrow.ubPossible = FALSE; // by default, assume Throwing isn't possible
|
||||
|
||||
|
||||
DebugAI(AI_MSG_TOPIC, pSoldier, String("[CheckIfTossPossible]"));
|
||||
CheckIfTossPossible(pSoldier,&BestThrow);
|
||||
|
||||
if (BestThrow.ubPossible)
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("throw possible"));
|
||||
else
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("throw not possible"));
|
||||
|
||||
if (BestThrow.ubPossible)
|
||||
{
|
||||
// if firing mortar make sure we have room
|
||||
//if ( Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].mortar ) //comm by ddd
|
||||
if (Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].mortar
|
||||
|| Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].grenadelauncher
|
||||
|| Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].flare )
|
||||
// sevenfm: allow using mortars, grenade launchers, flares and grenades in RED state
|
||||
if (Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].mortar ||
|
||||
//Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].cannon ||
|
||||
Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].rocketlauncher ||
|
||||
Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].grenadelauncher ||
|
||||
Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].flare ||
|
||||
Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].usItemClass & IC_GRENADE)
|
||||
{
|
||||
ubOpponentDir = GetDirectionFromGridNo( BestThrow.sTarget, pSoldier );
|
||||
|
||||
// Get new gridno!
|
||||
sCheckGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( ubOpponentDir ) );
|
||||
|
||||
if ( OKFallDirection( pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, ubOpponentDir, pSoldier->usAnimState ) )
|
||||
// if firing mortar make sure we have room
|
||||
if (Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].mortar)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("using mortar, check room to deploy"));
|
||||
ubOpponentDir = AIDirection(pSoldier->sGridNo, BestThrow.sTarget);
|
||||
|
||||
// then do it! The functions have already made sure that we have a
|
||||
// pair of worthy opponents, etc., so we're not just wasting our time
|
||||
// Get new gridno!
|
||||
sCheckGridNo = NewGridNo(pSoldier->sGridNo, DirectionInc(ubOpponentDir));
|
||||
|
||||
if (!OKFallDirection(pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, ubOpponentDir, pSoldier->usAnimState))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("no room to deploy mortar, check if we can move behind"));
|
||||
|
||||
// can't fire!
|
||||
BestThrow.ubPossible = FALSE;
|
||||
|
||||
// try behind us, see if there's room to move back
|
||||
sCheckGridNo = NewGridNo(pSoldier->sGridNo, DirectionInc(gOppositeDirection[ubOpponentDir]));
|
||||
if (OKFallDirection(pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, gOppositeDirection[ubOpponentDir], pSoldier->usAnimState))
|
||||
{
|
||||
pSoldier->aiData.usActionData = sCheckGridNo;
|
||||
|
||||
return(AI_ACTION_GET_CLOSER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if still possible
|
||||
if (BestThrow.ubPossible)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime));
|
||||
|
||||
// if necessary, swap the usItem from holster into the hand position
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: if necessary, swap the usItem from holster into the hand position");
|
||||
if (BestThrow.bWeaponIn != HANDPOS)
|
||||
RearrangePocket(pSoldier,HANDPOS,BestThrow.bWeaponIn,FOREVER);
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"));
|
||||
RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER);
|
||||
}
|
||||
|
||||
pSoldier->aiData.usActionData = BestThrow.sTarget;
|
||||
//POSSIBLE STRUCTURE CHANGE PROBLEM, NOT CURRENTLY CHANGED. GOTTHARD 7/14/08
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
// sevenfm: correctly set weapon mode for attached GL
|
||||
if (IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS]))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("set attached GL mode"));
|
||||
pSoldier->bWeaponMode = WM_ATTACHED_GL;
|
||||
}
|
||||
|
||||
// stand up before throwing if needed
|
||||
if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance &&
|
||||
pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance))
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.ubStance;
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE;
|
||||
pSoldier->aiData.usNextActionData = BestThrow.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
return AI_ACTION_CHANGE_STANCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestThrow.sTarget;
|
||||
pSoldier->bTargetLevel = BestThrow.bTargetLevel;
|
||||
pSoldier->aiData.bAimTime = BestThrow.ubAimTime;
|
||||
}
|
||||
|
||||
return(AI_ACTION_TOSS_PROJECTILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't fire!
|
||||
BestThrow.ubPossible = FALSE;
|
||||
|
||||
// try behind us, see if there's room to move back
|
||||
sCheckGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( gOppositeDirection[ ubOpponentDir ] ) );
|
||||
if ( OKFallDirection( pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, gOppositeDirection[ ubOpponentDir ], pSoldier->usAnimState ) )
|
||||
{
|
||||
pSoldier->aiData.usActionData = sCheckGridNo;
|
||||
|
||||
return( AI_ACTION_GET_CLOSER );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // toss/throw/launch not possible
|
||||
{
|
||||
// WDS - Fix problem when there is no "best thrown" weapon (i.e., BestThrow.bWeaponIn == NO_SLOT)
|
||||
// WDS - Fix problem when there is no "best thrown" weapon (i.e., BestThrow.bWeaponIn == NO_SLOT)
|
||||
// if this dude has a longe-range weapon on him (longer than normal
|
||||
// sight range), and there's at least one other team-mate around, and
|
||||
// spotters haven't already been called for, then DO SO!
|
||||
|
||||
if ( (BestThrow.bWeaponIn != NO_SLOT) &&
|
||||
(CalcMaxTossRange( pSoldier, pSoldier->inv[BestThrow.bWeaponIn].usItem, TRUE ) > MaxNormalDistanceVisible() ) &&
|
||||
if ((BestThrow.bWeaponIn != NO_SLOT) &&
|
||||
(CalcMaxTossRange(pSoldier, pSoldier->inv[BestThrow.bWeaponIn].usItem, TRUE) > MaxNormalDistanceVisible()) &&
|
||||
(gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) &&
|
||||
(gTacticalStatus.ubSpottersCalledForBy == NOBODY))
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("throw not possible, call for spotters!"));
|
||||
|
||||
// then call for spotters! Uses up the rest of his turn (whatever
|
||||
// that may be), but from now on, BLACK AI NPC may radio sightings!
|
||||
gTacticalStatus.ubSpottersCalledForBy = pSoldier->ubID;
|
||||
// HEADROCK HAM 3.1: This may be causing problems with HAM's lowered AP limit. From now on, we'll check
|
||||
// whether the soldier has more than 0 APs to begin with.
|
||||
if (pSoldier->bActionPoints > 0)
|
||||
pSoldier->bActionPoints = 0;
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
AINameMessage(pSoldier,"calls for spotters!",1000);
|
||||
#endif
|
||||
|
||||
pSoldier->aiData.usActionData = NOWHERE;
|
||||
return(AI_ACTION_NONE);
|
||||
@@ -4534,8 +4570,6 @@ INT16 ubMinAPCost;
|
||||
|
||||
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;
|
||||
BOOLEAN fChangeStanceFirst; // before firing
|
||||
BOOLEAN fClimb;
|
||||
INT16 ubBurstAPs;
|
||||
UINT8 ubOpponentDir;
|
||||
@@ -5071,10 +5105,6 @@ INT16 ubMinAPCost;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// this looks for throwables, and sets BestThrow.ubPossible if it can be done
|
||||
//if ( !gfHiddenInterrupt )
|
||||
// {
|
||||
//if(!is_networked) //disable for mp ai
|
||||
//{
|
||||
CheckIfTossPossible(pSoldier,&BestThrow);
|
||||
|
||||
if (BestThrow.ubPossible)
|
||||
@@ -5082,7 +5112,7 @@ INT16 ubMinAPCost;
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"good throw possible");
|
||||
if ( Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].mortar )
|
||||
{
|
||||
ubOpponentDir = (UINT8)GetDirectionFromGridNo( BestThrow.sTarget, pSoldier );
|
||||
ubOpponentDir = AIDirection(pSoldier->sGridNo, BestThrow.sTarget);
|
||||
|
||||
// Get new gridno!
|
||||
sCheckGridNo = NewGridNo( pSoldier->sGridNo, (UINT16)DirectionInc( ubOpponentDir ) );
|
||||
@@ -5110,13 +5140,6 @@ INT16 ubMinAPCost;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GO STAB AN OPPONENT WITH A KNIFE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -5387,8 +5410,6 @@ INT16 ubMinAPCost;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// copy the information on the best action selected into BestAttack struct
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"copy the information on the best action selected into BestAttack struct");
|
||||
switch (ubBestAttackAction)
|
||||
@@ -5561,8 +5582,6 @@ INT16 ubMinAPCost;
|
||||
// if we wanted to be REALLY mean, we could look at chance to hit and decide whether
|
||||
// to shoot at the head...
|
||||
|
||||
fChangeStanceFirst = FALSE;
|
||||
|
||||
// default settings
|
||||
//POSSIBLE STRUCTURE CHANGE PROBLEM, NOT CURRENTLY CHANGED. GOTTHARD 7/14/08
|
||||
pSoldier->aiData.bAimTime = BestAttack.ubAimTime;
|
||||
@@ -5570,45 +5589,13 @@ INT16 ubMinAPCost;
|
||||
pSoldier->bDoBurst = 0;
|
||||
|
||||
// HEADROCK HAM 3.6: bAimTime represents how MANY aiming levels are used, not how much APs they cost necessarily.
|
||||
INT16 sActualAimTime = CalcAPCostForAiming( pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime );
|
||||
INT16 sActualAimAP = CalcAPCostForAiming( pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime );
|
||||
|
||||
if (ubBestAttackAction == AI_ACTION_FIRE_GUN)
|
||||
{
|
||||
if ( gGameExternalOptions.fEnemyTanksCanMoveInTactical || !ARMED_VEHICLE( 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);
|
||||
}
|
||||
}
|
||||
// add reserve AP code here
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -5625,7 +5612,7 @@ INT16 ubMinAPCost;
|
||||
ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier );
|
||||
|
||||
// HEADROCK HAM 3.6: Use Actual Aiming Time.
|
||||
if (pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimTime + ubBurstAPs )
|
||||
if (pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimAP + ubBurstAPs )
|
||||
{
|
||||
// Base chance of bursting is 25% if best shot was +0 aim, down to 8% at +4
|
||||
if ( ARMED_VEHICLE( pSoldier ) )
|
||||
@@ -5677,7 +5664,7 @@ INT16 ubMinAPCost;
|
||||
|
||||
if ( (INT32) PreRandom( 100 ) < iChance)
|
||||
{
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimTime;//dnl ch58 130913
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimAP;//dnl ch58 130913
|
||||
// check for spread burst possibilities
|
||||
if (pSoldier->aiData.bAttitude != ATTACKSLAYONLY)
|
||||
{
|
||||
@@ -5706,28 +5693,28 @@ L_NEWAIM:
|
||||
dTotalRecoil += AICalcRecoilForShot( pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire );
|
||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
||||
}
|
||||
while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs + sActualAimTime && pSoldier->inv[ BestAttack.bWeaponIn ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && dTotalRecoil <= 10.0f );//dnl ch64 260813 pSoldier->ubAttackingHand is wrong because decision is to use BestAttack.bWeaponIn
|
||||
while( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs + sActualAimAP && pSoldier->inv[ BestAttack.bWeaponIn ][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && dTotalRecoil <= 10.0f );//dnl ch64 260813 pSoldier->ubAttackingHand is wrong because decision is to use BestAttack.bWeaponIn
|
||||
} else {
|
||||
do
|
||||
{
|
||||
pSoldier->bDoAutofire++;
|
||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
||||
}
|
||||
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
|
||||
while( pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs + sActualAimAP && 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);
|
||||
sActualAimAP = 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 + sActualAimTime + ubBurstAPs )
|
||||
if (pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimAP + ubBurstAPs )
|
||||
{
|
||||
// Base chance of bursting is 25% if best shot was +0 aim, down to 8% at +4
|
||||
if ( ARMED_VEHICLE( pSoldier ) )
|
||||
@@ -5791,17 +5778,15 @@ L_NEWAIM:
|
||||
if(Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto)
|
||||
iChance = 35;
|
||||
}
|
||||
if((INT32)PreRandom(100) < iChance && pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimTime))
|
||||
if((INT32)PreRandom(100) < iChance && pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimAP))
|
||||
{
|
||||
// Try short autofire to enhance chance of hitting
|
||||
pSoldier->bDoAutofire = 4;
|
||||
BestAttack.ubAPCost += ubHalfBurstAPs + sActualAimTime;
|
||||
//SendFmtMsg("HALF-Auto=%d ubAPCost=%d iChance=%d ubBurstAPs=%d,%d", pSoldier->bDoAutofire, BestAttack.ubAPCost, iChance, ubHalfBurstAPs, sActualAimTime);
|
||||
BestAttack.ubAPCost += ubHalfBurstAPs + sActualAimAP;
|
||||
}
|
||||
else
|
||||
{
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimTime;
|
||||
//SendFmtMsg("FULL-Auto=%d ubAPCost=%d iChance=%d ubBurstAPs=%d,%d", pSoldier->bDoAutofire, BestAttack.ubAPCost, iChance, ubBurstAPs, sActualAimTime);
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimAP;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5960,86 +5945,62 @@ L_NEWAIM:
|
||||
pSoldier->bWeaponMode = WM_BURST;
|
||||
}
|
||||
|
||||
if (fChangeStanceFirst)
|
||||
{ // currently only for guns...
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_FIRE_GUN;
|
||||
pSoldier->aiData.usNextActionData = BestAttack.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestAttack.bTargetLevel;
|
||||
pSoldier->aiData.usActionData = ubBestStance;
|
||||
return( AI_ACTION_CHANGE_STANCE );
|
||||
if (ubBestAttackAction == AI_ACTION_FIRE_GUN)
|
||||
{
|
||||
if (gAnimControl[pSoldier->usAnimState].ubEndHeight != BestAttack.ubStance &&
|
||||
IsValidStance(pSoldier, BestAttack.ubStance))
|
||||
{
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_FIRE_GUN;
|
||||
pSoldier->aiData.usNextActionData = BestAttack.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestAttack.bTargetLevel;
|
||||
pSoldier->aiData.usActionData = BestAttack.ubStance;
|
||||
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before shooting"));
|
||||
return(AI_ACTION_CHANGE_STANCE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestAttack.sTarget;
|
||||
pSoldier->bTargetLevel = BestAttack.bTargetLevel;
|
||||
return(AI_ACTION_FIRE_GUN);
|
||||
}
|
||||
}
|
||||
else if (ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE)
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("toss attack, disable burst/autofire"));
|
||||
pSoldier->bDoBurst = 0;
|
||||
pSoldier->bDoAutofire = 0;
|
||||
|
||||
if (IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS])) //dnl ch63 240813
|
||||
{
|
||||
DebugAI(AI_MSG_INFO, pSoldier, String("using attached GL"));
|
||||
pSoldier->bWeaponMode = WM_ATTACHED_GL;
|
||||
}
|
||||
|
||||
// stand up before throwing if needed
|
||||
if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestAttack.ubStance &&
|
||||
pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestAttack.sTarget), BestAttack.ubStance))
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestAttack.ubStance;
|
||||
pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE;
|
||||
pSoldier->aiData.usNextActionData = BestAttack.sTarget;
|
||||
pSoldier->aiData.bNextTargetLevel = BestAttack.bTargetLevel;
|
||||
return AI_ACTION_CHANGE_STANCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.usActionData = BestAttack.sTarget;
|
||||
pSoldier->bTargetLevel = BestAttack.bTargetLevel;
|
||||
return(AI_ACTION_TOSS_PROJECTILE);
|
||||
}
|
||||
}
|
||||
// other attacks
|
||||
else
|
||||
{
|
||||
|
||||
pSoldier->aiData.usActionData = BestAttack.sTarget;
|
||||
pSoldier->bTargetLevel = BestAttack.bTargetLevel;
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR tempstr="";
|
||||
sprintf( tempstr,
|
||||
"%d(%s) %s %d(%s) at gridno %d (%d APs aim)\n",
|
||||
pSoldier->ubID,pSoldier->name,
|
||||
(ubBestAttackAction == AI_ACTION_FIRE_GUN)?"SHOOTS":((ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE)?"TOSSES AT":"STABS"),
|
||||
BestAttack.ubOpponent,pSoldier->name ,
|
||||
BestAttack.sTarget,BestAttack.ubAimTime );
|
||||
DebugAI( tempstr);
|
||||
#endif
|
||||
|
||||
//DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( pSoldier, BestAttack.bWeaponIn, FALSE ),pSoldier->aiData.bRTPCombat,ubBestAttackAction ));
|
||||
//should be a bug
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("DecideActionBlack: Check for GL Bursts, is launcher capable? = %d, rtpcombat? = %d, bestattackaction = %d",IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier ),pSoldier->aiData.bRTPCombat,ubBestAttackAction ));
|
||||
if (ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE && (Item[pSoldier->inv[BestAttack.bWeaponIn].usItem].usItemClass == IC_LAUNCHER && IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier )) &&
|
||||
(pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.bRTPCombat == RTP_COMBAT_AGGRESSIVE) )
|
||||
{
|
||||
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing burst calc");
|
||||
ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier );
|
||||
|
||||
if ( (pSoldier->bActionPoints - BestAttack.ubAPCost) >= ubBurstAPs )
|
||||
{
|
||||
// Base chance of bursting is 25% if best shot was +0 aim, down to 8% at +4
|
||||
if ( ARMED_VEHICLE( pSoldier ) )
|
||||
{
|
||||
iChance = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
iChance = 50;
|
||||
switch (pSoldier->aiData.bAttitude)
|
||||
{
|
||||
case DEFENSIVE: iChance += -5; break;
|
||||
case BRAVESOLO: iChance += 5; break;
|
||||
case BRAVEAID: iChance += 5; break;
|
||||
case CUNNINGSOLO: iChance += 0; break;
|
||||
case CUNNINGAID: iChance += 0; break;
|
||||
case AGGRESSIVE: iChance += 10; break;
|
||||
case ATTACKSLAYONLY:iChance += 30; break;
|
||||
}
|
||||
// increase chance based on proximity and difficulty of enemy
|
||||
DebugMsg(TOPIC_JA2AI,DBG_LEVEL_3,String("DecideActionBlack: check chance to gl burst"));
|
||||
iChance += ( 15 - PythSpacesAway( pSoldier->sGridNo, BestAttack.sTarget ) ) * ( 1 + SoldierDifficultyLevel( pSoldier ) );
|
||||
}
|
||||
|
||||
if ( (INT32) PreRandom( 100 ) < iChance)
|
||||
{
|
||||
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "DecideActionBlack: Doing GL burst");
|
||||
BestAttack.ubAPCost = BestAttack.ubAPCost + CalcAPsToBurst( pSoldier->CalcActionPoints(), &(pSoldier->inv[HANDPOS]), pSoldier );
|
||||
// check for spread burst possibilities
|
||||
if (pSoldier->aiData.bAttitude != ATTACKSLAYONLY)
|
||||
{
|
||||
CalcSpreadBurst( pSoldier, BestAttack.sTarget, BestAttack.bTargetLevel );
|
||||
}
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ubBestAttackAction == AI_ACTION_TOSS_PROJECTILE && IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS]))//dnl ch63 240813
|
||||
pSoldier->bWeaponMode = WM_ATTACHED_GL;
|
||||
return(ubBestAttackAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// end of tank AI
|
||||
@@ -8710,7 +8671,6 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier )
|
||||
}
|
||||
|
||||
ATTACKTYPE BestShot, BestThrow, BestAttack;//dnl ch69 150913
|
||||
UINT8 ubBestStance = 1;
|
||||
BOOLEAN fClimb;
|
||||
INT16 ubBurstAPs;
|
||||
UINT8 ubOpponentDir;
|
||||
@@ -9199,7 +9159,7 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier )
|
||||
pSoldier->bDoBurst = 0;
|
||||
|
||||
// HEADROCK HAM 3.6: bAimTime represents how MANY aiming levels are used, not how much APs they cost necessarily.
|
||||
INT16 sActualAimTime = CalcAPCostForAiming( pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime );
|
||||
INT16 sActualAimAP = CalcAPCostForAiming( pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime );
|
||||
|
||||
if ( ubBestAttackAction == AI_ACTION_FIRE_GUN )
|
||||
{
|
||||
@@ -9231,13 +9191,13 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier )
|
||||
ubBurstAPs = CalcAPsToBurst( pSoldier->CalcActionPoints( ), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier );
|
||||
|
||||
// HEADROCK HAM 3.6: Use Actual Aiming Time.
|
||||
if ( pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimTime + ubBurstAPs )
|
||||
if ( pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimAP + ubBurstAPs )
|
||||
{
|
||||
iChance = 100;
|
||||
|
||||
if ( (INT32)PreRandom( 100 ) < iChance )
|
||||
{
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimTime;//dnl ch58 130913
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimAP;//dnl ch58 130913
|
||||
// check for spread burst possibilities
|
||||
if ( pSoldier->aiData.bAttitude != ATTACKSLAYONLY )
|
||||
{
|
||||
@@ -9265,28 +9225,28 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier )
|
||||
pSoldier->bDoAutofire++;
|
||||
dTotalRecoil += AICalcRecoilForShot( pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire );
|
||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints( ), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
||||
} while ( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs + sActualAimTime && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && dTotalRecoil <= 10.0f );//dnl ch64 260813 pSoldier->ubAttackingHand is wrong because decision is to use BestAttack.bWeaponIn
|
||||
} while ( pSoldier->bActionPoints >= BestShot.ubAPCost + ubBurstAPs + sActualAimAP && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft >= pSoldier->bDoAutofire && dTotalRecoil <= 10.0f );//dnl ch64 260813 pSoldier->ubAttackingHand is wrong because decision is to use BestAttack.bWeaponIn
|
||||
}
|
||||
else {
|
||||
do
|
||||
{
|
||||
pSoldier->bDoAutofire++;
|
||||
ubBurstAPs = CalcAPsToAutofire( pSoldier->CalcActionPoints( ), &(pSoldier->inv[BestAttack.bWeaponIn]), pSoldier->bDoAutofire, pSoldier );
|
||||
} 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
|
||||
} while ( pSoldier->bActionPoints >= BestAttack.ubAPCost + ubBurstAPs + sActualAimAP && 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 );
|
||||
sActualAimAP = 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 + sActualAimTime + ubBurstAPs )
|
||||
if ( pSoldier->bActionPoints >= BestAttack.ubAPCost + sActualAimAP + ubBurstAPs )
|
||||
{
|
||||
iChance = 100;
|
||||
|
||||
@@ -9303,17 +9263,15 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier )
|
||||
if ( Weapon[pSoldier->inv[BestAttack.bWeaponIn].usItem].NoSemiAuto )
|
||||
iChance = 35;
|
||||
}
|
||||
if ( (INT32)PreRandom( 100 ) < iChance && pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimTime) )
|
||||
if ( (INT32)PreRandom( 100 ) < iChance && pSoldier->bActionPoints > (2 * BestAttack.ubAPCost + ubHalfBurstAPs + sActualAimAP) )
|
||||
{
|
||||
// Try short autofire to enhance chance of hitting
|
||||
pSoldier->bDoAutofire = 4;
|
||||
BestAttack.ubAPCost += ubHalfBurstAPs + sActualAimTime;
|
||||
//SendFmtMsg("HALF-Auto=%d ubAPCost=%d iChance=%d ubBurstAPs=%d,%d", pSoldier->bDoAutofire, BestAttack.ubAPCost, iChance, ubHalfBurstAPs, sActualAimTime);
|
||||
BestAttack.ubAPCost += ubHalfBurstAPs + sActualAimAP;
|
||||
}
|
||||
else
|
||||
{
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimTime;
|
||||
//SendFmtMsg("FULL-Auto=%d ubAPCost=%d iChance=%d ubBurstAPs=%d,%d", pSoldier->bDoAutofire, BestAttack.ubAPCost, iChance, ubBurstAPs, sActualAimTime);
|
||||
BestAttack.ubAPCost += ubBurstAPs + sActualAimAP;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+28
-1
@@ -285,7 +285,9 @@ UINT8 GetClosestWoundedSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 au
|
||||
UINT8 GetClosestMedicSoldierID( SOLDIERTYPE * pSoldier, INT16 aRange, UINT8 auTeam );
|
||||
|
||||
// sevenfm:
|
||||
|
||||
BOOLEAN NightLight(void);
|
||||
BOOLEAN DuskLight(void);
|
||||
BOOLEAN InSmokeNearby(INT32 sGridNo, INT8 bLevel);
|
||||
INT16 MaxNormalVisionDistance( void );
|
||||
UINT8 MinFlankDirections( SOLDIERTYPE *pSoldier );
|
||||
UINT8 CountFriendsInDirection( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo );
|
||||
@@ -294,6 +296,13 @@ 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 );
|
||||
UINT8 CountTeamUnderAttack(INT8 bTeam, INT32 sGridNo, INT16 sDistance);
|
||||
|
||||
UINT8 SectorCurfew(BOOLEAN fNight);
|
||||
UINT8 TeamPercentKilled(INT8 bTeam);
|
||||
BOOLEAN TeamHighPercentKilled(INT8 bTeam);
|
||||
UINT8 ArmyPercentKilled(void);
|
||||
UINT8 ArmyPercentKilledTolerance(void);
|
||||
|
||||
BOOLEAN EnemySeenSoldierRecently( SOLDIERTYPE *pSoldier, UINT8 ubMax = SEEN_3_TURNS_AGO );
|
||||
UINT8 CountTeamSeeSoldier( INT8 bTeam, SOLDIERTYPE *pSoldier );
|
||||
@@ -364,6 +373,24 @@ BOOLEAN AICheckIsGLOperator(SOLDIERTYPE *pSoldier);
|
||||
BOOLEAN AICheckIsOfficer(SOLDIERTYPE *pSoldier);
|
||||
BOOLEAN AICheckIsCommander(SOLDIERTYPE *pSoldier);
|
||||
|
||||
// *************************************************************
|
||||
// Knowledge functions
|
||||
// *************************************************************
|
||||
|
||||
INT8 Knowledge(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
INT32 KnownLocation(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
INT8 KnownLevel(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
|
||||
BOOLEAN UsePersonalKnowledge(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
|
||||
INT8 PersonalKnowledge(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
INT32 KnownPersonalLocation(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
INT8 KnownPersonalLevel(SOLDIERTYPE *pSoldier, UINT8 ubOpponentID);
|
||||
|
||||
INT8 PublicKnowledge(UINT8 bTeam, UINT8 ubOpponentID);
|
||||
INT32 KnownPublicLocation(UINT8 bTeam, UINT8 ubOpponentID);
|
||||
INT8 KnownPublicLevel(UINT8 bTeam, UINT8 ubOpponentID);
|
||||
|
||||
#define MAX_FLANKS_RED 25
|
||||
#define MAX_FLANKS_YELLOW 25
|
||||
|
||||
|
||||
Reference in New Issue
Block a user