mirror of
https://github.com/1dot13/source.git
synced 2026-09-16 14:47:17 +02:00
Migrate +AI features
* Drink canteen * Jump through window * Use explosives * Use wirecutters
This commit is contained in:
@@ -758,6 +758,29 @@ void EatFromInventory( SOLDIERTYPE *pSoldier, BOOLEAN fcanteensonly )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrinkFromInventory(SOLDIERTYPE* pSoldier)
|
||||||
|
{
|
||||||
|
if ( !pSoldier )
|
||||||
|
return;
|
||||||
|
|
||||||
|
INT8 invsize = pSoldier->inv.size();
|
||||||
|
|
||||||
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||||
|
{
|
||||||
|
if ( pSoldier->inv[bLoop].exists() && ItemIsCanteen(pSoldier->inv[bLoop].usItem) )
|
||||||
|
{
|
||||||
|
OBJECTTYPE* pObj = &(pSoldier->inv[bLoop]);
|
||||||
|
|
||||||
|
if ( pObj && TotalPoints(pObj) > 1 )
|
||||||
|
{
|
||||||
|
//ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_DRINKS], pSoldier->GetName());
|
||||||
|
ApplyConsumable(pSoldier, pObj, false, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HourlyFoodUpdate( void )
|
void HourlyFoodUpdate( void )
|
||||||
{
|
{
|
||||||
SoldierID bMercID, bLastTeamID;
|
SoldierID bMercID, bLastTeamID;
|
||||||
|
|||||||
@@ -108,5 +108,6 @@ void SoldierAutoFillCanteens(SOLDIERTYPE *pSoldier);
|
|||||||
BOOLEAN HasFoodInInventory( SOLDIERTYPE *pSoldier, BOOLEAN fCheckFood, BOOLEAN fCheckDrink );
|
BOOLEAN HasFoodInInventory( SOLDIERTYPE *pSoldier, BOOLEAN fCheckFood, BOOLEAN fCheckDrink );
|
||||||
|
|
||||||
void DrinkFromWaterTap( SOLDIERTYPE *pSoldier );
|
void DrinkFromWaterTap( SOLDIERTYPE *pSoldier );
|
||||||
|
void DrinkFromInventory(SOLDIERTYPE* pSoldier);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12426,6 +12426,51 @@ INT8 FindCamoKit( SOLDIERTYPE * pSoldier )
|
|||||||
return( NO_SLOT );
|
return( NO_SLOT );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INT8 FindCanteen(SOLDIERTYPE* pSoldier)
|
||||||
|
{
|
||||||
|
const INT8 invsize = pSoldier->inv.size();
|
||||||
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||||
|
{
|
||||||
|
if ( pSoldier->inv[bLoop].exists() && ItemIsCanteen(pSoldier->inv[bLoop].usItem) )
|
||||||
|
{
|
||||||
|
OBJECTTYPE* pObj = &(pSoldier->inv[bLoop]);
|
||||||
|
if ( pObj && TotalPoints(pObj) > 1 )
|
||||||
|
{
|
||||||
|
return(bLoop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(NO_SLOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
INT8 FindWirecutters(SOLDIERTYPE* pSoldier)
|
||||||
|
{
|
||||||
|
const INT8 invsize = pSoldier->inv.size();
|
||||||
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||||
|
{
|
||||||
|
if ( pSoldier->inv[bLoop].exists() && ItemIsWirecutters(pSoldier->inv[bLoop].usItem) )
|
||||||
|
{
|
||||||
|
return(bLoop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_SLOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT8 FindTNT(SOLDIERTYPE* pSoldier)
|
||||||
|
{
|
||||||
|
const INT8 invsize = pSoldier->inv.size();
|
||||||
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop )
|
||||||
|
{
|
||||||
|
if ( pSoldier->inv[bLoop].exists() && ItemIsTNT(pSoldier->inv[bLoop].usItem) )
|
||||||
|
{
|
||||||
|
return(bLoop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_SLOT;
|
||||||
|
}
|
||||||
|
|
||||||
//JMich_SkillModifiers: Adding a function to see if we have an item with disarm bonus
|
//JMich_SkillModifiers: Adding a function to see if we have an item with disarm bonus
|
||||||
INT8 FindDisarmKit( SOLDIERTYPE * pSoldier )
|
INT8 FindDisarmKit( SOLDIERTYPE * pSoldier )
|
||||||
{
|
{
|
||||||
@@ -16045,6 +16090,20 @@ BOOLEAN FindAttachmentRange(UINT16 usAttachment, UINT32* pStartIndex, UINT32* pE
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOLEAN ItemIsTNT(UINT16 usItem)
|
||||||
|
{
|
||||||
|
if ( Item[usItem].usItemClass == IC_BOMB &&
|
||||||
|
!ItemIsMine(usItem) &&
|
||||||
|
!ItemIsTripwire(usItem) &&
|
||||||
|
Explosive[Item[usItem].ubClassIndex].ubType == EXPLOSV_NORMAL &&
|
||||||
|
Explosive[Item[usItem].ubClassIndex].ubDamage > 40 &&
|
||||||
|
GetLauncherFromLaunchable(usItem) == NOTHING )
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
// Item flagmask utility functions
|
// Item flagmask utility functions
|
||||||
// Just to improve readability
|
// Just to improve readability
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ BOOLEAN ItemIsOnlyInDisease(UINT16 usItem);
|
|||||||
BOOLEAN ItemProvidesRobotCamo(UINT16 usItem);
|
BOOLEAN ItemProvidesRobotCamo(UINT16 usItem);
|
||||||
BOOLEAN ItemProvidesRobotNightvision(UINT16 usItem);
|
BOOLEAN ItemProvidesRobotNightvision(UINT16 usItem);
|
||||||
BOOLEAN ItemProvidesRobotLaserBonus(UINT16 usItem);
|
BOOLEAN ItemProvidesRobotLaserBonus(UINT16 usItem);
|
||||||
|
BOOLEAN ItemIsTNT(UINT16 usItem);
|
||||||
|
|
||||||
//Existing functions without header def's, added them here, just incase I'll need to call
|
//Existing functions without header def's, added them here, just incase I'll need to call
|
||||||
//them from the editor.
|
//them from the editor.
|
||||||
@@ -479,9 +480,13 @@ INT8 FindFirstAidKit( SOLDIERTYPE * pSoldier );
|
|||||||
INT8 FindDisarmKit( SOLDIERTYPE * pSoldier ); //JMich_SkillsModifiers: Added function to check for disarm bonus
|
INT8 FindDisarmKit( SOLDIERTYPE * pSoldier ); //JMich_SkillsModifiers: Added function to check for disarm bonus
|
||||||
INT8 FindLocksmithKit( SOLDIERTYPE * pSoldier );
|
INT8 FindLocksmithKit( SOLDIERTYPE * pSoldier );
|
||||||
INT8 FindCamoKit( SOLDIERTYPE * pSoldier );
|
INT8 FindCamoKit( SOLDIERTYPE * pSoldier );
|
||||||
|
INT8 FindCanteen(SOLDIERTYPE* pSoldier);
|
||||||
INT8 FindWalkman( SOLDIERTYPE * pSoldier );
|
INT8 FindWalkman( SOLDIERTYPE * pSoldier );
|
||||||
INT8 FindTrigger( SOLDIERTYPE * pSoldier );
|
INT8 FindTrigger( SOLDIERTYPE * pSoldier );
|
||||||
INT8 FindRemoteControl( SOLDIERTYPE * pSoldier );
|
INT8 FindRemoteControl( SOLDIERTYPE * pSoldier );
|
||||||
|
INT8 FindWirecutters(SOLDIERTYPE* pSoldier);
|
||||||
|
INT8 FindTNT(SOLDIERTYPE* pSoldier);
|
||||||
|
|
||||||
INT16 GetWornCamo( SOLDIERTYPE * pSoldier );
|
INT16 GetWornCamo( SOLDIERTYPE * pSoldier );
|
||||||
INT16 GetCamoBonus( OBJECTTYPE * pObj );
|
INT16 GetCamoBonus( OBJECTTYPE * pObj );
|
||||||
INT16 GetWornStealth( SOLDIERTYPE * pSoldier );
|
INT16 GetWornStealth( SOLDIERTYPE * pSoldier );
|
||||||
|
|||||||
+33
-5
@@ -5031,11 +5031,11 @@ BOOLEAN NewOKDestination( SOLDIERTYPE * pCurrSoldier, INT32 sGridNo, BOOLEAN fPe
|
|||||||
BOOLEAN fOKCheckStruct;
|
BOOLEAN fOKCheckStruct;
|
||||||
|
|
||||||
// Allow civilians and NPCs with profile to go off screen, and also enemies if tactical retreat is enabled
|
// Allow civilians and NPCs with profile to go off screen, and also enemies if tactical retreat is enabled
|
||||||
auto destinationOffscreen = !(GridNoOnVisibleWorldTile(sGridNo));
|
const auto destinationOffscreen = !(GridNoOnVisibleWorldTile(sGridNo));
|
||||||
auto hasProfile = pCurrSoldier->ubProfile != NO_PROFILE;
|
const auto hasProfile = pCurrSoldier->ubProfile != NO_PROFILE;
|
||||||
auto isCivilian = pCurrSoldier->bTeam == CIV_TEAM;
|
const auto isCivilian = pCurrSoldier->bTeam == CIV_TEAM;
|
||||||
auto isEnemy = pCurrSoldier->bTeam == ENEMY_TEAM;
|
const auto isEnemy = pCurrSoldier->bTeam == ENEMY_TEAM;
|
||||||
auto retreatAllowed = gGameExternalOptions.fAITacticalRetreat == true;
|
const auto retreatAllowed = gGameExternalOptions.fAITacticalRetreat == TRUE;
|
||||||
|
|
||||||
if (destinationOffscreen && !(isCivilian || hasProfile || (isEnemy && retreatAllowed)))
|
if (destinationOffscreen && !(isCivilian || hasProfile || (isEnemy && retreatAllowed)))
|
||||||
{
|
{
|
||||||
@@ -5400,6 +5400,34 @@ BOOLEAN TeamMemberNear( INT8 bTeam, INT32 sGridNo, INT32 iRange )
|
|||||||
return(FALSE);
|
return(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOLEAN NotDeafTeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange)
|
||||||
|
{
|
||||||
|
for ( SoldierID pSoldier = gTacticalStatus.Team[bTeam].bFirstID; pSoldier <= gTacticalStatus.Team[bTeam].bLastID; ++pSoldier )
|
||||||
|
{
|
||||||
|
if ( pSoldier->bActive &&
|
||||||
|
pSoldier->bInSector &&
|
||||||
|
pSoldier->stats.bLife >= OKLIFE &&
|
||||||
|
pSoldier->bDeafenedCounter == 0 )
|
||||||
|
{
|
||||||
|
if ( PythSpacesAway(pSoldier->sGridNo, sGridNo) <= iRange )
|
||||||
|
{
|
||||||
|
return(TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN PlayerCanHearNoise(SOLDIERTYPE* pSoldier)
|
||||||
|
{
|
||||||
|
if ( pSoldier &&
|
||||||
|
(pSoldier->bVisible == TRUE || NotDeafTeamMemberNear(gbPlayerNum, pSoldier->sGridNo, TACTICAL_RANGE / 2) || NightLight() && !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) && NotDeafTeamMemberNear(gbPlayerNum, pSoldier->sGridNo, TACTICAL_RANGE)) )
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
INT32 FindAdjacentGridEx( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 *pubDirection, INT32 *psAdjustedGridNo, BOOLEAN fForceToPerson, BOOLEAN fDoor, bool allow_diagonal )
|
INT32 FindAdjacentGridEx( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 *pubDirection, INT32 *psAdjustedGridNo, BOOLEAN fForceToPerson, BOOLEAN fDoor, bool allow_diagonal )
|
||||||
{
|
{
|
||||||
// psAdjustedGridNo gets the original gridno or the new one if updated
|
// psAdjustedGridNo gets the original gridno or the new one if updated
|
||||||
|
|||||||
+3
-1
@@ -261,6 +261,9 @@ void SelectNextAvailSoldier( SOLDIERTYPE *pSoldier );
|
|||||||
BOOLEAN TeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange);
|
BOOLEAN TeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange);
|
||||||
BOOLEAN IsValidTargetMerc( SoldierID ubSoldierID );
|
BOOLEAN IsValidTargetMerc( SoldierID ubSoldierID );
|
||||||
|
|
||||||
|
BOOLEAN NotDeafTeamMemberNear(INT8 bTeam, INT32 sGridNo, INT32 iRange);
|
||||||
|
BOOLEAN PlayerCanHearNoise(SOLDIERTYPE* pSoldier);
|
||||||
|
|
||||||
// FUNCTIONS FOR MANIPULATING MERC SLOTS - A LIST OF ALL ACTIVE MERCS
|
// FUNCTIONS FOR MANIPULATING MERC SLOTS - A LIST OF ALL ACTIVE MERCS
|
||||||
INT32 GetFreeMercSlot( );
|
INT32 GetFreeMercSlot( );
|
||||||
INT32 AddMercSlot( SOLDIERTYPE *pSoldier );
|
INT32 AddMercSlot( SOLDIERTYPE *pSoldier );
|
||||||
@@ -433,4 +436,3 @@ BOOLEAN IsFreeSlotAvailable( int aTeam );
|
|||||||
void AttemptToCapturePlayerSoldiers();
|
void AttemptToCapturePlayerSoldiers();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -9700,6 +9700,85 @@ void SOLDIERTYPE::BeginSoldierClimbWallUp( void )
|
|||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void SOLDIERTYPE::BeginSoldierJumpWindowAI(void)
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, this, String("check if we can jump through window"));
|
||||||
|
|
||||||
|
//UINT8 ubDirection = this->aiData.usActionData;
|
||||||
|
UINT8 ubDirection = this->ubDirection;
|
||||||
|
|
||||||
|
INT32 sWindowGridNo = this->sGridNo;
|
||||||
|
if ( ubDirection == NORTH || ubDirection == WEST )
|
||||||
|
sWindowGridNo = NewGridNo(this->sGridNo, (UINT16)DirectionInc((UINT8)ubDirection));
|
||||||
|
|
||||||
|
DebugAI(AI_MSG_INFO, this, String("sWindowGridNo %d direction %d", sWindowGridNo, ubDirection));
|
||||||
|
|
||||||
|
if (//CheckWindow(this->sGridNo, ubDirection, gGameExternalOptions.fCanJumpThroughClosedWindows) &&
|
||||||
|
IsJumpableWindowPresentAtGridNo(sWindowGridNo, ubDirection, gGameExternalOptions.fCanJumpThroughClosedWindows) &&
|
||||||
|
//FindWindowJumpDirection(this, this->sGridNo, bDirection, &bDirection) &&
|
||||||
|
this->pathing.bLevel == 0 &&
|
||||||
|
(ubDirection == NORTH || ubDirection == EAST || ubDirection == SOUTH || ubDirection == WEST) )
|
||||||
|
{
|
||||||
|
// Flugente: if we are jumping through an intact window, smash it during our animation
|
||||||
|
if ( gGameExternalOptions.fCanJumpThroughClosedWindows )
|
||||||
|
{
|
||||||
|
// is there really an intact window that we jump through?
|
||||||
|
if ( IsJumpableWindowPresentAtGridNo(sWindowGridNo, ubDirection, TRUE) && !IsJumpableWindowPresentAtGridNo(sWindowGridNo, ubDirection, FALSE) )
|
||||||
|
{
|
||||||
|
STRUCTURE* pStructure = FindStructure(sWindowGridNo, STRUCTURE_WALLNWINDOW);
|
||||||
|
if ( pStructure && !(pStructure->fFlags & STRUCTURE_OPEN) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, this, String("jumping through closed window, damage soldier"));
|
||||||
|
// intact window found. Smash it!
|
||||||
|
WindowHit(sWindowGridNo, pStructure->usStructureID, (ubDirection == SOUTH || ubDirection == EAST), TRUE, PlayerCanHearNoise(this));
|
||||||
|
|
||||||
|
// we get a bit of damage for jumping through a window
|
||||||
|
this->SoldierTakeDamage(0, 2 + 2 * Random(4), 0, 1000, TAKE_DAMAGE_ELECTRICITY, NOBODY, sWindowGridNo, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sTempNewGridNo = NewGridNo(this->sGridNo, (UINT16)DirectionInc(ubDirection));
|
||||||
|
this->flags.fDontChargeTurningAPs = TRUE;
|
||||||
|
EVENT_InternalSetSoldierDesiredDirection(this, ubDirection, FALSE, this->usAnimState);
|
||||||
|
this->flags.fTurningUntilDone = TRUE;
|
||||||
|
// ATE: Reset flag to go back to prone...
|
||||||
|
|
||||||
|
// Flugente: In case an animation is missing (civilian bodytypes), we TELEPORT instead
|
||||||
|
if ( IsAnimationValidForBodyType(this, JUMPWINDOWS) == FALSE )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, this, String("teleport soldier to %d", this->sTempNewGridNo));
|
||||||
|
|
||||||
|
// sevenfm: deduct APs for jumping
|
||||||
|
if ( UsingNewInventorySystem() && FindBackpackOnSoldier(this) != ITEM_NOT_FOUND )
|
||||||
|
DeductPoints(this, GetAPsToJumpThroughWindows(this, TRUE), GetBPsToJumpThroughWindows(this, TRUE), SP_MOVEMENT_INTERRUPT);
|
||||||
|
else
|
||||||
|
DeductPoints(this, GetAPsToJumpThroughWindows(this, FALSE), GetBPsToJumpThroughWindows(this, FALSE), SP_MOVEMENT_INTERRUPT);
|
||||||
|
|
||||||
|
TeleportSoldier(this, this->sTempNewGridNo, TRUE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, this, String("start jumping"));
|
||||||
|
this->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
||||||
|
this->EVENT_InitNewSoldierAnim(JUMPWINDOWS, 0, FALSE);
|
||||||
|
//this->usPendingAnimation = JUMPWINDOWS;
|
||||||
|
//EndAIGuysTurn(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flugente: should be fixed now, re-enable if not
|
||||||
|
// Flugente: if an AI guy, end turn (weird endless clock syndrome)
|
||||||
|
//if ( this->bTeam != OUR_TEAM )
|
||||||
|
//EndAIGuysTurn( this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScreenMsg(FONT_LTRED, MSG_INTERFACE, L"[%d] %s cannot jump", this->ubID, this->GetName());
|
||||||
|
DebugAI(AI_MSG_INFO, this, String("CancelAIAction: cannot jump"));
|
||||||
|
CancelAIAction(this, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier )
|
UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier )
|
||||||
{
|
{
|
||||||
UINT32 uiChance;
|
UINT32 uiChance;
|
||||||
@@ -26297,3 +26376,13 @@ void SOLDIERTYPE::InitializeExtraData(void)
|
|||||||
|
|
||||||
this->delayedDamageFunction = nullptr;
|
this->delayedDamageFunction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINT8 SOLDIERTYPE::AnimHeight(void) const
|
||||||
|
{
|
||||||
|
return gAnimControl[this->usAnimState].ubHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 SOLDIERTYPE::AnimEndHeight(void) const
|
||||||
|
{
|
||||||
|
return gAnimControl[this->usAnimState].ubEndHeight;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1788,10 +1788,13 @@ public:
|
|||||||
void ChangeToFallbackAnimation( UINT8 fallBackDirection );
|
void ChangeToFallbackAnimation( UINT8 fallBackDirection );
|
||||||
|
|
||||||
// sevenfm
|
// sevenfm
|
||||||
|
void BeginSoldierJumpWindowAI(void);
|
||||||
void BreakWindow(void);
|
void BreakWindow(void);
|
||||||
BOOLEAN CanBreakWindow(void);
|
BOOLEAN CanBreakWindow(void);
|
||||||
BOOLEAN CanStartDrag(void);
|
BOOLEAN CanStartDrag(void);
|
||||||
void StartDrag(void);
|
void StartDrag(void);
|
||||||
|
UINT8 AnimHeight(void) const;
|
||||||
|
UINT8 AnimEndHeight(void) const;
|
||||||
|
|
||||||
void UpdateRobotControllerGivenController( void );
|
void UpdateRobotControllerGivenController( void );
|
||||||
void UpdateRobotControllerGivenRobot( void );
|
void UpdateRobotControllerGivenRobot( void );
|
||||||
|
|||||||
@@ -6089,7 +6089,7 @@ void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, Sol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce )
|
void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce, BOOLEAN fSound)
|
||||||
{
|
{
|
||||||
STRUCTURE * pWallAndWindow;
|
STRUCTURE * pWallAndWindow;
|
||||||
DB_STRUCTURE * pWallAndWindowInDB;
|
DB_STRUCTURE * pWallAndWindowInDB;
|
||||||
@@ -6210,10 +6210,12 @@ void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, B
|
|||||||
|
|
||||||
pNode = CreateAnimationTile( &AniParams );
|
pNode = CreateAnimationTile( &AniParams );
|
||||||
//ddd window{
|
//ddd window{
|
||||||
CompileWorldMovementCosts();
|
CompileWorldMovementCosts();
|
||||||
//ddd window}
|
//ddd window}
|
||||||
PlayJA2Sample( GLASS_SHATTER1 + Random(2), RATE_11025, MIDVOLUME, 1, SoundDir( sGridNo ) );
|
if ( fSound )
|
||||||
|
{
|
||||||
|
PlayJA2Sample(GLASS_SHATTER1 + Random(2), RATE_11025, MIDVOLUME, 1, SoundDir(sGridNo));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -456,7 +456,7 @@ INT16 ArmourVersusFirePercent( SOLDIERTYPE * pSoldier );
|
|||||||
extern BOOLEAN FireWeapon( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo );
|
extern BOOLEAN FireWeapon( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo );
|
||||||
extern void WeaponHit( SoldierID usSoldierID, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 usDirection, INT16 sXPos, INT16 sYPos, INT16 sZPos, INT16 sRange, SoldierID ubAttackerID, BOOLEAN fHit, UINT8 ubSpecial, UINT8 ubHitLocation );
|
extern void WeaponHit( SoldierID usSoldierID, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 usDirection, INT16 sXPos, INT16 sYPos, INT16 sZPos, INT16 sRange, SoldierID ubAttackerID, BOOLEAN fHit, UINT8 ubSpecial, UINT8 ubHitLocation );
|
||||||
extern void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, SoldierID ubAttackerID, UINT16 sXPos, INT16 sYPos, INT16 sZPos, UINT16 usStructureID, INT32 iImpact, BOOLEAN fStopped );
|
extern void StructureHit( INT32 iBullet, UINT16 usWeaponIndex, INT16 bWeaponStatus, SoldierID ubAttackerID, UINT16 sXPos, INT16 sYPos, INT16 sZPos, UINT16 usStructureID, INT32 iImpact, BOOLEAN fStopped );
|
||||||
extern void WindowHit( INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce );
|
void WindowHit(INT32 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce, BOOLEAN fSound = TRUE);
|
||||||
// HEADROCK HAM 5.1: Moved to Bullets.h
|
// HEADROCK HAM 5.1: Moved to Bullets.h
|
||||||
extern BOOLEAN InRange( SOLDIERTYPE *pSoldier, INT32 sGridNo );
|
extern BOOLEAN InRange( SOLDIERTYPE *pSoldier, INT32 sGridNo );
|
||||||
extern void ShotMiss( SoldierID ubAttackerID, INT32 iBullet );
|
extern void ShotMiss( SoldierID ubAttackerID, INT32 iBullet );
|
||||||
|
|||||||
+92
-1
@@ -49,6 +49,7 @@
|
|||||||
#include "Soldier Functions.h" // added by SANDRO
|
#include "Soldier Functions.h" // added by SANDRO
|
||||||
#include "Text.h" // sevenfm
|
#include "Text.h" // sevenfm
|
||||||
#include "english.h" // sevenfm: for ESC key
|
#include "english.h" // sevenfm: for ESC key
|
||||||
|
#include "Food.h"
|
||||||
|
|
||||||
#include "connect.h"
|
#include "connect.h"
|
||||||
// needed to use the modularized tactical AI:
|
// needed to use the modularized tactical AI:
|
||||||
@@ -2744,6 +2745,30 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
|
|||||||
case AI_ACTION_JUMP_WINDOW:
|
case AI_ACTION_JUMP_WINDOW:
|
||||||
{
|
{
|
||||||
pSoldier->BeginSoldierClimbWindow();
|
pSoldier->BeginSoldierClimbWindow();
|
||||||
|
pSoldier->BeginSoldierJumpWindowAI();
|
||||||
|
if ( gfTurnBasedAI )
|
||||||
|
{
|
||||||
|
//if (pSoldier->bActionPoints >= GetAPsToJumpThroughWindows(pSoldier, FALSE) + GetAPsToLook(pSoldier))
|
||||||
|
if ( pSoldier->bActionPoints >= GetAPsToLook(pSoldier) )
|
||||||
|
{
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_CHANGE_FACING;
|
||||||
|
INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
|
||||||
|
if ( !TileIsOutOfBounds(sClosestOpponent) )
|
||||||
|
pSoldier->aiData.usNextActionData = AIDirection(pSoldier->sGridNo, sClosestOpponent);
|
||||||
|
else
|
||||||
|
pSoldier->aiData.usNextActionData = PreRandom(8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_END_TURN;
|
||||||
|
pSoldier->aiData.usNextActionData = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_WAIT;
|
||||||
|
pSoldier->aiData.usNextActionData = REALTIME_AI_DELAY / 10;
|
||||||
|
}
|
||||||
ActionDone( pSoldier );
|
ActionDone( pSoldier );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2806,7 +2831,73 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case AI_ACTION_DRINK_CANTEEN:
|
||||||
|
DrinkFromInventory(pSoldier);
|
||||||
|
ActionDone(pSoldier);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AI_ACTION_HANDLE_ITEM:
|
||||||
|
iRetCode = HandleItem(pSoldier, pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, pSoldier->inv[HANDPOS].usItem, FALSE);
|
||||||
|
if ( iRetCode != ITEM_HANDLE_OK )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction (AI_ACTION_HANDLE_ITEM): HandleItem error code %d", iRetCode));
|
||||||
|
CancelAIAction(pSoldier, FORCE);
|
||||||
|
EndAIGuysTurn(pSoldier);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AI_ACTION_PLANT_BOMB:
|
||||||
|
if ( pSoldier->inv[HANDPOS].exists() &&
|
||||||
|
(Item[pSoldier->inv[HANDPOS].usItem].usItemClass & IC_BOMB) )
|
||||||
|
{
|
||||||
|
OBJECTTYPE bombobj;
|
||||||
|
INT32 sSpot = pSoldier->sGridNo;
|
||||||
|
|
||||||
|
if ( !TileIsOutOfBounds(sSpot) &&
|
||||||
|
pSoldier->inv[HANDPOS].MoveThisObjectTo(bombobj, 1) == 0 )
|
||||||
|
{
|
||||||
|
bombobj.fFlags |= OBJECT_ARMED_BOMB;
|
||||||
|
bombobj[0]->data.misc.bDetonatorType = BOMB_TIMED;
|
||||||
|
bombobj[0]->data.misc.usBombItem = bombobj.usItem;
|
||||||
|
//bombobj[0]->data.misc.ubBombOwner = pSoldier->ubID + 2;
|
||||||
|
bombobj[0]->data.misc.ubBombOwner = 1;
|
||||||
|
bombobj[0]->data.misc.bDelay = 1 + Random(2);
|
||||||
|
//pSoldier->inv[HANDPOS][0]->data.bTrap = EffectiveExplosive(pSoldier) / 20 + EffectiveExpLevel(pSoldier, TRUE) / 2;
|
||||||
|
pSoldier->inv[HANDPOS][0]->data.bTrap = 6 + SoldierDifficultyLevel(pSoldier);
|
||||||
|
AddItemToPool(sSpot, &bombobj, INVISIBLE, pSoldier->pathing.bLevel, WORLD_ITEM_ARMED_BOMB, 0);
|
||||||
|
NotifySoldiersToLookforItems();
|
||||||
|
DeductPoints(pSoldier, APBPConstants[AP_INVENTORY_ARM] + APBPConstants[AP_DROP_BOMB], APBPConstants[BP_INVENTORY_ARM] + APBPConstants[BP_DROP_BOMB]);
|
||||||
|
if ( gAnimControl[pSoldier->usAnimState].ubHeight == ANIM_STAND )
|
||||||
|
{
|
||||||
|
pSoldier->EVENT_InitNewSoldierAnim(DROP_ITEM, 0, FALSE);
|
||||||
|
}
|
||||||
|
else if ( gAnimControl[pSoldier->usAnimState].ubHeight == ANIM_CROUCH )
|
||||||
|
{
|
||||||
|
pSoldier->EVENT_InitNewSoldierAnim(CUTTING_FENCE, 0, FALSE);
|
||||||
|
}
|
||||||
|
if ( pSoldier->bVisible != -1 )
|
||||||
|
{
|
||||||
|
PlayJA2Sample(THROW_IMPACT_2, RATE_11025, SoundVolume(MIDVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo));
|
||||||
|
}
|
||||||
|
ActionDone(pSoldier);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction (AI_ACTION_PLANT_BOMB): failed to move object"));
|
||||||
|
CancelAIAction(pSoldier, FORCE);
|
||||||
|
EndAIGuysTurn(pSoldier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction (AI_ACTION_PLANT_BOMB): failed to find bomb in hand"));
|
||||||
|
CancelAIAction(pSoldier, FORCE);
|
||||||
|
EndAIGuysTurn(pSoldier);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
#ifdef BETAVERSION
|
#ifdef BETAVERSION
|
||||||
NumMessage("ExecuteAction - Illegal action type = ",pSoldier->aiData.bAction);
|
NumMessage("ExecuteAction - Illegal action type = ",pSoldier->aiData.bAction);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "SmokeEffects.h" // sevenfm
|
#include "SmokeEffects.h" // sevenfm
|
||||||
|
|
||||||
#include "GameInitOptionsScreen.h"
|
#include "GameInitOptionsScreen.h"
|
||||||
|
#include "Structure Wrap.h"
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// SANDRO - In this file, all APBPConstants[AP_CROUCH] and APBPConstants[AP_PRONE] were changed to GetAPsCrouch() and GetAPsProne()
|
// SANDRO - In this file, all APBPConstants[AP_CROUCH] and APBPConstants[AP_PRONE] were changed to GetAPsCrouch() and GetAPsProne()
|
||||||
@@ -812,6 +813,18 @@ BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier, INT8 bAction)
|
|||||||
bMinPointsNeeded = 20; // TODO
|
bMinPointsNeeded = 20; // TODO
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AI_ACTION_DRINK_CANTEEN:
|
||||||
|
bMinPointsNeeded = APBPConstants[AP_DRINK];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AI_ACTION_HANDLE_ITEM:
|
||||||
|
bMinPointsNeeded = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AI_ACTION_PLANT_BOMB:
|
||||||
|
bMinPointsNeeded = APBPConstants[AP_INVENTORY_ARM] + APBPConstants[AP_DROP_BOMB];
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
#ifdef BETAVERSION
|
#ifdef BETAVERSION
|
||||||
//NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction);
|
//NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction);
|
||||||
@@ -6327,3 +6340,221 @@ BOOLEAN CheckSuppressionDirection(SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, IN
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINT8 CountKnownEnemies(SOLDIERTYPE* pSoldier, INT32 sSpot, INT16 sDistance, INT8 bLevel)
|
||||||
|
{
|
||||||
|
CHECKF(pSoldier);
|
||||||
|
|
||||||
|
SOLDIERTYPE* pOpponent;
|
||||||
|
INT32 sThreatLoc;
|
||||||
|
INT8 bThreatLevel;
|
||||||
|
UINT8 ubNum = 0;
|
||||||
|
|
||||||
|
// loop through all the enemies
|
||||||
|
for ( UINT32 uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop )
|
||||||
|
{
|
||||||
|
pOpponent = MercSlots[uiLoop];
|
||||||
|
|
||||||
|
// if this merc is inactive, at base, on assignment, dead, unconscious
|
||||||
|
if ( !pOpponent || pOpponent->stats.bLife < OKLIFE )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ValidOpponent(pSoldier, pOpponent) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check knowledge
|
||||||
|
if ( Knowledge(pSoldier, pOpponent->ubID) == NOT_HEARD_OR_SEEN )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sThreatLoc = KnownLocation(pSoldier, pOpponent->ubID);
|
||||||
|
bThreatLevel = KnownLevel(pSoldier, pOpponent->ubID);
|
||||||
|
|
||||||
|
if ( TileIsOutOfBounds(sThreatLoc) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( PythSpacesAway(sSpot, sThreatLoc) > sDistance )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bLevel >= 0 && bThreatLevel != bLevel )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ubNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CountKnownEnemiesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom)
|
||||||
|
{
|
||||||
|
CHECKF(pSoldier);
|
||||||
|
|
||||||
|
UINT8 ubNum = 0;
|
||||||
|
for ( UINT32 uiLoop = 0; uiLoop < guiNumMercSlots; ++uiLoop )
|
||||||
|
{
|
||||||
|
SOLDIERTYPE* pOpponent = MercSlots[uiLoop];
|
||||||
|
|
||||||
|
// if this merc is inactive, at base, on assignment, dead, unconscious
|
||||||
|
if ( !pOpponent || pOpponent->stats.bLife < OKLIFE )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ValidOpponent(pSoldier, pOpponent) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check public knowledge
|
||||||
|
if ( Knowledge(pSoldier, pOpponent->ubID) == NOT_HEARD_OR_SEEN )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 sThreatLoc = KnownLocation(pSoldier, pOpponent->ubID);
|
||||||
|
|
||||||
|
if ( TileIsOutOfBounds(sThreatLoc) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check room
|
||||||
|
UINT16 usRoomNo;
|
||||||
|
if ( !InARoom(sThreatLoc, &usRoomNo) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( usRoomNo != usRoom )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ubNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ubNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CountFriendsInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom)
|
||||||
|
{
|
||||||
|
CHECKF(pSoldier);
|
||||||
|
|
||||||
|
SOLDIERTYPE* pFriend;
|
||||||
|
UINT8 ubFriendCount = 0;
|
||||||
|
UINT16 usRoomNo;
|
||||||
|
|
||||||
|
// Run through each friendly.
|
||||||
|
for ( SoldierID iCounter = gTacticalStatus.Team[pSoldier->bTeam].bFirstID; iCounter <= gTacticalStatus.Team[pSoldier->bTeam].bLastID; ++iCounter )
|
||||||
|
{
|
||||||
|
pFriend = iCounter;
|
||||||
|
|
||||||
|
if ( pFriend &&
|
||||||
|
pFriend != pSoldier &&
|
||||||
|
pFriend->bActive &&
|
||||||
|
pFriend->stats.bLife >= OKLIFE &&
|
||||||
|
InARoom(pFriend->sGridNo, &usRoomNo) &&
|
||||||
|
usRoomNo == usRoom )
|
||||||
|
{
|
||||||
|
ubFriendCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ubFriendCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 CountCorpsesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoomNo, INT8 bLevel)
|
||||||
|
{
|
||||||
|
CHECKF(pSoldier);
|
||||||
|
|
||||||
|
ROTTING_CORPSE* pCorpse;
|
||||||
|
INT32 iCount = 0;
|
||||||
|
|
||||||
|
for ( INT32 cnt = 0; cnt < giNumRottingCorpse; ++cnt )
|
||||||
|
{
|
||||||
|
pCorpse = &(gRottingCorpse[cnt]);
|
||||||
|
|
||||||
|
if ( pCorpse &&
|
||||||
|
pCorpse->fActivated &&
|
||||||
|
pCorpse->def.ubType < ROTTING_STAGE2 &&
|
||||||
|
pCorpse->def.ubBodyType <= REGFEMALE &&
|
||||||
|
pCorpse->def.ubAIWarningValue > 0 &&
|
||||||
|
pCorpse->def.bLevel == bLevel &&
|
||||||
|
!TileIsOutOfBounds(pCorpse->def.sGridNo) &&
|
||||||
|
RoomNo(pCorpse->def.sGridNo) == usRoomNo &&
|
||||||
|
(pSoldier->bTeam == ENEMY_TEAM && CorpseEnemyTeam(pCorpse) || pSoldier->bTeam == MILITIA_TEAM && CorpseMilitiaTeam(pCorpse) || pSoldier->bTeam == CIV_TEAM && !pSoldier->aiData.bNeutral) )
|
||||||
|
{
|
||||||
|
iCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN FindFenceAroundSpot(INT32 sSpot)
|
||||||
|
{
|
||||||
|
if ( TileIsOutOfBounds(sSpot) )
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 sTempSpot;
|
||||||
|
|
||||||
|
// check adjacent locations
|
||||||
|
for ( UINT8 ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ubDirection++ )
|
||||||
|
{
|
||||||
|
sTempSpot = NewGridNo(sSpot, DirectionInc(ubDirection));
|
||||||
|
|
||||||
|
if ( sTempSpot != sSpot && IsCuttableWireFenceAtGridNo(sTempSpot) )
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN SameRoom(INT32 sSpot1, INT32 sSpot2)
|
||||||
|
{
|
||||||
|
if ( RoomNo(sSpot1) == RoomNo(sSpot2) && sSpot1 != NO_ROOM )
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT16 RoomNo(INT32 sSpot)
|
||||||
|
{
|
||||||
|
if ( TileIsOutOfBounds(sSpot) )
|
||||||
|
return NO_ROOM;
|
||||||
|
|
||||||
|
return gusWorldRoomInfo[sSpot];
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN CheckWindow(INT32 sSpot, UINT8 ubDirection, BOOLEAN fAllowClosed)
|
||||||
|
{
|
||||||
|
CHECKF(!TileIsOutOfBounds(sSpot));
|
||||||
|
|
||||||
|
// find window spot
|
||||||
|
INT32 sWindowSpot = sSpot;
|
||||||
|
if ( ubDirection == NORTH || ubDirection == WEST )
|
||||||
|
sWindowSpot = NewGridNo(sSpot, (UINT16)DirectionInc(ubDirection));
|
||||||
|
|
||||||
|
//if (IsJumpableWindowPresentAtGridNo(sWindowSpot, ubDirection, gGameExternalOptions.fCanJumpThroughClosedWindows))
|
||||||
|
if ( IsJumpableWindowPresentAtGridNo(sWindowSpot, ubDirection, fAllowClosed) )
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|||||||
+337
-1
@@ -11065,7 +11065,7 @@ static ActionType DecideActionVIPretreat(SOLDIERTYPE* pSoldier, bool logAction)
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// CHANGE STANCE
|
// CHANGE STANCE
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
ActionType DecideActionChangeStance(SOLDIERTYPE* pSoldier, UINT8 ubCanMove, ATTACKTYPE BestAttack, UINT8 ubBestAttackAction, bool logAction)
|
static ActionType DecideActionChangeStance(SOLDIERTYPE* pSoldier, UINT8 ubCanMove, ATTACKTYPE BestAttack, UINT8 ubBestAttackAction, bool logAction)
|
||||||
{
|
{
|
||||||
// if not in water and not already crouched, try to crouch down first
|
// if not in water and not already crouched, try to crouch down first
|
||||||
if (!gfTurnBasedAI || GetAPsToChangeStance(pSoldier, ANIM_CROUCH) <= pSoldier->bActionPoints)
|
if (!gfTurnBasedAI || GetAPsToChangeStance(pSoldier, ANIM_CROUCH) <= pSoldier->bActionPoints)
|
||||||
@@ -11170,6 +11170,180 @@ static ActionType MoveCloserBeforeShooting(SOLDIERTYPE* pSoldier, ATTACKTYPE att
|
|||||||
return(AI_ACTION_INVALID);
|
return(AI_ACTION_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ActionType DecideBlowUpObstacle(SOLDIERTYPE* pSoldier, INT32 sClosestOpponent)
|
||||||
|
{
|
||||||
|
INT8 bTNTSlot = FindTNT(pSoldier);
|
||||||
|
|
||||||
|
if ( bTNTSlot != NO_SLOT &&
|
||||||
|
pSoldier->bTeam == ENEMY_TEAM &&
|
||||||
|
IsActionAffordable(pSoldier, AI_ACTION_PLANT_BOMB) &&
|
||||||
|
pSoldier->pathing.bLevel == 0 &&
|
||||||
|
!TileIsOutOfBounds(sClosestOpponent) &&
|
||||||
|
(Chance(SoldierDifficultyLevel(pSoldier) * 10) ||
|
||||||
|
InARoom(sClosestOpponent, NULL) &&
|
||||||
|
CountFriendsInRoom(pSoldier, RoomNo(sClosestOpponent)) == 0 &&
|
||||||
|
!SameRoom(sClosestOpponent, pSoldier->sGridNo) &&
|
||||||
|
(CountCorpsesInRoom(pSoldier, RoomNo(sClosestOpponent), 0) > 0 || TeamHighPercentKilled(pSoldier->bTeam) || EstimatePathCostToLocation(pSoldier, sClosestOpponent, pSoldier->pathing.bLevel, FALSE, NULL, NULL) == 0)) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("found TNT in slot %d, check to plant bomb", bTNTSlot));
|
||||||
|
|
||||||
|
const INT8 bLevel = pSoldier->pathing.bLevel;
|
||||||
|
UINT8 ubDesiredDir = AIDirection(pSoldier->sGridNo, sClosestOpponent);
|
||||||
|
INT32 sCheckSpot;
|
||||||
|
INT32 sPathCost, sNewPathCost;
|
||||||
|
INT32 sOriginalGridNo;
|
||||||
|
UINT16 usRoom;
|
||||||
|
|
||||||
|
for ( UINT8 ubCheckDir = 0; ubCheckDir < NUM_WORLD_DIRECTIONS; ubCheckDir++ )
|
||||||
|
{
|
||||||
|
INT32 sNewSpot = NewGridNo(pSoldier->sGridNo, DirectionInc(ubCheckDir));
|
||||||
|
INT32 sNextSpot = NewGridNo(sNewSpot, DirectionInc(ubCheckDir));
|
||||||
|
|
||||||
|
if ( sNewSpot == pSoldier->sGridNo ||
|
||||||
|
sNextSpot == sNewSpot ||
|
||||||
|
gpWorldLevelData[sNewSpot].sHeight != gpWorldLevelData[pSoldier->sGridNo].sHeight ||
|
||||||
|
gpWorldLevelData[sNextSpot].sHeight != gpWorldLevelData[pSoldier->sGridNo].sHeight ||
|
||||||
|
//ubCheckDir != ubDesiredDir && ubCheckDir != gOneCDirection[ubDesiredDir] && ubCheckDir != gOneCCDirection[ubDesiredDir] ||
|
||||||
|
gubWorldMovementCosts[sNewSpot][ubCheckDir][bLevel] < TRAVELCOST_BLOCKED )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sCheckSpot = NOWHERE;
|
||||||
|
|
||||||
|
if ( (IsCuttableWireFenceAtGridNo(sNewSpot) && !IsCutWireFenceAtGridNo(sNewSpot) || gubWorldMovementCosts[sNewSpot][ubCheckDir][bLevel] == TRAVELCOST_OBSTACLE) &&
|
||||||
|
IsLocationSittable(sNextSpot, bLevel) && NewOKDestination(pSoldier, sNextSpot, TRUE, bLevel) && !Water(sNextSpot, bLevel) )
|
||||||
|
{
|
||||||
|
// found fence, jump 2 tiles
|
||||||
|
sCheckSpot = sNextSpot;
|
||||||
|
}
|
||||||
|
else if ( (gubWorldMovementCosts[sNewSpot][ubCheckDir][bLevel] == TRAVELCOST_WALL || gubWorldMovementCosts[sNewSpot][ubCheckDir][bLevel] == TRAVELCOST_DOOR) &&
|
||||||
|
IsLocationSittable(sNewSpot, bLevel) && NewOKDestination(pSoldier, sNewSpot, TRUE, bLevel) && !Water(sNewSpot, bLevel) )
|
||||||
|
{
|
||||||
|
// found wall, jump 1 tile
|
||||||
|
sCheckSpot = sNewSpot;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !TileIsOutOfBounds(sCheckSpot) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("check if jumping to %d improves path cost", sCheckSpot));
|
||||||
|
|
||||||
|
// check if removing obstacle improves situation
|
||||||
|
sPathCost = EstimatePlotPath(pSoldier, sClosestOpponent, FALSE, FALSE, FALSE, RUNNING, pSoldier->bStealthMode, FALSE, 0);
|
||||||
|
sOriginalGridNo = pSoldier->sGridNo;
|
||||||
|
pSoldier->sGridNo = sCheckSpot;
|
||||||
|
sNewPathCost = EstimatePlotPath(pSoldier, sClosestOpponent, FALSE, FALSE, FALSE, RUNNING, pSoldier->bStealthMode, FALSE, 0);
|
||||||
|
pSoldier->sGridNo = sOriginalGridNo;
|
||||||
|
|
||||||
|
if ( sNewPathCost > 0 &&
|
||||||
|
(sPathCost == 0 ||
|
||||||
|
sPathCost > sNewPathCost && sPathCost - sNewPathCost > APBPConstants[AP_MAXIMUM] ||
|
||||||
|
InARoom(sCheckSpot, &usRoom) &&
|
||||||
|
!SameRoom(sCheckSpot, pSoldier->sGridNo) &&
|
||||||
|
CountFriendsInRoom(pSoldier, usRoom) == 0 &&
|
||||||
|
CountKnownEnemiesInRoom(pSoldier, usRoom) > 0 &&
|
||||||
|
(CountCorpsesInRoom(pSoldier, usRoom, 0) > 0 || TeamHighPercentKilled(pSoldier->bTeam))) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("blowing up fence/wall/obstacle improves path cost, plant bomb"));
|
||||||
|
|
||||||
|
RearrangePocket(pSoldier, HANDPOS, bTNTSlot, FOREVER);
|
||||||
|
pSoldier->aiData.usActionData = 0;
|
||||||
|
|
||||||
|
return AI_ACTION_PLANT_BOMB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AI_ACTION_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ActionType DecideJumpWindow(SOLDIERTYPE* pSoldier, INT32 sClosestOpponent)
|
||||||
|
{
|
||||||
|
if ( gGameExternalOptions.fCanJumpThroughWindows &&
|
||||||
|
!pSoldier->bBlindedCounter &&
|
||||||
|
!TileIsOutOfBounds(sClosestOpponent) &&
|
||||||
|
pSoldier->pathing.bLevel == 0 &&
|
||||||
|
pSoldier->AnimEndHeight() >= ANIM_CROUCH &&
|
||||||
|
IsActionAffordable(pSoldier, AI_ACTION_JUMP_WINDOW) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("check if we can jump through window"));
|
||||||
|
|
||||||
|
const INT8 bLevel = pSoldier->pathing.bLevel;
|
||||||
|
|
||||||
|
for ( UINT8 ubCheckDir = 0; ubCheckDir < NUM_WORLD_DIRECTIONS; ubCheckDir++ )
|
||||||
|
{
|
||||||
|
// cannot jump diagonally
|
||||||
|
if ( ubCheckDir % 2 != 0 )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 sNewSpot = NewGridNo(pSoldier->sGridNo, DirectionInc(ubCheckDir));
|
||||||
|
|
||||||
|
if ( sNewSpot != pSoldier->sGridNo &&
|
||||||
|
CheckWindow(pSoldier->sGridNo, ubCheckDir, gGameExternalOptions.fCanJumpThroughClosedWindows) &&
|
||||||
|
!Water(sNewSpot, bLevel) &&
|
||||||
|
IsLocationSittable(sNewSpot, bLevel) &&
|
||||||
|
NewOKDestination(pSoldier, sNewSpot, TRUE, bLevel) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("found jumpable window at %d", sNewSpot));
|
||||||
|
|
||||||
|
// check if jumping improves situation
|
||||||
|
INT32 sPathCost = EstimatePlotPath(pSoldier, sClosestOpponent, FALSE, FALSE, FALSE, RUNNING, pSoldier->bStealthMode, FALSE, 0);
|
||||||
|
INT32 sOriginalGridNo = pSoldier->sGridNo;
|
||||||
|
pSoldier->sGridNo = sNewSpot;
|
||||||
|
INT32 sNewPathCost = EstimatePlotPath(pSoldier, sClosestOpponent, FALSE, FALSE, FALSE, RUNNING, pSoldier->bStealthMode, FALSE, 0);
|
||||||
|
pSoldier->sGridNo = sOriginalGridNo;
|
||||||
|
|
||||||
|
if ( sNewPathCost > 0 && (sPathCost == 0 || sPathCost > sNewPathCost && sPathCost - sNewPathCost > APBPConstants[AP_MAXIMUM] || SameRoom(sClosestOpponent, sNewSpot)) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("jumping improves path cost"));
|
||||||
|
|
||||||
|
if ( gfTurnBasedAI && pSoldier->bActionPoints < pSoldier->bInitialActionPoints )
|
||||||
|
{
|
||||||
|
if ( pSoldier->ubDirection != ubCheckDir &&
|
||||||
|
pSoldier->InternalIsValidStance(ubCheckDir, gAnimControl[pSoldier->usAnimState].ubEndHeight) &&
|
||||||
|
pSoldier->bActionPoints >= GetAPsToLook(pSoldier) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("turn to window, end turn before jumping"));
|
||||||
|
pSoldier->aiData.usActionData = ubCheckDir;
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_END_TURN;
|
||||||
|
pSoldier->aiData.usNextActionData = 0;
|
||||||
|
return AI_ACTION_CHANGE_FACING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("already facing window, end turn before jumping"));
|
||||||
|
pSoldier->aiData.usActionData = 0;
|
||||||
|
return AI_ACTION_END_TURN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pSoldier->ubDirection == ubCheckDir )
|
||||||
|
{
|
||||||
|
pSoldier->aiData.usActionData = 0;
|
||||||
|
return AI_ACTION_JUMP_WINDOW;
|
||||||
|
}
|
||||||
|
else if ( pSoldier->InternalIsValidStance(ubCheckDir, gAnimControl[pSoldier->usAnimState].ubEndHeight) &&
|
||||||
|
pSoldier->bActionPoints >= GetAPsToJumpThroughWindows(pSoldier, FALSE) + GetAPsToLook(pSoldier) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("turn before jumping"));
|
||||||
|
pSoldier->aiData.usActionData = ubCheckDir;
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_JUMP_WINDOW;
|
||||||
|
pSoldier->aiData.usNextActionData = 0;
|
||||||
|
return AI_ACTION_CHANGE_FACING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AI_ACTION_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
// Boxer AI decision routines
|
// Boxer AI decision routines
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
@@ -20533,6 +20707,7 @@ INT8 DecideActionBlackSoldier(SOLDIERTYPE* pSoldier)
|
|||||||
INT8 bOpponentLevel;
|
INT8 bOpponentLevel;
|
||||||
INT32 distanceToOpponent;
|
INT32 distanceToOpponent;
|
||||||
INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, &sOpponentGridNo, &bOpponentLevel, NULL, &distanceToOpponent);
|
INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, &sOpponentGridNo, &bOpponentLevel, NULL, &distanceToOpponent);
|
||||||
|
INT32 sClosestSeenOpponent = ClosestSeenOpponent(pSoldier, NULL, NULL);
|
||||||
DebugAI(AI_MSG_INFO, pSoldier, String("sClosestOpponent %d", sClosestOpponent));
|
DebugAI(AI_MSG_INFO, pSoldier, String("sClosestOpponent %d", sClosestOpponent));
|
||||||
|
|
||||||
|
|
||||||
@@ -20623,6 +20798,25 @@ INT8 DecideActionBlackSoldier(SOLDIERTYPE* pSoldier)
|
|||||||
pSoldier->aiData.bAIMorale = CalcMorale(pSoldier);
|
pSoldier->aiData.bAIMorale = CalcMorale(pSoldier);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// DRINK IF LOW IN BREATH
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
DebugAI(AI_MSG_TOPIC, pSoldier, String("[drink if low on breath]"));
|
||||||
|
|
||||||
|
if (
|
||||||
|
!bInWater &&
|
||||||
|
!fDangerousSpot &&
|
||||||
|
pSoldier->bBreath < OKBREATH &&
|
||||||
|
pSoldier->CheckInitialAP() &&
|
||||||
|
Chance(30 - 10 * pSoldier->aiData.bUnderFire) &&
|
||||||
|
IsActionAffordable(pSoldier, AI_ACTION_DRINK_CANTEEN) &&
|
||||||
|
FindCanteen(pSoldier) != NO_SLOT )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("drink from canteen"));
|
||||||
|
return AI_ACTION_DRINK_CANTEEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// IF GASSED, OR REALLY TIRED (ON THE VERGE OF COLLAPSING), TRY TO RUN AWAY
|
// IF GASSED, OR REALLY TIRED (ON THE VERGE OF COLLAPSING), TRY TO RUN AWAY
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -20826,6 +21020,148 @@ INT8 DecideActionBlackSoldier(SOLDIERTYPE* pSoldier)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// USE EXPLOSIVES
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
INT8 bTNTSlot = FindTNT(pSoldier);
|
||||||
|
if ( bTNTSlot != NO_SLOT &&
|
||||||
|
(pSoldier->CheckInitialAP() || gfTurnBasedAI) &&
|
||||||
|
IsActionAffordable(pSoldier, AI_ACTION_PLANT_BOMB) &&
|
||||||
|
!pSoldier->aiData.bUnderFire &&
|
||||||
|
//(pSoldier->aiData.bOrders == SEEKENEMY || pSoldier->RushAttackActive()) &&
|
||||||
|
pSoldier->aiData.bOrders == SEEKENEMY &&
|
||||||
|
!TileIsOutOfBounds(sClosestSeenOpponent) &&
|
||||||
|
PythSpacesAway(pSoldier->sGridNo, sClosestSeenOpponent) > TACTICAL_RANGE_HALF &&
|
||||||
|
CountKnownEnemies(pSoldier, pSoldier->sGridNo, 2) > 0 &&
|
||||||
|
(Chance(SoldierDifficultyLevel(pSoldier) * 10) || InARoom(pSoldier->sGridNo, nullptr) && CountKnownEnemies(pSoldier, pSoldier->sGridNo, 2) > 0) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("found TNT in slot %d, plant bomb", bTNTSlot));
|
||||||
|
|
||||||
|
RearrangePocket(pSoldier, HANDPOS, bTNTSlot, FOREVER);
|
||||||
|
pSoldier->aiData.usActionData = 0;
|
||||||
|
return AI_ACTION_PLANT_BOMB;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( pSoldier->bTeam == ENEMY_TEAM &&
|
||||||
|
!pSoldier->bBlindedCounter &&
|
||||||
|
(pSoldier->CheckInitialAP() || gfTurnBasedAI) &&
|
||||||
|
!pSoldier->aiData.bUnderFire &&
|
||||||
|
pSoldier->aiData.bAIMorale >= MORALE_CONFIDENT &&
|
||||||
|
//(pSoldier->aiData.bOrders == SEEKENEMY || pSoldier->RushAttackActive()) &&
|
||||||
|
pSoldier->aiData.bOrders == SEEKENEMY &&
|
||||||
|
!TileIsOutOfBounds(sClosestOpponent) &&
|
||||||
|
(Chance(SoldierDifficultyLevel(pSoldier) * 10) ||
|
||||||
|
InARoom(sClosestOpponent, nullptr) &&
|
||||||
|
CountFriendsInRoom(pSoldier, RoomNo(sClosestOpponent)) == 0 &&
|
||||||
|
!SameRoom(sClosestOpponent, pSoldier->sGridNo) &&
|
||||||
|
(CountCorpsesInRoom(pSoldier, RoomNo(sClosestOpponent), 0) > 0 || TeamHighPercentKilled(pSoldier->bTeam) || EstimatePathCostToLocation(pSoldier, sClosestOpponent, pSoldier->pathing.bLevel, FALSE, NULL, NULL) == 0)) )
|
||||||
|
{
|
||||||
|
decision = DecideBlowUpObstacle(pSoldier, sClosestOpponent);
|
||||||
|
if ( decision != AI_ACTION_INVALID )
|
||||||
|
{
|
||||||
|
return decision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CUT A FENCE WITH WIRECUTTERS
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
INT8 bWirecutterSlot = FindWirecutters(pSoldier);
|
||||||
|
if (
|
||||||
|
(pSoldier->CheckInitialAP() || gfTurnBasedAI) &&
|
||||||
|
!pSoldier->aiData.bUnderFire &&
|
||||||
|
bWirecutterSlot != NO_SLOT &&
|
||||||
|
pSoldier->pathing.bLevel == 0 &&
|
||||||
|
pSoldier->aiData.bAIMorale >= MORALE_CONFIDENT &&
|
||||||
|
//(pSoldier->aiData.bOrders == SEEKENEMY || pSoldier->RushAttackActive()) &&
|
||||||
|
pSoldier->aiData.bOrders == SEEKENEMY &&
|
||||||
|
!TileIsOutOfBounds(sClosestOpponent) &&
|
||||||
|
Chance(SoldierDifficultyLevel(pSoldier) * 20) &&
|
||||||
|
pSoldier->bActionPoints >= GetAPsToCutFence(pSoldier) + GetAPsToLook(pSoldier) &&
|
||||||
|
FindFenceAroundSpot(pSoldier->sGridNo) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("found wire cutter, check if we can find fence to cut"));
|
||||||
|
|
||||||
|
UINT8 ubDesiredDir = AIDirection(pSoldier->sGridNo, sClosestOpponent);
|
||||||
|
INT32 sNewSpot;
|
||||||
|
INT32 sNextSpot;
|
||||||
|
INT32 sPathCost, sNewPathCost;
|
||||||
|
INT32 sOriginalGridNo;
|
||||||
|
|
||||||
|
for ( UINT8 ubCheckDir = 0; ubCheckDir < NUM_WORLD_DIRECTIONS; ubCheckDir++ )
|
||||||
|
{
|
||||||
|
// cannot cut fence diagonally, check desired dir
|
||||||
|
if ( ubCheckDir % 2 != 0 ||
|
||||||
|
ubCheckDir != ubDesiredDir &&
|
||||||
|
ubCheckDir != gOneCDirection[ubDesiredDir] &&
|
||||||
|
ubCheckDir != gOneCCDirection[ubDesiredDir] )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sNewSpot = NewGridNo(pSoldier->sGridNo, DirectionInc(ubCheckDir));
|
||||||
|
sNextSpot = NewGridNo(sNewSpot, DirectionInc(ubCheckDir));
|
||||||
|
|
||||||
|
if ( sNewSpot != pSoldier->sGridNo &&
|
||||||
|
sNextSpot != sNewSpot &&
|
||||||
|
!Water(sNewSpot, pSoldier->pathing.bLevel) &&
|
||||||
|
!Water(sNextSpot, pSoldier->pathing.bLevel) &&
|
||||||
|
IsCuttableWireFenceAtGridNo(sNewSpot) &&
|
||||||
|
!IsCutWireFenceAtGridNo(sNewSpot) &&
|
||||||
|
IsLocationSittable(sNextSpot, pSoldier->pathing.bLevel) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("found cuttable fence at %d", sNewSpot));
|
||||||
|
|
||||||
|
// check if cutting the fence improves situation
|
||||||
|
sPathCost = EstimatePlotPath(pSoldier, sClosestOpponent, FALSE, FALSE, FALSE, RUNNING, pSoldier->bStealthMode, FALSE, 0);
|
||||||
|
sOriginalGridNo = pSoldier->sGridNo;
|
||||||
|
pSoldier->sGridNo = sNewSpot;
|
||||||
|
sNewPathCost = EstimatePlotPath(pSoldier, sClosestOpponent, FALSE, FALSE, FALSE, RUNNING, pSoldier->bStealthMode, FALSE, 0);
|
||||||
|
pSoldier->sGridNo = sOriginalGridNo;
|
||||||
|
|
||||||
|
if ( sNewPathCost > 0 && (sPathCost == 0 || sPathCost > sNewPathCost && sPathCost - sNewPathCost > APBPConstants[AP_MAXIMUM]) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("cutting fence improves path cost, use wire cutter"));
|
||||||
|
if ( pSoldier->ubDirection == ubCheckDir )
|
||||||
|
{
|
||||||
|
RearrangePocket(pSoldier, HANDPOS, bWirecutterSlot, FOREVER);
|
||||||
|
pSoldier->aiData.usActionData = sNewSpot;
|
||||||
|
return AI_ACTION_HANDLE_ITEM;
|
||||||
|
}
|
||||||
|
else if ( pSoldier->InternalIsValidStance(ubCheckDir, gAnimControl[pSoldier->usAnimState].ubEndHeight) )
|
||||||
|
{
|
||||||
|
DebugAI(AI_MSG_INFO, pSoldier, String("turn before cutting fence"));
|
||||||
|
RearrangePocket(pSoldier, HANDPOS, bWirecutterSlot, FOREVER);
|
||||||
|
pSoldier->aiData.usActionData = ubCheckDir;
|
||||||
|
pSoldier->aiData.bNextAction = AI_ACTION_HANDLE_ITEM;
|
||||||
|
pSoldier->aiData.usNextActionData = sNewSpot;
|
||||||
|
return AI_ACTION_CHANGE_FACING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// JUMP THROUGH WINDOW
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
DebugAI(AI_MSG_TOPIC, pSoldier, String("[try to jump into window]"));
|
||||||
|
if ( (pSoldier->CheckInitialAP() || gfTurnBasedAI) &&
|
||||||
|
!pSoldier->aiData.bUnderFire &&
|
||||||
|
pSoldier->aiData.bAIMorale >= MORALE_CONFIDENT &&
|
||||||
|
pSoldier->aiData.bOrders == SEEKENEMY &&
|
||||||
|
!TileIsOutOfBounds(sClosestOpponent) )
|
||||||
|
{
|
||||||
|
decision = DecideJumpWindow(pSoldier, sClosestOpponent);
|
||||||
|
if ( decision != AI_ACTION_INVALID )
|
||||||
|
return decision;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// THROW A SMOKE GRENADE FOR COVER
|
// THROW A SMOKE GRENADE FOR COVER
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ typedef enum
|
|||||||
AI_ACTION_DOCTOR_SELF, // added by Flugente: AI-ONLY! bandage/surgery on self. DO NOT USE THIS FOR MERCS!!!
|
AI_ACTION_DOCTOR_SELF, // added by Flugente: AI-ONLY! bandage/surgery on self. DO NOT USE THIS FOR MERCS!!!
|
||||||
AI_ACTION_SELFDETONATE, // added by Flugente: blow up an explosive in own inventory
|
AI_ACTION_SELFDETONATE, // added by Flugente: blow up an explosive in own inventory
|
||||||
AI_ACTION_STOP_MEDIC, // sevenfm: stop giving aid animation
|
AI_ACTION_STOP_MEDIC, // sevenfm: stop giving aid animation
|
||||||
|
AI_ACTION_DRINK_CANTEEN, // sevenfm: drink from canteen in inventory
|
||||||
|
AI_ACTION_HANDLE_ITEM, // sevenfm: use item in hand
|
||||||
|
AI_ACTION_PLANT_BOMB, // sevenfm: plant bomb using item in hand
|
||||||
AI_ACTION_INVALID
|
AI_ACTION_INVALID
|
||||||
} ActionType;
|
} ActionType;
|
||||||
|
|
||||||
@@ -215,6 +218,7 @@ INT32 FindClosestDoor( SOLDIERTYPE * pSoldier );
|
|||||||
INT32 FindNearbyPointOnEdgeOfMap( SOLDIERTYPE * pSoldier, INT8 * pbDirection );
|
INT32 FindNearbyPointOnEdgeOfMap( SOLDIERTYPE * pSoldier, INT8 * pbDirection );
|
||||||
INT32 FindNearestEdgePoint( INT32 sGridNo );
|
INT32 FindNearestEdgePoint( INT32 sGridNo );
|
||||||
INT32 FindNearestPassableSpot( INT32 sGridNo, UINT8 usSearchRadius = 5 );
|
INT32 FindNearestPassableSpot( INT32 sGridNo, UINT8 usSearchRadius = 5 );
|
||||||
|
BOOLEAN FindFenceAroundSpot(INT32 sSpot);
|
||||||
|
|
||||||
//Kris: Added these as I need specific searches on certain sides.
|
//Kris: Added these as I need specific searches on certain sides.
|
||||||
enum
|
enum
|
||||||
@@ -312,6 +316,13 @@ UINT8 CountFriendsBlack( SOLDIERTYPE *pSoldier, INT32 sClosestOpponent = NOWHERE
|
|||||||
UINT16 CountTeamUnderAttack(INT8 bTeam, INT32 sGridNo, INT16 sDistance);
|
UINT16 CountTeamUnderAttack(INT8 bTeam, INT32 sGridNo, INT16 sDistance);
|
||||||
UINT16 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 sDistance);
|
UINT16 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 sDistance);
|
||||||
UINT16 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier);
|
UINT16 CountPublicKnownEnemies(SOLDIERTYPE *pSoldier);
|
||||||
|
UINT8 CountKnownEnemies(SOLDIERTYPE* pSoldier, INT32 sSpot, INT16 sDistance, INT8 bLevel = HEARD_THIS_TURN);
|
||||||
|
UINT8 CountKnownEnemiesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom);
|
||||||
|
UINT8 CountFriendsInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoom);
|
||||||
|
INT32 CountCorpsesInRoom(SOLDIERTYPE* pSoldier, UINT16 usRoomNo, INT8 bLevel);
|
||||||
|
UINT16 RoomNo(INT32 sSpot);
|
||||||
|
BOOLEAN SameRoom(INT32 sSpot1, INT32 sSpot2);
|
||||||
|
BOOLEAN CheckWindow(INT32 sSpot, UINT8 ubDirection, BOOLEAN fAllowClosed);
|
||||||
|
|
||||||
UINT8 SectorCurfew(BOOLEAN fNight);
|
UINT8 SectorCurfew(BOOLEAN fNight);
|
||||||
UINT8 TeamPercentKilled(INT8 bTeam);
|
UINT8 TeamPercentKilled(INT8 bTeam);
|
||||||
@@ -434,6 +445,7 @@ INT8 KnownPublicLevel(UINT8 bTeam, SoldierID ubOpponentID);
|
|||||||
#define TACTICAL_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
|
#define TACTICAL_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
|
||||||
#define TACTICAL_RANGE_CELL_COORDS TACTICAL_RANGE*CELL_X_SIZE
|
#define TACTICAL_RANGE_CELL_COORDS TACTICAL_RANGE*CELL_X_SIZE
|
||||||
#define BOMB_DETECTION_RANGE (TACTICAL_RANGE / 4)
|
#define BOMB_DETECTION_RANGE (TACTICAL_RANGE / 4)
|
||||||
|
#define TACTICAL_RANGE_HALF (TACTICAL_RANGE / 2)
|
||||||
#define TACTICAL_RANGE_CLOSE (TACTICAL_RANGE / 4)
|
#define TACTICAL_RANGE_CLOSE (TACTICAL_RANGE / 4)
|
||||||
#define TACTICAL_RANGE_VERYCLOSE (TACTICAL_RANGE / 6)
|
#define TACTICAL_RANGE_VERYCLOSE (TACTICAL_RANGE / 6)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user