Migrate +AI features

* Drink canteen
* Jump through window
* Use explosives
* Use wirecutters
This commit is contained in:
Asdow
2026-01-23 23:19:07 +02:00
parent 41945842d5
commit bd9cbd9944
14 changed files with 896 additions and 14 deletions
+231
View File
@@ -27,6 +27,7 @@
#include "SmokeEffects.h" // sevenfm
#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()
@@ -812,6 +813,18 @@ BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier, INT8 bAction)
bMinPointsNeeded = 20; // TODO
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:
#ifdef BETAVERSION
//NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction);
@@ -6327,3 +6340,221 @@ BOOLEAN CheckSuppressionDirection(SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, IN
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;
}