Enemy soldiers will try to avoid being hit by artillery strike.

FindBestNearbyCover:
- penalty to locations in red smoke danger zone
- avoid moving into red smoke danger zone if not in red smoke danger already
FindNearestUngassedLand: 
- improved code
- try not to move closer to known enemies.
- calculate path with correct movement mode
FindNearbyDarkerSpot: 
- improved code
- calculate path with correct movement mode
FindFlankingSpot:
- avoid locations near bombs
- avoid moving into red smoke danger zone

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8753 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Sevenfm
2020-02-15 12:54:59 +00:00
parent a6271e2e47
commit fe838ad7d9
5 changed files with 419 additions and 59 deletions
-8
View File
@@ -48,13 +48,6 @@ enum
NUM_URGENCY_STATES
};
#define NOWATER 0
#define WATEROK 1
#define IGNORE_PATH 0
#define ENSURE_PATH 1
#define ENSURE_PATH_COST 2
#define DONTFORCE 0
#define FORCE 1
@@ -95,7 +88,6 @@ enum
#define MAX_THREAT_RANGE 400 // 30 tiles worth
#define MIN_PERCENT_BETTER 5 // 5% improvement in cover is good
#define TOSSES_PER_10TURNS 18 // max # of grenades tossable in 10 turns
#define SHELLS_PER_10TURNS 13 // max # of shells firable in 10 turns
+261
View File
@@ -27,6 +27,7 @@
#include "Game Clock.h" // sevenfm
#include "Rotting Corpses.h" // sevenfm
#include "wcheck.h" // sevenfm
#include "SmokeEffects.h" // sevenfm
#endif
#include "GameInitOptionsScreen.h"
@@ -4255,6 +4256,266 @@ BOOLEAN FindBombNearby( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance )
return FALSE;
}
// danger percent based on distance to closest smoke effect
UINT8 RedSmokeDanger(INT32 sGridNo, INT8 bLevel)
{
UINT32 uiCnt;
INT32 sDist;
INT32 sClosestDist;
INT32 sMaxDist = min(gSkillTraitValues.usVOMortarRadius, DAY_VISION_RANGE);
INT32 sClosestSmoke = NOWHERE;
UINT8 ubDangerPercent = 0;
if (TileIsOutOfBounds(sGridNo))
{
return 0;
}
if (!gSkillTraitValues.fROAllowArtillery)
{
return 0;
}
// no artillery strike danger underground
if (gbWorldSectorZ > 0)
{
return 0;
}
// check if artillery strike was ordered by any team
if (!CheckArtilleryStrike())
{
return 0;
}
// no danger when in a building
if (bLevel == 0 && CheckRoof(sGridNo))
{
return 0;
}
// no danger when in dense terrain
if (bLevel == 0 && TerrainDensity(sGridNo, bLevel, 2, FALSE) >= 20)
{
return 0;
}
//loop through all red smoke effects and find closest
for (uiCnt = 0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
if (gSmokeEffectData[uiCnt].fAllocated &&
gSmokeEffectData[uiCnt].bType == SIGNAL_SMOKE_EFFECT &&
!TileIsOutOfBounds(gSmokeEffectData[uiCnt].sGridNo))
{
sDist = PythSpacesAway(gSmokeEffectData[uiCnt].sGridNo, sGridNo);
if (sClosestSmoke == NOWHERE || sDist < sClosestDist)
{
sClosestDist = sDist;
sClosestSmoke = gSmokeEffectData[uiCnt].sGridNo;
}
}
}
// if we found red smoke, calculate danger percent based on distance
// 0% at DAY_VISION_RANGE/2, 100% at zero range
if (sClosestSmoke != NOWHERE)
{
ubDangerPercent = 100 * (sMaxDist - min(sMaxDist, sClosestDist)) / sMaxDist;
}
return ubDangerPercent;
}
// check if artillery strike was ordered by any team
BOOLEAN CheckArtilleryStrike(void)
{
UINT32 uiBombIndex;
OBJECTTYPE *pObj;
// search all bombs
for (uiBombIndex = 0; uiBombIndex < guiNumWorldBombs; uiBombIndex++)
{
if (gWorldBombs[uiBombIndex].fExists &&
gWorldItems[gWorldBombs[uiBombIndex].iItemIndex].usFlags & WORLD_ITEM_ARMED_BOMB)
{
pObj = &(gWorldItems[gWorldBombs[uiBombIndex].iItemIndex].object);
if (pObj && pObj->exists() && (*pObj)[0]->data.ubWireNetworkFlag & ANY_ARTILLERY_FLAG)
{
return TRUE;
}
}
}
return FALSE;
}
BOOLEAN CheckRoof(INT32 sGridNo)
{
if (FindStructure(sGridNo, STRUCTURE_ROOF) != NULL)
{
return TRUE;
}
return FALSE;
}
UINT8 TerrainDensity(INT32 sSpot, INT8 bLevel, UINT8 ubDistance, BOOLEAN fGrass)
{
if (TileIsOutOfBounds(sSpot))
return 0;
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT32 sCheckSpot = NOWHERE;
INT32 sCountSpots = 0;
INT32 sCountObstacles = 0;
UINT16 usRoom1, usRoom2;
STRUCTURE *pCurrent;
INT16 sDesiredLevel;
// determine maximum horizontal limits
sMaxLeft = min(ubDistance, (sSpot % MAXCOL));
sMaxRight = min(ubDistance, MAXCOL - ((sSpot % MAXCOL) + 1));
// determine maximum vertical limits
sMaxUp = min(ubDistance, (sSpot / MAXROW));
sMaxDown = min(ubDistance, MAXROW - ((sSpot / MAXROW) + 1));
// count obstacles
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
sCheckSpot = sSpot + sXOffset + (MAXCOL * sYOffset);
if (TileIsOutOfBounds(sCheckSpot))
{
continue;
}
if (InARoom(sSpot, &usRoom1) != InARoom(sCheckSpot, &usRoom2) || usRoom1 != usRoom2)
{
continue;
}
sCountSpots++;
if (!IsLocationSittableExcludingPeople(sCheckSpot, bLevel))
{
sCountObstacles++;
}
if (fGrass && IsLocationSittableExcludingPeople(sCheckSpot, bLevel))
{
pCurrent = gpWorldLevelData[sCheckSpot].pStructureHead;
if (bLevel > 0)
sDesiredLevel = STRUCTURE_ON_ROOF;
else
sDesiredLevel = STRUCTURE_ON_GROUND;
if (pCurrent != NULL &&
pCurrent->sCubeOffset == sDesiredLevel &&
pCurrent->pDBStructureRef->pDBStructure->ubArmour == 4) // light vegetation
{
sCountObstacles++;
}
}
}
}
if (sCountSpots > 0)
return 100 * sCountObstacles / sCountSpots;
else
return 0;
}
INT16 DistanceToClosestActiveOpponent(SOLDIERTYPE *pSoldier, INT32 sSpot)
{
INT32 sGridNo;
UINT32 uiLoop;
INT8 bLevel;
SOLDIERTYPE *pOpponent;
INT16 sDistance, sClosestDistance = -1;
if (!pSoldier || TileIsOutOfBounds(sSpot))
{
return 0;
}
// look through this man's personal & public opplists for opponents known
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pOpponent = MercSlots[uiLoop];
// if this merc is inactive, at base, on assignment, or dead
if (!pOpponent)
{
continue; // next merc
}
if (!ValidOpponent(pSoldier, pOpponent))
{
continue;
}
if (pOpponent->stats.bLife < OKLIFE)
{
continue;
}
// if this opponent is unknown personally and publicly
if (pSoldier->aiData.bOppList[pOpponent->ubID] == NOT_HEARD_OR_SEEN)
{
continue;
}
// obtain opponent's location and level
sGridNo = gsLastKnownOppLoc[pSoldier->ubID][pOpponent->ubID];
bLevel = gbLastKnownOppLevel[pSoldier->ubID][pOpponent->ubID];
if (TileIsOutOfBounds(sGridNo))
{
continue;
}
sDistance = PythSpacesAway(sSpot, sGridNo);
if (sClosestDistance < 0 ||
sDistance < sClosestDistance)
{
sClosestDistance = sDistance;
}
}
return sClosestDistance;
}
BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* pOpponent)
{
if (!pSoldier || !pOpponent)
{
return FALSE;
}
if (!pOpponent->bActive ||
!pOpponent->bInSector ||
pOpponent->stats.bLife == 0 ||
CONSIDERED_NEUTRAL(pSoldier, pOpponent) ||
pSoldier->bSide == pOpponent->bSide ||
pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpponent->ubProfile != SLAY ||
(pOpponent->ubWhatKindOfMercAmI == MERC_TYPE__VEHICLE && GetNumberInVehicle(pOpponent->bVehicleID) == 0) ||
gTacticalStatus.bBoxingState == BOXING && (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && !(pOpponent->flags.uiStatusFlags & SOLDIER_BOXER) ||
pOpponent->ubBodyType == CROW)
{
return FALSE;
}
return TRUE;
}
BOOLEAN AnyCoverFromSpot( INT32 sSpot, INT8 bLevel, INT32 sThreatLoc, INT8 bThreatLevel )
{
UINT8 ubDirection;
+4 -4
View File
@@ -1084,7 +1084,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier)
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionGreen: get out of water and gas"));
if (bInWater || bInGas || FindBombNearby(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/8))
if (bInWater || bInGas || FindBombNearby(pSoldier, pSoldier->sGridNo, BOMB_DETECTION_RANGE) || RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel))
{
pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier);
@@ -1607,7 +1607,7 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
// WHEN IN GAS, GO TO NEAREST REACHABLE SPOT OF UNGASSED LAND
////////////////////////////////////////////////////////////////////////////
if ( InGas(pSoldier, pSoldier->sGridNo) || DeepWater( pSoldier->sGridNo, pSoldier->pathing.bLevel ) || FindBombNearby(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/8) )
if (InGas(pSoldier, pSoldier->sGridNo) || DeepWater(pSoldier->sGridNo, pSoldier->pathing.bLevel) || FindBombNearby(pSoldier, pSoldier->sGridNo, BOMB_DETECTION_RANGE))
{
pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier);
@@ -2582,7 +2582,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier, UINT8 ubUnconsciousOK)
// WHEN IN GAS, GO TO NEAREST REACHABLE SPOT OF UNGASSED LAND
////////////////////////////////////////////////////////////////////////////
if (ubCanMove && (bInGas || FindBombNearby(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/8)))
if (ubCanMove && (bInGas || bInDeepWater || FindBombNearby(pSoldier, pSoldier->sGridNo, BOMB_DETECTION_RANGE) || RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel)))
{
pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier);
@@ -4719,7 +4719,7 @@ INT16 ubMinAPCost;
////////////////////////////////////////////////////////////////////////////
// if soldier in water/gas has enough APs left to move at least 1 square
if ( ( bInDeepWater || bInGas || FindBombNearby(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/8) ) && ubCanMove)
if (ubCanMove && (bInGas || bInDeepWater || FindBombNearby(pSoldier, pSoldier->sGridNo, BOMB_DETECTION_RANGE) || RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel)))
{
pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier);
+147 -47
View File
@@ -423,7 +423,6 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
pHim->dYPos = dHisY; // and the 'y'
}
// these value should be < 1 million each
iHisPosValue = bHisCTGT * Threat[uiThreatIndex].iValue * Threat[uiThreatIndex].iAPs;
iMyPosValue = bMyCTGT * iMyThreat * iMyAPsLeft;
@@ -438,7 +437,7 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
sDist = PythSpacesAway(sMyGridNo, sHisGridNo);
if( sDist < DAY_VISION_RANGE / 2 )
if (sDist < (INT32)(DAY_VISION_RANGE / 2))
{
ubCoverReduction = ubCoverReduction * 2 * sDist / DAY_VISION_RANGE;
}
@@ -888,6 +887,9 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
{
iCurrentCoverValue -= abs(iCurrentCoverValue) / (8-ubDiff);
}
// sevenfm: penalize locations near red smoke
iCurrentCoverValue -= abs(iCurrentCoverValue) * RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel) / 100;
}
#ifdef DEBUGCOVER
@@ -1027,7 +1029,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
}
// sevenfm: avoid tiles near bombs
if( FindBombNearby(pSoldier, sGridNo, DAY_VISION_RANGE / 8))
if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE))
{
continue;
}
@@ -1046,6 +1048,15 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
continue;
}
// avoid moving into red smoke
if (gGameExternalOptions.fAIBetterCover &&
RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) &&
!RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel))
{
//DebugCover(pSoldier, String("moving into red smoke, skip"));
continue;
}
iPathCost = gubAIPathCosts[AI_PATHCOST_RADIUS + sXOffset][AI_PATHCOST_RADIUS + sYOffset];
/*
// water is OK, if the only good hiding place requires us to get wet, OK
@@ -1134,6 +1145,9 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
{
iCoverValue -= abs(iCoverValue) / (8-ubDiff);
}
// sevenfm: penalize locations near red smoke
iCoverValue -= abs(iCoverValue) * RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) / 100;
}
if ( fNight && !( InARoom( sGridNo, NULL ) ) ) // ignore in buildings in case placed there
@@ -1496,6 +1510,12 @@ INT32 FindSpotMaxDistFromOpponents(SOLDIERTYPE *pSoldier)
continue;
}
// sevenfm: avoid red smoke
if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// OK, this place shows potential. How useful is it as cover?
//NumMessage("Promising seems gridno #",gridno);
@@ -1561,35 +1581,34 @@ INT32 FindSpotMaxDistFromOpponents(SOLDIERTYPE *pSoldier)
INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
{
INT32 sGridNo,sClosestLand = NOWHERE,sPathCost,sShortestPath = 1000;
INT16 sMaxLeft,sMaxRight,sMaxUp,sMaxDown,sXOffset,sYOffset;
INT32 sGridNo, sClosestLand = NOWHERE, sPathCost, sShortestPath = 1000;
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT32 iSearchRange;
INT16 sDistance, sOriginalDistance;
UINT16 usMovementMode = DetermineMovementMode(pSoldier, AI_ACTION_LEAVE_WATER_GAS);
//NameMessage(pSoldier,"looking for nearest reachable land");
sOriginalDistance = DistanceToClosestActiveOpponent(pSoldier, pSoldier->sGridNo);
// start with a small search area, and expand it if we're unsuccessful
// this should almost never need to search farther than 5 or 10 squares...
for (iSearchRange = 5; iSearchRange <= 25; iSearchRange += 5)
for (iSearchRange = 5; iSearchRange <= 25; iSearchRange += 10)
{
//NumMessage("Trying iSearchRange = ", iSearchRange);
// determine maximum horizontal limits
sMaxLeft = min(iSearchRange,(pSoldier->sGridNo % MAXCOL));
sMaxLeft = min(iSearchRange, (pSoldier->sGridNo % MAXCOL));
//NumMessage("sMaxLeft = ",sMaxLeft);
sMaxRight = min(iSearchRange,MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
sMaxRight = min(iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
//NumMessage("sMaxRight = ",sMaxRight);
// determine maximum vertical limits
sMaxUp = min(iSearchRange,(pSoldier->sGridNo / MAXROW));
sMaxUp = min(iSearchRange, (pSoldier->sGridNo / MAXROW));
//NumMessage("sMaxUp = ",sMaxUp);
sMaxDown = min(iSearchRange,MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
sMaxDown = min(iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
//NumMessage("sMaxDown = ",sMaxDown);
// Call FindBestPath to set flags in all locations that we can
// walk into within range. We have to set some things up first...
// set the distance limit of the square region
gubNPCDistLimit = (UINT8) iSearchRange;
// walk into within range. We have to set some things up first...
// reset the "reachable" flags in the region we're looking at
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
@@ -1597,7 +1616,7 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
@@ -1606,7 +1625,11 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
}
}
FindBestPath( pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, DetermineMovementMode( pSoldier, AI_ACTION_LEAVE_WATER_GAS ), COPYREACHABLE, 0 );//dnl ch50 071009
gubNPCAPBudget = pSoldier->bActionPoints;
gubNPCDistLimit = (UINT8)iSearchRange;
FindBestPath(pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, usMovementMode, COPYREACHABLE, 0); //dnl ch50 071009
gubNPCAPBudget = 0;
gubNPCDistLimit = 0;
// Turn off the "reachable" flag for his current location
// so we don't consider it
@@ -1620,7 +1643,7 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
// calculate the next potential gridno
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
//NumMessage("Testing gridno #",gridno);
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
@@ -1631,7 +1654,31 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
}
// ignore blacklisted spot
if ( sGridNo == pSoldier->pathing.sBlackList )
if (sGridNo == pSoldier->pathing.sBlackList)
{
continue;
}
// sevenfm: check for gas
if (InGas(pSoldier, sGridNo))
{
continue;
}
// check for deep water
if (DeepWater(pSoldier->sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// check for bombs nearby
if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE))
{
continue;
}
// check for red smoke
if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) > 0)
{
continue;
}
@@ -1642,16 +1689,39 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
continue;
}
// check for red smoke
if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) > 0)
{
continue;
}
// CJC: here, unfortunately, we must calculate a path so we have an AP cost
// obviously, we're looking for LAND, so water is out!
sPathCost = LegalNPCDestination(pSoldier,sGridNo,ENSURE_PATH_COST,NOWATER,0);
//sPathCost = LegalNPCDestination(pSoldier,sGridNo,ENSURE_PATH_COST,NOWATER,0);
if (!sPathCost)
if (!LegalNPCDestination(pSoldier, sGridNo, IGNORE_PATH, NOWATER, 0))
{
continue; // skip on to the next potential grid
}
sPathCost = PlotPath(pSoldier, sGridNo, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, pSoldier->bReverse, 0);
// check if spot is reachable
if(sPathCost == 0)
{
continue;
}
sDistance = DistanceToClosestActiveOpponent(pSoldier, pSoldier->sGridNo);
// penalty if moving closer to enemy
if (sDistance >= 0 && sOriginalDistance >= 0 && sDistance < sOriginalDistance)
{
//sPathCost += APBPConstants[AP_MAXIMUM];
sPathCost += (sOriginalDistance - sDistance) * (APBPConstants[AP_MOVEMENT_FLAT] + APBPConstants[AP_MODIFIER_WALK]);
}
// if this path is shorter than the one to the closest land found so far
if (sPathCost < sShortestPath)
{
@@ -1674,49 +1744,47 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier)
return(sClosestLand);
}
INT32 FindNearbyDarkerSpot( SOLDIERTYPE *pSoldier )
INT32 FindNearbyDarkerSpot(SOLDIERTYPE *pSoldier)
{
INT32 sGridNo, sClosestSpot = NOWHERE, sPathCost;
INT32 iSpotValue, iBestSpotValue = 1000;
INT16 sMaxLeft,sMaxRight,sMaxUp,sMaxDown,sXOffset,sYOffset;
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT32 iSearchRange;
INT8 bLightLevel, bCurrLightLevel, bLightDiff;
INT32 iRoamRange;
INT32 sOrigin;
UINT16 usMovementMode = DetermineMovementMode(pSoldier, AI_ACTION_LEAVE_WATER_GAS);
bCurrLightLevel = LightTrueLevel( pSoldier->sGridNo, pSoldier->pathing.bLevel );
bCurrLightLevel = LightTrueLevel(pSoldier->sGridNo, pSoldier->pathing.bLevel);
iRoamRange = RoamingRange( pSoldier, &sOrigin );
iRoamRange = RoamingRange(pSoldier, &sOrigin);
// start with a small search area, and expand it if we're unsuccessful
// this should almost never need to search farther than 5 or 10 squares...
for (iSearchRange = 5; iSearchRange <= 15; iSearchRange += 5)
{
// determine maximum horizontal limits
sMaxLeft = min(iSearchRange,(pSoldier->sGridNo % MAXCOL));
sMaxLeft = min(iSearchRange, (pSoldier->sGridNo % MAXCOL));
//NumMessage("sMaxLeft = ",sMaxLeft);
sMaxRight = min(iSearchRange,MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
sMaxRight = min(iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
//NumMessage("sMaxRight = ",sMaxRight);
// determine maximum vertical limits
sMaxUp = min(iSearchRange,(pSoldier->sGridNo / MAXROW));
sMaxUp = min(iSearchRange, (pSoldier->sGridNo / MAXROW));
//NumMessage("sMaxUp = ",sMaxUp);
sMaxDown = min(iSearchRange,MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
sMaxDown = min(iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
//NumMessage("sMaxDown = ",sMaxDown);
// Call FindBestPath to set flags in all locations that we can
// walk into within range. We have to set some things up first...
// set the distance limit of the square region
gubNPCDistLimit = (UINT8) iSearchRange;
// reset the "reachable" flags in the region we're looking at
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
@@ -1725,7 +1793,11 @@ INT32 FindNearbyDarkerSpot( SOLDIERTYPE *pSoldier )
}
}
FindBestPath( pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, DetermineMovementMode( pSoldier, AI_ACTION_LEAVE_WATER_GAS ), COPYREACHABLE, 0 );//dnl ch50 071009
gubNPCAPBudget = pSoldier->bActionPoints;
gubNPCDistLimit = (UINT8)iSearchRange;
FindBestPath(pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, usMovementMode, COPYREACHABLE, 0); //dnl ch50 071009
gubNPCAPBudget = 0;
gubNPCDistLimit = 0;
// Turn off the "reachable" flag for his current location
// so we don't consider it
@@ -1739,7 +1811,7 @@ INT32 FindNearbyDarkerSpot( SOLDIERTYPE *pSoldier )
// calculate the next potential gridno
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
//NumMessage("Testing gridno #",gridno);
if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) )
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
@@ -1750,7 +1822,18 @@ INT32 FindNearbyDarkerSpot( SOLDIERTYPE *pSoldier )
}
// ignore blacklisted spot
if ( sGridNo == pSoldier->pathing.sBlackList )
if (sGridNo == pSoldier->pathing.sBlackList)
{
continue;
}
// require this character to stay within their roam range
if (PythSpacesAway(sOrigin, sGridNo) > iRoamRange)
{
continue;
}
if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE))
{
continue;
}
@@ -1761,29 +1844,35 @@ INT32 FindNearbyDarkerSpot( SOLDIERTYPE *pSoldier )
continue;
}
// require this character to stay within their roam range
if ( PythSpacesAway( sOrigin, sGridNo ) > iRoamRange )
if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// screen out anything brighter than our current best spot
bLightLevel = LightTrueLevel( sGridNo, pSoldier->pathing.bLevel );
bLightLevel = LightTrueLevel(sGridNo, pSoldier->pathing.bLevel);
//bLightDiff = gbLightSighting[0][ bCurrLightLevel ] - gbLightSighting[0][ bLightLevel ];
bLightDiff = gGameExternalOptions.ubBrightnessVisionMod[ bCurrLightLevel ] - gGameExternalOptions.ubBrightnessVisionMod[ bLightLevel ];
bLightDiff = gGameExternalOptions.ubBrightnessVisionMod[bCurrLightLevel] - gGameExternalOptions.ubBrightnessVisionMod[bLightLevel];
// if the spot is darker than our current location, then bLightDiff > 0
// plus ignore differences of just 1 light level
if ( bLightDiff <= 1 )
if (bLightDiff <= 1)
{
continue;
}
// CJC: here, unfortunately, we must calculate a path so we have an AP cost
//sPathCost = LegalNPCDestination(pSoldier,sGridNo,ENSURE_PATH_COST,NOWATER,0);
sPathCost = LegalNPCDestination(pSoldier,sGridNo,ENSURE_PATH_COST,NOWATER,0);
if (!LegalNPCDestination(pSoldier, sGridNo, IGNORE_PATH, NOWATER, 0))
{
continue; // skip on to the next potential grid
}
if (!sPathCost)
sPathCost = PlotPath(pSoldier, sGridNo, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, pSoldier->bReverse, 0);
// check if spot is reachable
if (sPathCost == 0)
{
continue; // skip on to the next potential grid
}
@@ -1791,7 +1880,7 @@ INT32 FindNearbyDarkerSpot( SOLDIERTYPE *pSoldier )
// decrease the "cost" of the spot by the amount of light/darkness
iSpotValue = sPathCost * 2 - bLightDiff;
if ( iSpotValue < iBestSpotValue )
if (iSpotValue < iBestSpotValue)
{
// remember it instead
iBestSpotValue = iSpotValue;
@@ -2676,7 +2765,8 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction )
}
// sevenfm: don't go into deep water for flanking
if( DeepWater( sGridNo, pSoldier->pathing.bLevel ) )
if (DeepWater(sGridNo, pSoldier->pathing.bLevel) &&
!DeepWater(pSoldier->sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
@@ -2695,14 +2785,24 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction )
continue;
}
// sevenfm: skip buildings if not in building already, because soldiers often run into buildings and stop flanking
if (InARoom(sGridNo, NULL) && !InARoom(pSoldier->sGridNo, NULL))
{
continue;
}
// sevenfm: penalize locations near fresh corpses
if( GetNearestRottingCorpseAIWarning( sGridNo ) > 0 )
{
sTempDist = sTempDist / 2;
}
if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE))
{
continue;
}
// sevenfm: skip buildings if not in building already, because soldiers often run into buildings and stop flanking
if( InARoom( sGridNo, NULL ) && !InARoom(pSoldier->sGridNo, NULL) )
if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
+7
View File
@@ -297,6 +297,12 @@ BOOLEAN ProneSightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot );
BOOLEAN SightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot );
BOOLEAN FindBombNearby( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance );
UINT8 RedSmokeDanger(INT32 sGridNo, INT8 bLevel);
BOOLEAN CheckArtilleryStrike(void);
BOOLEAN CheckRoof(INT32 sGridNo);
UINT8 TerrainDensity(INT32 sSpot, INT8 bLevel, UINT8 ubDistance, BOOLEAN fGrass);
INT16 DistanceToClosestActiveOpponent(SOLDIERTYPE *pSoldier, INT32 sSpot);
BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* pOpponent);
BOOLEAN AnyCoverFromSpot( INT32 sSpot, INT8 bLevel, INT32 sThreatLoc, INT8 bThreatLevel );
UINT8 CountSeenEnemiesLastTurn( SOLDIERTYPE *pSoldier );
@@ -320,5 +326,6 @@ BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier);
// sevenfm: distance for tactical AI checks, roughly equal to normal day vision range
#define TACTICAL_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
#define BOMB_DETECTION_RANGE (TACTICAL_RANGE / 4)
#endif