diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 9b5833a7b..9188113de 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -945,16 +945,22 @@ INT16 RandomFriendWithin(SOLDIERTYPE *pSoldier) continue; } - // if our movement range is NOT restricted - if (!fRangeRestricted || (SpacesAway(usOrigin,usDest) <= usMaxDist)) + if (SpacesAway(usOrigin, usDest) > usMaxDist) { - if (LegalNPCDestination(pSoldier,usDest,ENSURE_PATH,NOWATER, 0)) - { - fFound = TRUE; // found a spot - pSoldier->aiData.usActionData = usDest; // store this->pathing.sDestination - pSoldier->pathing.bPathStored = TRUE; // optimization - Ian - break; // stop checking in other directions - } + continue; + } + + if (!CheckNPCDestination(pSoldier, usDest, TRUE, TRUE)) + { + continue; + } + + if (LegalNPCDestination(pSoldier, usDest, ENSURE_PATH, NOWATER, 0)) + { + fFound = TRUE; // found a spot + pSoldier->aiData.usActionData = usDest; // store this->pathing.sDestination + pSoldier->pathing.bPathStored = TRUE; // optimization - Ian + break; // stop checking in other directions } } } @@ -1109,6 +1115,12 @@ INT32 RandDestWithinRange(SOLDIERTYPE *pSoldier) continue; } + if (!CheckNPCDestination(pSoldier, sRandDest, TRUE, TRUE)) + { + sRandDest = NOWHERE; + continue; + } + if (!LegalNPCDestination(pSoldier,sRandDest,ENSURE_PATH,NOWATER,0)) { sRandDest = NOWHERE; @@ -4212,6 +4224,47 @@ UINT8 CountTeamSeeSoldier( INT8 bTeam, SOLDIERTYPE *pSoldier ) return ubFriends; } +BOOLEAN CheckDoorAtGridno(UINT32 usGridNo) +{ + STRUCTURE *pStructure; + + pStructure = FindStructure(usGridNo, STRUCTURE_ANYDOOR); + if (pStructure != NULL) + { + return TRUE; + } + + return FALSE; +} + +BOOLEAN CheckDoorNearGridno(UINT32 usGridNo) +{ + UINT8 ubMovementCost; + INT32 sTempGridNo; + UINT8 ubDirection; + + if (CheckDoorAtGridno(usGridNo)) + { + return TRUE; + } + + for (ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ubDirection++) + { + sTempGridNo = NewGridNo(usGridNo, DirectionInc(ubDirection)); + + if (sTempGridNo != usGridNo) + { + ubMovementCost = gubWorldMovementCosts[sTempGridNo][ubDirection][0]; + if (IS_TRAVELCOST_DOOR(ubMovementCost)) + { + return TRUE; + } + } + } + + return FALSE; +} + BOOLEAN FindBombNearby( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance ) { UINT32 uiBombIndex; @@ -4361,6 +4414,34 @@ BOOLEAN CheckRoof(INT32 sGridNo) return FALSE; } + +BOOLEAN FindNearbyExplosiveStructure(INT32 sSpot, INT8 bLevel) +{ + INT32 sTempGridNo; + UINT8 ubDirection; + + if (TileIsOutOfBounds(sSpot)) + { + return 0; + } + + // check adjacent reachable tiles + for (ubDirection = 0; ubDirection < NUM_WORLD_DIRECTIONS; ubDirection++) + { + sTempGridNo = NewGridNo(sSpot, DirectionInc(ubDirection)); + + if (sTempGridNo != sSpot) + { + if (FindStructFlag(sTempGridNo, bLevel, STRUCTURE_EXPLOSIVE)) + { + return TRUE; + } + } + } + + return FALSE; +} + UINT8 TerrainDensity(INT32 sSpot, INT8 bLevel, UINT8 ubDistance, BOOLEAN fGrass) { if (TileIsOutOfBounds(sSpot)) @@ -4619,4 +4700,60 @@ BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier) return FALSE; return TRUE; -} \ No newline at end of file +} + +UINT8 SpotDangerLevel(SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fCheckWater, BOOLEAN fCheckLight) +{ + if (!pSoldier) + return 0; + + if (TileIsOutOfBounds(sGridNo)) + return 0; + + BOOLEAN fCautious = FALSE; + BOOLEAN fGreen = FALSE; + UINT8 ubLevel = 0; + + if ((pSoldier->aiData.bAlertStatus >= STATUS_RED || pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE)) + fCautious = TRUE; + + if (pSoldier->aiData.bAlertStatus < STATUS_YELLOW) + fGreen = TRUE; + + if (!gGameExternalOptions.fAITacticalRetreat && NorthSpot(sGridNo, pSoldier->pathing.bLevel) || + pSoldier->ubProfile == NO_PROFILE && fGreen && CheckDoorNearGridno(sGridNo)) + ubLevel = 1; + + if (fCautious && (fCheckLight && InLightAtNight(sGridNo, pSoldier->pathing.bLevel) || FindNearbyExplosiveStructure(sGridNo, pSoldier->pathing.bLevel))) + ubLevel = 2; + + if (fCheckWater && DeepWater(sGridNo, pSoldier->pathing.bLevel) || + RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel)) + ubLevel = 3; + + if (InGas(pSoldier, sGridNo) || + FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE)) + ubLevel = 4; + + return ubLevel; +} + +BOOLEAN CheckNPCDestination(SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fCheckWater, BOOLEAN fCheckLight) +{ + if (!pSoldier) + return FALSE; + + if (TileIsOutOfBounds(sGridNo)) + return FALSE; + + // find current danger level + UINT8 ubLevel = SpotDangerLevel(pSoldier, pSoldier->sGridNo, fCheckWater, fCheckLight); + // find danger level at target spot + UINT8 ubTargetLevel = SpotDangerLevel(pSoldier, sGridNo, fCheckWater, fCheckLight); + + // avoid moving into dangerous spot + if (ubTargetLevel > 0 && ubTargetLevel >= ubLevel) + return FALSE; + + return TRUE; +} diff --git a/TacticalAI/FindLocations.cpp b/TacticalAI/FindLocations.cpp index 41ae37454..cc2848468 100644 --- a/TacticalAI/FindLocations.cpp +++ b/TacticalAI/FindLocations.cpp @@ -1499,19 +1499,7 @@ INT32 FindSpotMaxDistFromOpponents(SOLDIERTYPE *pSoldier) continue; } - //Madd: skip lighted spots - if ( InLightAtNight( sGridNo, pSoldier->pathing.bLevel ) ) - continue; - - // sevenfm: avoid staying at north edge - if (!gGameExternalOptions.fAITacticalRetreat && - NorthSpot(sGridNo, pSoldier->pathing.bLevel)) - { - continue; - } - - // sevenfm: avoid red smoke - if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel)) + if (!CheckNPCDestination(pSoldier, sGridNo, TRUE, TRUE)) { continue; } @@ -1665,38 +1653,7 @@ INT32 FindNearestUngassedLand(SOLDIERTYPE *pSoldier) 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; - } - - // sevenfm: avoid staying at north edge - if (NorthSpot(sGridNo, pSoldier->pathing.bLevel)) - { - continue; - } - - // check for red smoke - if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) > 0) + if (!CheckNPCDestination(pSoldier, sGridNo, TRUE, FALSE)) { continue; } @@ -1839,18 +1796,12 @@ INT32 FindNearbyDarkerSpot(SOLDIERTYPE *pSoldier) continue; } - if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE)) + if (!CheckNPCDestination(pSoldier, sGridNo, TRUE, TRUE)) { continue; } - // sevenfm: avoid staying at north edge - if (NorthSpot(sGridNo, pSoldier->pathing.bLevel)) - { - continue; - } - - if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel)) + if (InLightAtNight(sGridNo, pSoldier->pathing.bLevel)) { continue; } @@ -2026,24 +1977,7 @@ INT8 SearchForItems( SOLDIERTYPE * pSoldier, INT8 bReason, UINT16 usItem ) continue; } - // exclude locations with tear/mustard gas (at this point, smoke is cool!) - if (InGas(pSoldier, sGridNo)) - { - continue; - } - - if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE)) - { - continue; - } - - // sevenfm: avoid staying at north edge - /*if (NorthSpot(sGridNo, pSoldier->pathing.bLevel)) - { - continue; - }*/ - - if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel)) + if (!CheckNPCDestination(pSoldier, sGridNo, TRUE, FALSE)) { continue; } @@ -2775,16 +2709,6 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction ) continue; } - // exclude locations with tear/mustard gas (at this point, smoke is cool!) - if ( InGas( pSoldier, sGridNo ) ) - { - continue; - } - - //Madd: skip lighted spots - if ( InLightAtNight( sGridNo, pSoldier->pathing.bLevel ) ) - continue; - // sevenfm: skip tiles too close to edge if ( PythSpacesAway( FindNearestEdgePoint ( sGridNo ), sGridNo ) <= 2 ) { @@ -2806,8 +2730,13 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction ) continue; } - // sevenfm: avoid staying at north edge - if (NorthSpot(sGridNo, pSoldier->pathing.bLevel)) + // avoid fresh corpses + if (GetNearestRottingCorpseAIWarning(sGridNo) > 0) + { + continue; + } + + if (!CheckNPCDestination(pSoldier, sGridNo, FALSE, TRUE)) { continue; } @@ -2823,20 +2752,16 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction ) continue; } - // sevenfm: penalize locations near fresh corpses - if( GetNearestRottingCorpseAIWarning( sGridNo ) > 0 ) + // sevenfm: penalize locations too far from noise gridno + if (PythSpacesAway(sGridNo, sPos) > MAX_FLANK_DIST_RED) { sTempDist = sTempDist / 2; - } - - if (FindBombNearby(pSoldier, sGridNo, BOMB_DETECTION_RANGE)) - { - continue; } - if (RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel)) + // sevenfm: try to flank closer to vision distance limit for faster flanking + if (PythSpacesAway(sGridNo, sPos) > sDistanceVisible + 10) { - continue; + sTempDist = sTempDist / 2; } // sevenfm: penalize locations with no sight cover from noise gridno (supposed that we are sneaking) @@ -2847,18 +2772,6 @@ INT32 FindFlankingSpot(SOLDIERTYPE *pSoldier, INT32 sPos, INT8 bAction ) sTempDist = sTempDist / 2; } - // sevenfm: penalize locations too far from noise gridno - if( PythSpacesAway( sGridNo, sPos) > MAX_FLANK_DIST_RED ) - { - sTempDist = sTempDist / 2; - } - - // sevenfm: try to flank closer to vision distance limit for faster flanking - if( PythSpacesAway( sGridNo, sPos) > sDistanceVisible + 10 ) - { - sTempDist = sTempDist / 2; - } - // allow extra directions for flanking if ( bAction == AI_ACTION_FLANK_LEFT ) { diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index b03060d49..9bcab8309 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -296,17 +296,23 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier); BOOLEAN ProneSightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot ); BOOLEAN SightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot ); +BOOLEAN CheckDoorAtGridno(UINT32 usGridNo); +BOOLEAN CheckDoorNearGridno(UINT32 usGridNo); 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); +BOOLEAN FindNearbyExplosiveStructure(INT32 sSpot, INT8 bLevel); 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 ); +BOOLEAN CheckNPCDestination(SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fCheckWater, BOOLEAN fCheckLight); +UINT8 SpotDangerLevel(SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fCheckWater, BOOLEAN fCheckLight); + BOOLEAN NorthSpot(INT32 sSpot, INT8 bLevel); BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier); diff --git a/TileEngine/worldman.cpp b/TileEngine/worldman.cpp index 1577287e7..97989be57 100644 --- a/TileEngine/worldman.cpp +++ b/TileEngine/worldman.cpp @@ -1478,6 +1478,59 @@ BOOLEAN RemoveStruct( INT32 iMapIndex, UINT16 usIndex ) return( FALSE ); } +BOOLEAN FindStruct(INT32 sSpot, INT8 bLevel, UINT16 usIndex) +{ + if (TileIsOutOfBounds(sSpot)) + return FALSE; + + LEVELNODE *pStruct = NULL; + LEVELNODE *pOldStruct = NULL; + + if (bLevel == 0) + { + pStruct = gpWorldLevelData[sSpot].pStructHead; + } + else + { + pStruct = gpWorldLevelData[sSpot].pOnRoofHead; + } + + // Look through all structs + while (pStruct != NULL) + { + if (pStruct->usIndex == usIndex) + { + return(TRUE); + } + + pOldStruct = pStruct; + pStruct = pStruct->pNext; + } + + return FALSE; +} + +BOOLEAN FindStructFlag(INT32 sSpot, INT8 bLevel, UINT32 uiFlag) +{ + if (bLevel > 0) + return FALSE; + + if (TileIsOutOfBounds(sSpot)) + return FALSE; + + STRUCTURE * pStructure; + + pStructure = FindStructure(sSpot, uiFlag); + + if (pStructure) + { + return TRUE; + } + + + return FALSE; +} + // same as RemoveStruct(...), except that this focuses on .pOnRoofHead instead of .pStructHead BOOLEAN RemoveOnRoofStruct( INT32 iMapIndex, UINT16 usIndex ) { diff --git a/TileEngine/worldman.h b/TileEngine/worldman.h index e22202eb7..74a2fdc43 100644 --- a/TileEngine/worldman.h +++ b/TileEngine/worldman.h @@ -170,4 +170,8 @@ void SetTreeTopStateForMap(); BOOLEAN IsOknoLevel( INT32 sGridNo ); BOOLEAN IsLegionLevel( INT32 sGridNo ); +// sevenfm +BOOLEAN FindStruct(INT32 sSpot, INT8 bLevel, UINT16 usIndex); +BOOLEAN FindStructFlag(INT32 sSpot, INT8 bLevel, UINT32 uiFlag); + #endif \ No newline at end of file