- AI controlled soldiers can now jump through windows

- new ini option allows to jump through closed windows, smashing them while doing so

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5682 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2012-11-11 05:31:41 +00:00
parent e98c1ce83c
commit 004728d2e4
12 changed files with 297 additions and 100 deletions
+3 -3
View File
@@ -177,11 +177,11 @@ void CreatureDecideAlertStatus( SOLDIERTYPE *pCreature );
// Flugente: Zombie AI
INT8 ZombieDecideAction( SOLDIERTYPE * pZombie );
void ZombieDecideAlertStatus( SOLDIERTYPE *pZombie );
// a variant of ClosestSeenOpponent(...), that allows zombies
INT32 ClosestSeenOpponentforZombie(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel);
#endif
// a variant of ClosestSeenOpponent(...), that allows to find enemies on a roof
INT32 ClosestSeenOpponentWithRoof(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel);
INT8 CrowDecideAction( SOLDIERTYPE * pSoldier );
void DecideAlertStatus( SOLDIERTYPE *pSoldier );
INT8 DecideAutoBandage( SOLDIERTYPE * pSoldier );
+5
View File
@@ -2845,6 +2845,11 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier)
}
break;
case AI_ACTION_JUMP_WINDOW:
{
pSoldier->BeginSoldierClimbWindow();
}
break;
default:
#ifdef BETAVERSION
+85 -80
View File
@@ -578,6 +578,14 @@ BOOLEAN IsActionAffordable(SOLDIERTYPE *pSoldier)
//bMinPointsNeeded = GetAPsToStealItem( pSoldier, NULL, pSoldier->aiData.usActionData );;
break;
case AI_ACTION_JUMP_WINDOW:
if((UsingNewInventorySystem() == true) && pSoldier->inv[BPACKPOCKPOS].exists() == true)
bMinPointsNeeded = GetAPsToJumpThroughWindows( pSoldier, TRUE );
else
bMinPointsNeeded = GetAPsToJumpFence( pSoldier, FALSE );
break;
default:
#ifdef BETAVERSION
//NumMessage("AffordableAction - Illegal action type = ",pSoldier->aiData.bAction);
@@ -1299,101 +1307,98 @@ INT32 ClosestSeenOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLeve
}
// special variant with a minor twist for the zombie AI
#ifdef ENABLE_ZOMBIES
INT32 ClosestSeenOpponentforZombie(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel)
// special variant with a minor twist
INT32 ClosestSeenOpponentWithRoof(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLevel)
{
INT32 sGridNo, sClosestOpponent = NOWHERE;
UINT32 uiLoop;
INT32 iRange, iClosestRange = 1500;
INT8 *pbPersOL;
INT8 bLevel, bClosestLevel;
SOLDIERTYPE * pOpp;
bClosestLevel = -1;
// look through this man's personal & public opplists for opponents known
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
INT32 sGridNo, sClosestOpponent = NOWHERE;
UINT32 uiLoop;
INT32 iRange, iClosestRange = 1500;
INT8 *pbPersOL;
INT8 bLevel, bClosestLevel;
SOLDIERTYPE * pOpp;
pOpp = MercSlots[ uiLoop ];
bClosestLevel = -1;
// look through this man's personal & public opplists for opponents known
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
// if this merc is inactive, at base, on assignment, or dead
if (!pOpp)
{
pOpp = MercSlots[ uiLoop ];
// if this merc is inactive, at base, on assignment, or dead
if (!pOpp)
{
continue; // next merc
}
// if this merc is neutral/on same side, he's not an opponent
if ( CONSIDERED_NEUTRAL( pSoldier, pOpp ) || (pSoldier->bSide == pOpp->bSide))
{
continue; // next merc
}
// Special stuff for Carmen the bounty hunter
if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpp->ubProfile != 64)
{
continue; // next opponent
}
pbPersOL = pSoldier->aiData.bOppList + pOpp->ubID;
// if this opponent is not seen personally
if (*pbPersOL != SEEN_CURRENTLY)
{
continue; // next merc
}
// since we're dealing with seen people, use exact gridnos
sGridNo = pOpp->sGridNo;
bLevel = pOpp->pathing.bLevel;
// if we are standing at that gridno(!, obviously our info is old...)
if (sGridNo == pSoldier->sGridNo)
{
continue; // next merc
}
// special: allow zombies to also find opponents on a roof
// otherwise they have will never decide to climb a roof while they are seeing an enemy
// this function is used only for turning towards closest opponent or changing stance
// as such, if they AI is in a building,
// we should ignore people who are on the roof of the same building as the AI
if ( !pSoldier->IsZombie() && (bLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, sGridNo ) )
{
continue;
}
// I hope this will be good enough; otherwise we need a fractional/world-units-based 2D distance function
//sRange = PythSpacesAway( pSoldier->sGridNo, sGridNo);
iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo );
if (iRange < iClosestRange)
{
iClosestRange = iRange;
sClosestOpponent = sGridNo;
bClosestLevel = bLevel;
}
continue; // next merc
}
#ifdef DEBUGDECISIONS
if (!TileIsOutOfBounds(sClosestOpponent))
// if this merc is neutral/on same side, he's not an opponent
if ( CONSIDERED_NEUTRAL( pSoldier, pOpp ) || (pSoldier->bSide == pOpp->bSide))
{
AINumMessage("CLOSEST OPPONENT is at gridno ",sClosestOpponent);
continue; // next merc
}
#endif
if (psGridNo)
// Special stuff for Carmen the bounty hunter
if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpp->ubProfile != 64)
{
*psGridNo = sClosestOpponent;
continue; // next opponent
}
if (pbLevel)
pbPersOL = pSoldier->aiData.bOppList + pOpp->ubID;
// if this opponent is not seen personally
if (*pbPersOL != SEEN_CURRENTLY)
{
*pbLevel = bClosestLevel;
continue; // next merc
}
return( sClosestOpponent );
// since we're dealing with seen people, use exact gridnos
sGridNo = pOpp->sGridNo;
bLevel = pOpp->pathing.bLevel;
// if we are standing at that gridno(!, obviously our info is old...)
if (sGridNo == pSoldier->sGridNo)
{
continue; // next merc
}
// special: allow zombies to also find opponents on a roof
// otherwise they have will never decide to climb a roof while they are seeing an enemy
// this function is used only for turning towards closest opponent or changing stance
// as such, if they AI is in a building,
// we should ignore people who are on the roof of the same building as the AI
/*if ( !pSoldier->IsZombie() && (bLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, sGridNo ) )
{
continue;
}*/
// I hope this will be good enough; otherwise we need a fractional/world-units-based 2D distance function
//sRange = PythSpacesAway( pSoldier->sGridNo, sGridNo);
iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo );
if (iRange < iClosestRange)
{
iClosestRange = iRange;
sClosestOpponent = sGridNo;
bClosestLevel = bLevel;
}
}
#ifdef DEBUGDECISIONS
if (!TileIsOutOfBounds(sClosestOpponent))
{
AINumMessage("CLOSEST OPPONENT is at gridno ",sClosestOpponent);
}
#endif
if (psGridNo)
{
*psGridNo = sClosestOpponent;
}
if (pbLevel)
{
*pbLevel = bClosestLevel;
}
return( sClosestOpponent );
}
INT32 ClosestPC( SOLDIERTYPE *pSoldier, INT32 * psDistance )
{
+145 -2
View File
@@ -5300,6 +5300,106 @@ INT16 ubMinAPCost;
return( AI_ACTION_NONE );
}
// get the location of the closest reachable opponent
/* Flugente 22.02.2012 - A few clarifications: I changed ClosestSeenOpponent so that for zombies, this function also returns an opponent if he is on the
* roof of a building, we are not, but our GridNo belongs to that same building.
* If that is the case, it is clear that we have to get on that roof. However, we cannot do that in BlackState. If, by pure chance, we can still see our
* enemy, we cannot climb (there is no climbing option in BlackState sofar).
* So, I changed the code so that now we will climb the roof.
*/
INT32 targetGridNo = -1;
INT8 targetbLevel = 0;
sClosestOpponent = ClosestSeenOpponentWithRoof(pSoldier, &targetGridNo, &targetbLevel);
if (!TileIsOutOfBounds(sClosestOpponent) && ! ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) )
{
//////////////////////////////////////////////////////////////////////
// GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT
//////////////////////////////////////////////////////////////////////
// try to move towards him
pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,sClosestOpponent,AI_ACTION_GET_CLOSER);
// if it's possible
if (!TileIsOutOfBounds(sClosestOpponent))
{
if (targetbLevel != pSoldier->pathing.bLevel )
return(AI_ACTION_MOVE_TO_CLIMB);
else
{
// Flugente: if on the same level and there is a jumpable window here, jump through it
if ( gGameExternalOptions.fCanJumpThroughWindows && targetbLevel == pSoldier->pathing.bLevel && targetbLevel == 0 )
{
// determine if there is a jumpable window in the direction to our target
// if yes, and we are not facing it, face it now
// if yes, and we are facing it, jump
// if no, go on, nothing to see here
// determine direction of our target
INT8 targetdirection = GetDirectionToGridNoFromGridNo(pSoldier->sGridNo, sClosestOpponent);
// determine if there is a jumpable window here, in the direction of our target
// store old direction for this check
INT tmpdirection = pSoldier->ubDirection;
pSoldier->ubDirection = targetdirection;
INT8 windowdirection = DIRECTION_IRRELEVANT;
if ( FindWindowJumpDirection(pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &windowdirection) && targetdirection == windowdirection )
{
pSoldier->ubDirection = tmpdirection;
// are we already looking in that direction?
if ( pSoldier->ubDirection == targetdirection )
{
// jump through the window
return(AI_ACTION_JUMP_WINDOW);
}
else
{
// look into that direction
if ( pSoldier->InternalIsValidStance( targetdirection, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) )
{
pSoldier->aiData.usActionData = targetdirection;
return(AI_ACTION_CHANGE_FACING);
}
}
}
pSoldier->ubDirection = tmpdirection;
}
}
}
}
// The situation mentioned above happens...
else if ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) )
{
if ( 1 )//gGameExternalOptions.fZombieCanClimb )
{
if (!TileIsOutOfBounds(targetGridNo) )
{
// need to climb AND have enough APs to get there this turn
BOOLEAN fUp = TRUE;
if (pSoldier->pathing.bLevel > 0 )
fUp = FALSE;
if ( pSoldier->bActionPoints > GetAPsToClimbRoof ( pSoldier, fUp ) )
{
pSoldier->aiData.usActionData = targetGridNo;//FindClosestClimbPoint(pSoldier, fUp );
// Necessary test: can we climb up at this position? It might happen that our target is directly above us, then we'll have to move
INT8 newdirection;
if ( ( fUp && FindHeigherLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &newdirection ) ) || ( !fUp && FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &newdirection ) ) )
{
return( AI_ACTION_CLIMB_ROOF );
}
else
{
return(AI_ACTION_SEEK_OPPONENT);
}
}
}
}
}
// try to make boxer close if possible
if (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER )
{
@@ -7853,7 +7953,7 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
*/
INT32 targetGridNo = -1;
INT8 targetbLevel = 0;
sClosestOpponent = ClosestSeenOpponentforZombie(pSoldier, &targetGridNo, &targetbLevel);
sClosestOpponent = ClosestSeenOpponentWithRoof(pSoldier, &targetGridNo, &targetbLevel);
if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ZOMBIE )
{
if (!TileIsOutOfBounds(sClosestOpponent) && ! ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) )
@@ -7866,14 +7966,57 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
pSoldier->aiData.usActionData = GoAsFarAsPossibleTowards(pSoldier,sClosestOpponent,AI_ACTION_GET_CLOSER);
// if it's possible
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
if (!TileIsOutOfBounds(sClosestOpponent))
{
if (targetbLevel != pSoldier->pathing.bLevel && gGameExternalOptions.fZombieCanClimb )
return(AI_ACTION_MOVE_TO_CLIMB);
else
{
// Flugente: if on the same level and there is a jumpable window here, jump through it
if ( gGameExternalOptions.fZombieCanJumpWindows && targetbLevel == pSoldier->pathing.bLevel && targetbLevel == 0 )
{
// determine if there is a jumpable window in the direction to our target
// if yes, and we are not facing it, face it now
// if yes, and we are facing it, jump
// if no, go on, nothing to see here
// determine direction of our target
INT8 targetdirection = GetDirectionToGridNoFromGridNo(pSoldier->sGridNo, sClosestOpponent);
// determine if there is a jumpable window here, in the direction of our target
// store old direction for this check
INT tmpdirection = pSoldier->ubDirection;
pSoldier->ubDirection = targetdirection;
INT8 windowdirection = DIRECTION_IRRELEVANT;
if ( FindWindowJumpDirection(pSoldier, pSoldier->sGridNo, pSoldier->ubDirection, &windowdirection) && targetdirection == windowdirection )
{
pSoldier->ubDirection = tmpdirection;
// are we already looking in that direction?
if ( pSoldier->ubDirection == targetdirection )
{
// jump through the window
return(AI_ACTION_JUMP_WINDOW);
}
else
{
// look into that direction
if ( pSoldier->InternalIsValidStance( targetdirection, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) )
{
pSoldier->aiData.usActionData = targetdirection;
return(AI_ACTION_CHANGE_FACING);
}
}
}
pSoldier->ubDirection = tmpdirection;
}
return(AI_ACTION_SEEK_OPPONENT);
}
}
}
// The situation mentioned above happens...
else if ( (targetbLevel != pSoldier->pathing.bLevel) && SameBuilding( pSoldier->sGridNo, targetGridNo ) )
{
+2
View File
@@ -100,6 +100,8 @@ typedef enum
AI_ACTION_STEAL_MOVE, // added by SANDRO
AI_ACTION_RELOAD_GUN,
AI_ACTION_JUMP_WINDOW, // added by Flugente: jump through a window
} ActionType;