From 93acd19c111b263a8cd8f0cd0997dcefa2ace52f Mon Sep 17 00:00:00 2001 From: Asdow <20314541+Asdow@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:05:54 +0200 Subject: [PATCH] Try to find a cover spot when advancing to attack Calling InternalGoAsFarAsPossibleTowards() with target's gridno means AI doesn't take into account eg. nearby friendlies resulting in them bunching up and making a beeline straight at whoever they're trying to attack. We'll first at least attempt to find a cover spot near said target, if it doesn't work, only then head straight at them. --- TacticalAI/DecideAction.cpp | 13 +++++++++- TacticalAI/FindLocations.cpp | 47 ++++++++++++++++++++---------------- TacticalAI/ai.h | 2 +- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 680b9c30..2fb4b0f5 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -6697,7 +6697,18 @@ L_NEWAIM: INT8 oldOrders = pSoldier->aiData.bOrders; pSoldier->aiData.sPatrolGrid[0] = pSoldier->sGridNo; pSoldier->aiData.bOrders = CLOSEPATROL; - pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards( pSoldier, sClosestOpponent, BestAttack.ubAPCost, AI_ACTION_GET_CLOSER, 0 ); + // Try to find a cover spot near opponent + iCoverPercentBetter = 0; + INT32 spotNearTarget = FindBestNearbyCover(pSoldier, pSoldier->aiData.bAIMorale, &iCoverPercentBetter, sClosestOpponent); + if (spotNearTarget != NOWHERE) + { + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, spotNearTarget, BestAttack.ubAPCost, AI_ACTION_GET_CLOSER, 0); + + } + else + { + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards( pSoldier, sClosestOpponent, BestAttack.ubAPCost, AI_ACTION_GET_CLOSER, 0 ); + } pSoldier->aiData.sPatrolGrid[0] = tgrd; pSoldier->aiData.bOrders = oldOrders; diff --git a/TacticalAI/FindLocations.cpp b/TacticalAI/FindLocations.cpp index 37fcd984..20f72e6c 100644 --- a/TacticalAI/FindLocations.cpp +++ b/TacticalAI/FindLocations.cpp @@ -612,7 +612,7 @@ UINT8 NumberOfTeamMatesAdjacent( SOLDIERTYPE * pSoldier, INT32 sGridNo ) return( ubCount ); } -INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentBetter) +INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentBetter, INT32 targetGridNo) { DebugMsg(TOPIC_JA2AI,DBG_LEVEL_3,String("FindBestNearbyCover")); @@ -649,6 +649,11 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB BOOLEAN fProneCover; UINT8 ubDiff = SoldierDifficultyLevel( pSoldier ); + if (targetGridNo == NOWHERE) + { + targetGridNo = pSoldier->sGridNo; + } + // There's no cover when boxing! if (gTacticalStatus.bBoxingState == BOXING) { @@ -710,9 +715,9 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB }*/ // maximum search range is 1 tile / 8 pts of wisdom - if (iSearchRange > (pSoldier->stats.bWisdom / 8)) + if (iSearchRange > (pSoldier->stats.bWisdom / 4)) { - iSearchRange = (pSoldier->stats.bWisdom / 8); + iSearchRange = (pSoldier->stats.bWisdom / 4); } if (!gfTurnBasedAI) @@ -777,13 +782,13 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB if (Threat[uiLoop].iOrigRange <= MAX_THREAT_RANGE) { // add this opponent's cover value to our current total cover value - iCurrentCoverValue += CalcCoverValue(pSoldier,pSoldier->sGridNo,iMyThreatValue,pSoldier->bActionPoints,uiLoop,Threat[uiLoop].iOrigRange,morale,&iCurrentScale); + iCurrentCoverValue += CalcCoverValue(pSoldier,targetGridNo,iMyThreatValue,pSoldier->bActionPoints,uiLoop,Threat[uiLoop].iOrigRange,morale,&iCurrentScale); } // sevenfm: sight test if( gGameExternalOptions.fAIBetterCover ) { - if ( LocationToLocationLineOfSightTest( Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, pSoldier->sGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS ) ) - //if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, pSoldier->sGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, NO_DISTANCE_LIMIT ) ) + if ( LocationToLocationLineOfSightTest( Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, targetGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS ) ) + //if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, targetGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, NO_DISTANCE_LIMIT ) ) { fProneCover = FALSE; } @@ -796,12 +801,12 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB // by 10% (so locations next to several people will be very much frowned upon if ( iCurrentCoverValue >= 0 ) { - iCurrentCoverValue -= (iCurrentCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, pSoldier->sGridNo ); + iCurrentCoverValue -= (iCurrentCoverValue / 5) * NumberOfTeamMatesAdjacent( pSoldier, targetGridNo ); } else { // when negative, must add a negative to decrease the total - iCurrentCoverValue += (iCurrentCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, pSoldier->sGridNo ); + iCurrentCoverValue += (iCurrentCoverValue / 5) * NumberOfTeamMatesAdjacent( pSoldier, targetGridNo ); } if( gGameExternalOptions.fAIBetterCover ) @@ -816,17 +821,17 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB } // sevenfm: check for nearby friends, add bonus/penalty - ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, pSoldier->sGridNo, 5 )); + ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, targetGridNo, 5 )); iCurrentCoverValue -= ubNearbyFriends * abs(iCurrentCoverValue) / (10-ubDiff); // sevenfm: penalize locations with fresh corpses - if(GetNearestRottingCorpseAIWarning( pSoldier->sGridNo ) > 0) + if(GetNearestRottingCorpseAIWarning( targetGridNo ) > 0) { iCurrentCoverValue -= abs(iCurrentCoverValue) / (8-ubDiff); } // sevenfm: penalize locations near red smoke - iCurrentCoverValue -= abs(iCurrentCoverValue) * RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel) / 100; + iCurrentCoverValue -= abs(iCurrentCoverValue) * RedSmokeDanger(targetGridNo, pSoldier->pathing.bLevel) / 100; } #ifdef DEBUGCOVER @@ -834,15 +839,15 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB #endif // determine maximum horizontal limits - sMaxLeft = min(iSearchRange,(pSoldier->sGridNo % MAXCOL)); + sMaxLeft = min(iSearchRange,(targetGridNo % MAXCOL)); //NumMessage("sMaxLeft = ",sMaxLeft); - sMaxRight = min(iSearchRange,MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1)); + sMaxRight = min(iSearchRange,MAXCOL - ((targetGridNo % MAXCOL) + 1)); //NumMessage("sMaxRight = ",sMaxRight); // determine maximum vertical limits - sMaxUp = min(iSearchRange,(pSoldier->sGridNo / MAXROW)); + sMaxUp = min(iSearchRange,(targetGridNo / MAXROW)); //NumMessage("sMaxUp = ",sMaxUp); - sMaxDown = min(iSearchRange,MAXROW - ((pSoldier->sGridNo / MAXROW) + 1)); + sMaxDown = min(iSearchRange,MAXROW - ((targetGridNo / MAXROW) + 1)); //NumMessage("sMaxDown = ",sMaxDown); iRoamRange = RoamingRange(pSoldier,&sOrigin); @@ -852,7 +857,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB (!TileIsOutOfBounds(sOrigin))) { // must try to stay within or return to the point of origin - iDistFromOrigin = SpacesAway(sOrigin,pSoldier->sGridNo); + iDistFromOrigin = SpacesAway(sOrigin,targetGridNo); } else { @@ -899,7 +904,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB { for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++) { - sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset); + sGridNo = targetGridNo + sXOffset + (MAXCOL * sYOffset); if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) ) { continue; @@ -912,7 +917,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB // Turn off the "reachable" flag for his current location // so we don't consider it - gpWorldLevelData[pSoldier->sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE); + gpWorldLevelData[targetGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE); // SET UP DOUBLE-LOOP TO STEP THROUGH POTENTIAL GRID #s for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++) @@ -922,7 +927,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB //HandleMyMouseCursor(KEYBOARDALSO); // calculate the next potential gridno - sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset); + sGridNo = targetGridNo + sXOffset + (MAXCOL * sYOffset); if ( !(sGridNo >=0 && sGridNo < WORLD_MAX) ) { continue; @@ -979,7 +984,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB // sevenfm: avoid moving into light if (InLightAtNight(sGridNo, pSoldier->pathing.bLevel) && - !InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel) && + !InLightAtNight(targetGridNo, pSoldier->pathing.bLevel) && !pSoldier->aiData.bUnderFire) { continue; @@ -988,7 +993,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB // avoid moving into red smoke if (gGameExternalOptions.fAIBetterCover && RedSmokeDanger(sGridNo, pSoldier->pathing.bLevel) && - !RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel)) + !RedSmokeDanger(targetGridNo, pSoldier->pathing.bLevel)) { //DebugCover(pSoldier, String("moving into red smoke, skip")); continue; diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index cd25caf5..60f0f45c 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -199,7 +199,7 @@ void EndAIGuysTurn( SOLDIERTYPE *pSoldier ); INT8 ExecuteAction(SOLDIERTYPE *pSoldier); INT32 FindAdjacentSpotBeside(SOLDIERTYPE *pSoldier, INT32 sGridNo); -INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *pPercentBetter); +INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *pPercentBetter, INT32 targetGridNo = NOWHERE); INT32 FindClosestDoor( SOLDIERTYPE * pSoldier ); INT32 FindNearbyPointOnEdgeOfMap( SOLDIERTYPE * pSoldier, INT8 * pbDirection ); INT32 FindNearestEdgePoint( INT32 sGridNo );