diff --git a/Tactical/Boxing.cpp b/Tactical/Boxing.cpp index 02db51b6d..05a4b4a08 100644 --- a/Tactical/Boxing.cpp +++ b/Tactical/Boxing.cpp @@ -22,6 +22,7 @@ #include "Font Control.h" #include "message.h" #include "GameSettings.h" // added by SANDRO + #include "Soldier macros.h" INT32 gsBoxerGridNo[ NUM_BOXERS ] = { 11393, 11233, 11073 }; UINT8 gubBoxerID[ NUM_BOXERS ] = { NOBODY, NOBODY, NOBODY }; @@ -55,7 +56,7 @@ void ExitBoxing( void ) if ( pSoldier != NULL ) { - if ( ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) && InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING ) + if ( BOXER(pSoldier) && InARoom( pSoldier->sGridNo, &usRoom ) && usRoom == BOXING_RING ) { if ( pSoldier->flags.uiStatusFlags & SOLDIER_PC ) { @@ -239,7 +240,7 @@ void CountPeopleInBoxingRingAndDoActions( void ) { ++ubPlayersInRing; - if ( !pNonBoxingPlayer && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( !pNonBoxingPlayer && !BOXER(pSoldier) ) { pNonBoxingPlayer = pSoldier; } @@ -291,7 +292,7 @@ void CountPeopleInBoxingRingAndDoActions( void ) // ladieees and gennleman, we have a fight! for (uiLoop = 0; uiLoop < 2; ++uiLoop) { - if (!(pInRing[uiLoop]->flags.uiStatusFlags & SOLDIER_BOXER)) + if (!BOXER(pInRing[uiLoop])) { // set as boxer! pInRing[uiLoop]->flags.uiStatusFlags |= SOLDIER_BOXER; @@ -515,7 +516,7 @@ void BoxingMovementCheck( SOLDIERTYPE * pSoldier ) // someone moving in/into the ring CountPeopleInBoxingRingAndDoActions(); } - else if ( ( gTacticalStatus.bBoxingState == BOXING ) && ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) ) + else if ( ( gTacticalStatus.bBoxingState == BOXING ) && BOXER(pSoldier) ) { // boxer stepped out of the ring! BoxingPlayerDisqualified( pSoldier, BOXER_OUT_OF_RING ); @@ -565,7 +566,7 @@ void ClearAllBoxerFlags( void ) { for (UINT32 uiSlot = 0; uiSlot < guiNumMercSlots; ++uiSlot) { - if ( MercSlots[ uiSlot ] && MercSlots[ uiSlot ]->flags.uiStatusFlags & SOLDIER_BOXER ) + if ( MercSlots[ uiSlot ] && BOXER(MercSlots[ uiSlot ]) ) { // Flugente: nuke the entire opponent count, remove boxing flag, reevaluate opponent list DecayIndividualOpplist(MercSlots[uiSlot]); diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index e42433812..9aa9fc8fe 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -87,7 +87,7 @@ #include "SaveLoadScreen.h" #include "Map Screen Interface.h" // added by Flugente for SquadNames #include "Keys.h" // added by silversurfer for door handling from the side - +#include "Cheats.h" #include "AIInternals.h" extern BOOLEAN gubWorldTileInLight[MAX_ALLOWED_WORLD_MAX]; extern BOOLEAN gubIsCorpseThere[MAX_ALLOWED_WORLD_MAX]; @@ -386,7 +386,7 @@ BOOLEAN gfDisplayTimerCursor = FALSE; UINT32 guiTimerCursorID = 0; UINT32 guiTimerLastUpdate = 0; UINT32 guiTimerCursorDelay = 0; - +UINT8 gRenderDebugInfoMode = DEBUG_OFF; CHAR16 gzLocation[ 20 ]; BOOLEAN gfLocation = FALSE; @@ -518,6 +518,54 @@ void GetMercOknoDirection( UINT8 ubSoldierID, BOOLEAN *pfGoDown, BOOLEAN *pfGoUp } //---------------------------------------------------------------------------------- +void HandleRenderDebugInfoModes() +{ + if (DEBUG_CHEAT_LEVEL()) + { + switch (gRenderDebugInfoMode) + { + case DEBUG_PATHFINDING: + // Nothing to do here, pathfinding info is filled in the pathing functions. + break; + case DEBUG_THREATVALUE: + break; + case DEBUG_COVERVALUE: + // Calculate cover values for pSoldier under cursor, or for currently selected merc, if nobody is under the cursor. + if (gTacticalStatus.Team[OUR_TEAM].bTeamActive) + { + static SOLDIERTYPE* previousSoldier = nullptr; + static INT32 previousLocation = NOWHERE; + static UINT8 previousStance = 0; + + UINT16 usSoldierIndex = NOBODY; + UINT32 uiMercFlags; + FindSoldierFromMouse(&usSoldierIndex, &uiMercFlags); + if (usSoldierIndex == NOBODY) + { + usSoldierIndex = gusSelectedSoldier; + } + + if (usSoldierIndex != NOBODY) + { + // Get Soldier + INT32 iPercentBetter; + SOLDIERTYPE* pSoldier; + GetSoldier(&pSoldier, usSoldierIndex); + if (previousSoldier != pSoldier || previousLocation != pSoldier->sGridNo || previousStance != gAnimControl[pSoldier->usAnimState].ubEndHeight) + { + FindBestNearbyCover(pSoldier, pSoldier->aiData.bAIMorale, &iPercentBetter); + previousSoldier = pSoldier; + previousLocation = pSoldier->sGridNo; + previousStance = gAnimControl[pSoldier->usAnimState].ubEndHeight; + } + } + } + break; + default: // off + break; + } + } +} void PreventFromTheFreezingBug(SOLDIERTYPE* pSoldier) { @@ -691,6 +739,7 @@ UINT32 HandleTacticalUI( void ) } } + HandleRenderDebugInfoModes(); // Check if current event has changed and clear event if so, to prepare it for execution // Clearing it does things like set first time flag, param variables, etc if ( uiNewEvent != guiOldEvent ) diff --git a/Tactical/Handle UI.h b/Tactical/Handle UI.h index 5937d9112..bdcc6a1f1 100644 --- a/Tactical/Handle UI.h +++ b/Tactical/Handle UI.h @@ -296,6 +296,8 @@ extern BOOLEAN gfUIForceReExamineCursorData; extern INT16 guiCreateGuyIndex; extern INT16 guiCreateBadGuyIndex; +extern UINT8 gRenderDebugInfoMode; + // WANNE: Calculate the APs to turn around INT16 APsToTurnAround(SOLDIERTYPE *pSoldier, INT32 sAdjustedGridNo); @@ -382,4 +384,4 @@ void GetGridNoScreenXY( INT32 sGridNo, INT16 *pScreenX, INT16 *pScreenY ); //Legion by Jazz void GetMercOknoDirection( UINT8 ubSoldierID, BOOLEAN *pfGoDown, BOOLEAN *pfGoUp ); -#endif \ No newline at end of file +#endif diff --git a/Tactical/Interface.cpp b/Tactical/Interface.cpp index 5ab367503..42f3e9d0b 100644 --- a/Tactical/Interface.cpp +++ b/Tactical/Interface.cpp @@ -6299,7 +6299,7 @@ BOOLEAN ShowSoldierRoleSymbol(SOLDIERTYPE* pSoldier) if ( pSoldier->usSkillCounter[SOLDIER_COUNTER_ROLE_OBSERVED] >= gGameExternalOptions.usTurnsToUncover ) { // are we a VIP? show that only when the player knows a VIP is in this sector. otherwise, don't even show our officer property - if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP && !pSoldier->bSectorZ ) + if (ISVIP(pSoldier) && !pSoldier->bSectorZ ) { if ( PlayerKnowsAboutVIP( pSoldier->sSectorX, pSoldier->sSectorY ) ) { diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 3052c2bb1..2a85df93e 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -3043,7 +3043,7 @@ BOOLEAN HandleAtNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving ) // sevenfm: check all nearby enemy boxers for opportunity attack if (IS_MERC_BODY_TYPE(pSoldier) && - (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pSoldier) && gTacticalStatus.bBoxingState == BOXING && pSoldier->aiData.bAlertStatus >= STATUS_RED) { @@ -3064,7 +3064,7 @@ BOOLEAN HandleAtNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving ) pOpponent->bCollapsed || pOpponent->bBreathCollapsed || !IS_MERC_BODY_TYPE(pOpponent) || - !(pOpponent->flags.uiStatusFlags & SOLDIER_BOXER) || + !BOXER(pOpponent) || gAnimControl[pOpponent->usAnimState].ubEndHeight < ANIM_STAND || pOpponent->pathing.bLevel != pSoldier->pathing.bLevel || !SoldierToSoldierLineOfSightTest(pOpponent, pSoldier, TRUE, CALC_FROM_WANTED_DIR) || @@ -7233,7 +7233,7 @@ void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) //if ( pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->stats.bLife != 0 ) { // officers and generals are 'special' prisoners... - if ( pTeamSoldier->usSoldierFlagMask & SOLDIER_VIP ) + if (ISVIP(pTeamSoldier)) ++sNumPrisoner[PRISONER_GENERAL]; // downed pilots count as officers too, even though they are civilians. This makes capturing them more rewarding else if ( (pTeamSoldier->usSoldierFlagMask & SOLDIER_ENEMY_OFFICER) || pTeamSoldier->ubCivilianGroup == DOWNEDPILOT_CIV_GROUP ) @@ -7262,7 +7262,7 @@ void RemoveCapturedEnemiesFromSectorInfo( INT16 sMapX, INT16 sMapY, INT8 bMapZ ) } // Flugente: VIPs - if ( pTeamSoldier->usSoldierFlagMask & SOLDIER_VIP ) + if (ISVIP(pTeamSoldier)) DeleteVIP( pTeamSoldier->sSectorX, pTeamSoldier->sSectorY ); // Flugente: turncoats @@ -9320,10 +9320,10 @@ BOOLEAN ProcessImplicationsOfPCAttack( SOLDIERTYPE * pSoldier, SOLDIERTYPE ** pp if ( gTacticalStatus.bBoxingState == BOXING ) { // should have a check for "in boxing ring", no? - if ( ( pSoldier->usAttackingWeapon != NOTHING && !Item[pSoldier->usAttackingWeapon].brassknuckles ) || !( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) || pSoldier->IsRiotShieldEquipped() ) + if ( ( pSoldier->usAttackingWeapon != NOTHING && !Item[pSoldier->usAttackingWeapon].brassknuckles ) || !BOXER(pSoldier) || pSoldier->IsRiotShieldEquipped() ) { // someone's cheating! - if ( (Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_BLADE || Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_PUNCH) && (pTarget->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( (Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_BLADE || Item[ pSoldier->usAttackingWeapon ].usItemClass == IC_PUNCH) && BOXER(pTarget) ) { // knife or brass knuckles disqualify the player! BoxingPlayerDisqualified( pSoldier, BAD_ATTACK ); @@ -9334,7 +9334,7 @@ BOOLEAN ProcessImplicationsOfPCAttack( SOLDIERTYPE * pSoldier, SOLDIERTYPE ** pp //gTacticalStatus.bBoxingState = NOT_BOXING; SetBoxingState( NOT_BOXING ); // if we are attacking a boxer we should set them to neutral (temporarily) so that the rest of the civgroup code works... - if ( (pTarget->bTeam == CIV_TEAM) && (pTarget->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( (pTarget->bTeam == CIV_TEAM) && BOXER(pTarget) ) { SetSoldierNeutral( pTarget ); } @@ -9481,7 +9481,7 @@ BOOLEAN ProcessImplicationsOfPCAttack( SOLDIERTYPE * pSoldier, SOLDIERTYPE ** pp //TriggerNPCWithIHateYouQuote( pTarget->ubProfile ); } } - else if ( pTarget->ubCivilianGroup != NON_CIV_GROUP && !( pTarget->flags.uiStatusFlags & SOLDIER_BOXER ) ) + else if ( pTarget->ubCivilianGroup != NON_CIV_GROUP && !BOXER(pTarget) ) { // Firing at a civ in a civ group who isn't hostile... if anyone in that civ group can see this // going on they should become hostile. @@ -10927,7 +10927,7 @@ void TurnCoatAttemptMessageBoxCallBack( UINT8 ubExitValue ) UINT8 approachchance = MercPtrs[gusSelectedSoldier]->GetTurncoatConvinctionChance( prisonerdialoguetargetID, approachselected ); // you can never turn a VIP (though we don't tell the player if someone is a VIP, lest they have an exploit to find out) - if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP ) + if (ISVIP(pSoldier)) approachchance = 0; // as using random numbers to pass the check would result in players savescumming, use a number based on the soldier's stats diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index 02a8067d3..882c6e471 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -25,7 +25,7 @@ #include "english.h" #include "worlddef.h" #include "worldman.h" -// #include "renderworld.h" + #include "renderworld.h" #include "pathai.h" #include "Points.h" #include "ai.h" @@ -41,7 +41,7 @@ #include "Rotting Corpses.h" #include "Meanwhile.h" #include "connect.h" - +#include #include "LOS.h" //ddd //forward declarations of common classes to eliminate includes @@ -66,19 +66,8 @@ extern BOOLEAN InGasSpot(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel); // skiplist has extra level of pointers every 4 elements, so a level 5is optimized for // 4 to the power of 5 elements, or 2 to the power of 10, 1024 -//#define PATHAI_VISIBLE_DEBUG //#define PATHAI_SKIPLIST_DEBUG - -#ifdef PATHAI_VISIBLE_DEBUG - #include "video.h" - -//extern INT16 gsCoverValue[WORLD_MAX]; -extern INT16 * gsCoverValue; - BOOLEAN gfDisplayCoverValues = TRUE; - BOOLEAN gfDrawPathPoints = TRUE; -#endif - BOOLEAN gfPlotPathToExitGrid = FALSE; BOOLEAN gfRecalculatingExistingPathCost = FALSE; UINT8 gubGlobalPathFlags = 0; @@ -623,15 +612,12 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , } - -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { - memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX ); + ResetDebugInfoValues(); + gRenderDebugInfoValues[ StartNode ] = 0; + PATHAI_VISIBLE_DEBUG_Counter = 1; } - gsCoverValue[ StartNode ] = 0; - PATHAI_VISIBLE_DEBUG_Counter = 1; -#endif //init other private data, mostly flags endDir = lastDir = direction = startDir = 0; @@ -796,12 +782,10 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , return 0; } -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { SetRenderFlags( RENDER_FLAG_FULL ); } -#endif // Count the number of steps, but keep it less than the max path length. // Adjust the parent until it begins at the tail end of the max path length (or the dest if reachable) @@ -884,12 +868,10 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , sizePath = giPathDataSize; } -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { SetRenderFlags( RENDER_FLAG_FULL ); } -#endif #ifdef COUNT_PATHS guiSuccessfulPathChecks++; @@ -1001,15 +983,13 @@ void AStarPathfinder::ExecuteAStarLogic() SetAStarStatus(ParentNode, AStar_Closed); //ClosedList.push_back(ParentNode); -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { - if (gsCoverValue[ ParentNode ] > 0) + if (gRenderDebugInfoValues[ParentNode] > 0) { - gsCoverValue[ ParentNode ] *= -1; + gRenderDebugInfoValues[ParentNode] *= -1; } } -#endif // Shouldn't G and AP be the same thing? INT16 baseGCost = GetAStarG(ParentNode); @@ -1092,7 +1072,7 @@ void AStarPathfinder::ExecuteAStarLogic() gpWorldLevelData[ CurrentNode ].ubExtFlags[0] |= MAPELEMENT_EXT_CLIMBPOINT; gpWorldLevelData[ ParentNode ].ubExtFlags[1] |= MAPELEMENT_EXT_CLIMBPOINT; #ifdef ROOF_DEBUG - gsCoverValue[CurrentNode] = 1; + gRenderDebugInfoValues[CurrentNode] = 1; #endif } @@ -1190,26 +1170,13 @@ void AStarPathfinder::ExecuteAStarLogic() int AStarH = CalcH(); int AStarF = (AStarG + extraGCoverCost) + AStarH; -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { - if (gsCoverValue[CurrentNode] == 0x7F7F) + if (gRenderDebugInfoValues[CurrentNode] == 0x7FFFFFFF) { - //gsCoverValue[CurrentNode] = PATHAI_VISIBLE_DEBUG_Counter++; - gsCoverValue[CurrentNode] = (INT16) AStarF; + gRenderDebugInfoValues[CurrentNode] = (INT16) AStarF; } - /* - else if (gsCoverValue[CurrentNodeIndex] >= 0) - { - gsCoverValue[CurrentNodeIndex]++; - } - else - { - gsCoverValue[CurrentNodeIndex]--; - } - */ } -#endif //insert this node onto the heap if (GetAStarStatus(CurrentNode) == AStar_Init) @@ -2260,9 +2227,7 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT32 sDestination, INT8 bLevel, INT16 usMov CHAR8 zTempString[1000], zTS[50]; #endif -#ifdef PATHAI_VISIBLE_DEBUG UINT16 usCounter = 0; -#endif fVehicle = FALSE; iOriginationX = iOriginationY = 0; @@ -2512,12 +2477,10 @@ if(!GridNoOnVisibleWorldTile(iDestination)) memset( pathQ, 0, iMaxPathQ * sizeof( path_t ) ); memset( trailTree, 0, iMaxTrailTree * sizeof( trail_t ) ); -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { - memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX ); + ResetDebugInfoValues(); } -#endif bSkipListLevel = 1; iSkipListSize = 0; @@ -2629,15 +2592,13 @@ if(!GridNoOnVisibleWorldTile(iDestination)) // remember the cost used to get here... prevCost = gubWorldMovementCosts[ trailTree[ sCurPathNdx ].sGridNo ][ trailTree[ sCurPathNdx ].stepDir ][ bLevel ]; -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) { - if (gsCoverValue[ curLoc ] > 0) + if (gRenderDebugInfoValues[ curLoc ] > 0) { - gsCoverValue[ curLoc ] *= -1; + gRenderDebugInfoValues[ curLoc ] *= -1; } } -#endif /* if (fTurnSlow) @@ -3563,27 +3524,13 @@ if(!GridNoOnVisibleWorldTile(iDestination)) // costs less than the best so far to the same location? if (trailCostUsed[newLoc] != gubGlobalPathCount || newTotCost < trailCost[newLoc]) { - - #if defined( PATHAI_VISIBLE_DEBUG ) - - if (gfDisplayCoverValues && gfDrawPathPoints) - { - if (gsCoverValue[newLoc] == 0x7F7F) - { - gsCoverValue[newLoc] = usCounter++; - } - /* - else if (gsCoverValue[newLoc] >= 0) - { - gsCoverValue[newLoc]++; - } - else + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) + { + if (gRenderDebugInfoValues[newLoc] == 0x7FFFFFFF) { - gsCoverValue[newLoc]--; + gRenderDebugInfoValues[newLoc] = usCounter++; } - */ - } - #endif + } //NEWQUENODE; { @@ -3826,20 +3773,13 @@ if(!GridNoOnVisibleWorldTile(iDestination)) while (pathQNotEmpty && pathNotYetFound); - #if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) + { + if ( guiCurrentScreen == GAME_SCREEN ) { - SetRenderFlags( RENDER_FLAG_FULL ); - if ( guiCurrentScreen == GAME_SCREEN ) - { - RenderWorld(); - RenderCoverDebug( ); - InvalidateScreen( ); - EndFrameBufferRender(); - RefreshScreen( NULL ); - } + InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y); } - #endif + } // work finished. Did we find a path? @@ -3894,17 +3834,10 @@ if(!GridNoOnVisibleWorldTile(iDestination)) } - #if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) - { - SetRenderFlags( RENDER_FLAG_FULL ); - RenderWorld(); - RenderCoverDebug( ); - InvalidateScreen( ); - EndFrameBufferRender(); - RefreshScreen( NULL ); - } - #endif + if (gRenderDebugInfoMode == DEBUG_PATHFINDING && DEBUG_CHEAT_LEVEL()) + { + InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y); + } // return path length : serves as a "successful" flag and a path length counter diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index eda237d26..ad304f876 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -4234,7 +4234,7 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) } // Flugente: VIPs - if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP ) + if (ISVIP(pSoldier)) { DeleteVIP( pSoldier->sSectorX, pSoldier->sSectorY ); } diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index dacb8dfc8..b86d1483a 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -9847,7 +9847,7 @@ void SOLDIERTYPE::BeginSoldierGetup( void ) else { this->bTurnsCollapsed++; - if ( (gTacticalStatus.bBoxingState == BOXING) && (this->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( (gTacticalStatus.bBoxingState == BOXING) && (BOXER(this)) ) { if ( this->bTurnsCollapsed > 1 ) { diff --git a/Tactical/Soldier macros.h b/Tactical/Soldier macros.h index af0a32fe3..f31c83fff 100644 --- a/Tactical/Soldier macros.h +++ b/Tactical/Soldier macros.h @@ -37,8 +37,10 @@ #define TANK( p ) (p->ubBodyType == TANK_NE || p->ubBodyType == TANK_NW ) #define ENEMYROBOT( p ) (p->ubBodyType == ROBOTNOWEAPON && p->bTeam == ENEMY_TEAM) #define ARMED_VEHICLE( p ) ( TANK( p ) || COMBAT_JEEP(p) ) +#define BOXER( p ) ( p->flags.uiStatusFlags & SOLDIER_BOXER ) +#define ISVIP( p ) ( p->usSoldierFlagMask & SOLDIER_VIP ) //#define OK_ENTERABLE_VEHICLE( p ) ( ( p->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !TANK( p ) && p->stats.bLife >= OKLIFE ) #define OK_ENTERABLE_VEHICLE( p ) ( ( p->flags.uiStatusFlags & SOLDIER_VEHICLE ) && (!ARMED_VEHICLE( p ) || !(p->flags.uiStatusFlags & SOLDIER_ENEMY) ) && p->stats.bLife >= OKLIFE ) -#endif \ No newline at end of file +#endif diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index ddf9e4430..824c180a9 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -1224,10 +1224,10 @@ void EndInterrupt( BOOLEAN fMarkInterruptOccurred ) else { ubInterruptedSoldier = LATEST_INTERRUPT_GUY; + pSoldier = MercPtrs[ubInterruptedSoldier]; - DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: interrupt over, %d's team regains control", ubInterruptedSoldier ) ); + DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("INTERRUPT: interrupt over, soldier %d's team %d regains control", ubInterruptedSoldier, pSoldier->bTeam ) ); - pSoldier = MercPtrs[ubInterruptedSoldier]; cnt = 0; for ( pTempSoldier = MercPtrs[ cnt ]; cnt < MAX_NUM_SOLDIERS; cnt++,pTempSoldier++) diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index abc773808..3e71b6831 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -1700,6 +1700,24 @@ void ItemCreationCallBack( UINT8 ubResult ) memset(gszMsgBoxInputString,0,sizeof(gszMsgBoxInputString)); } +static void CycleThroughTileDebugInfo() +{ + const STR16 modeStrings[] = + { + L"Pathfinding", + L"Threat values", + L"Cover values", + L"Off", + }; + + gRenderDebugInfoMode += 1; + if (gRenderDebugInfoMode > DEBUG_OFF) + { + gRenderDebugInfoMode = 0; + } + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, modeStrings[gRenderDebugInfoMode]); +} + extern BOOLEAN gfDisableRegionActive; extern BOOLEAN gfUserTurnRegionActive; @@ -4763,6 +4781,13 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) break; case 'Z': + if (fCtrl) + { + if (DEBUG_CHEAT_LEVEL()) + { + CycleThroughTileDebugInfo(); + } + } break; } diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index cdc60e02a..f421763f7 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -4260,10 +4260,10 @@ BOOLEAN UseHandToHand( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, BOOLEAN fStea // sevenfm: bonus for boxers for attack from the back if (iHitChance < 100 && - (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pSoldier) && !pSoldier->bBlindedCounter && gAnimControl[pTargetSoldier->usAnimState].ubEndHeight > ANIM_PRONE && - (pTargetSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pTargetSoldier) && pTargetSoldier->usSoldierFlagMask2 & SOLDIER_BACK_ATTACK) { iHitChance += (100 - iHitChance) / 2; @@ -4879,7 +4879,7 @@ BOOLEAN UseHandToHand( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo, BOOLEAN fStea if (pTargetSoldier->bActionPoints > 0 && gGameOptions.fNewTraitSystem && gTacticalStatus.bBoxingState == BOXING && - (pTargetSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pTargetSoldier) && Chance(ubCounterattackChance) && IS_MERC_BODY_TYPE(pSoldier) && IS_MERC_BODY_TYPE(pTargetSoldier) && @@ -9719,7 +9719,7 @@ UINT32 CalcChanceHTH( SOLDIERTYPE * pAttacker,SOLDIERTYPE *pDefender, INT16 ubAi { // Changed from DG by CJC to give higher chances of hitting with a stab or punch // sevenfm: lowered chance for boxers - if (pAttacker->flags.uiStatusFlags & SOLDIER_BOXER) + if (BOXER(pAttacker)) iChance = 50 + (iAttRating - iDefRating) / 3; else iChance = 67 + (iAttRating - iDefRating) / 3; @@ -9774,8 +9774,8 @@ UINT32 CalcChanceHTH( SOLDIERTYPE * pAttacker,SOLDIERTYPE *pDefender, INT16 ubAi // sevenfm: bonus for boxers for attacking from the back if (ubMode == HTH_MODE_PUNCH && - (pAttacker->flags.uiStatusFlags & SOLDIER_BOXER) && - (pDefender->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pAttacker) && + BOXER(pDefender) && iChance < 100 && !pAttacker->bBlindedCounter && gAnimControl[pDefender->usAnimState].ubEndHeight > ANIM_PRONE && diff --git a/TacticalAI/AIInternals.h b/TacticalAI/AIInternals.h index 1e72dedc8..56881bad7 100644 --- a/TacticalAI/AIInternals.h +++ b/TacticalAI/AIInternals.h @@ -190,7 +190,6 @@ typedef enum INT16 AdvanceToFiringRange( SOLDIERTYPE * pSoldier, INT16 sClosestOpponent ); -BOOLEAN AimingGun(SOLDIERTYPE *pSoldier); void CalcBestShot(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestShot); void CalcBestStab(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab, BOOLEAN fBladeAttack); void CalcBestThrow(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestThrow); @@ -223,7 +222,6 @@ INT8 ArmedVehicleDecideAction( SOLDIERTYPE* pSoldier ); // 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 ); UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction ); diff --git a/TacticalAI/AIList.cpp b/TacticalAI/AIList.cpp index fef94af55..8913f71f5 100644 --- a/TacticalAI/AIList.cpp +++ b/TacticalAI/AIList.cpp @@ -18,6 +18,7 @@ #include "opplist.h" #include "Interface.h" #include "Tactical Save.h" +#include #define AI_LIST_SIZE TOTAL_SOLDIERS @@ -180,7 +181,7 @@ BOOLEAN InsertIntoAIList( UINT8 ubID, INT8 bPriority ) BOOLEAN SatisfiesAIListConditions( SOLDIERTYPE * pSoldier, UINT8 * pubDoneCount, BOOLEAN fDoRandomChecks ) { - if ( (gTacticalStatus.bBoxingState == BOXING) && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( (gTacticalStatus.bBoxingState == BOXING) && !BOXER(pSoldier) ) { return( FALSE ); } diff --git a/TacticalAI/AIMain.cpp b/TacticalAI/AIMain.cpp index 00bf2902d..9cee11f86 100644 --- a/TacticalAI/AIMain.cpp +++ b/TacticalAI/AIMain.cpp @@ -206,18 +206,96 @@ STR szAction[] = { "AI_ACTION_STOP_MEDIC" }; +STR16 wszAction[] = { + L"AI_ACTION_NONE", + + L"AI_ACTION_RANDOM_PATROL", + L"AI_ACTION_SEEK_FRIEND", + L"AI_ACTION_SEEK_OPPONENT", + L"AI_ACTION_TAKE_COVER", + L"AI_ACTION_GET_CLOSER", + + L"AI_ACTION_POINT_PATROL", + L"AI_ACTION_LEAVE_WATER_GAS", + L"AI_ACTION_SEEK_NOISE", + L"AI_ACTION_ESCORTED_MOVE", + L"AI_ACTION_RUN_AWAY", + + L"AI_ACTION_KNIFE_MOVE", + L"AI_ACTION_APPROACH_MERC", + L"AI_ACTION_TRACK", + L"AI_ACTION_EAT", + L"AI_ACTION_PICKUP_ITEM", + + L"AI_ACTION_SCHEDULE_MOVE", + L"AI_ACTION_WALK", + L"AI_ACTION_RUN", + L"AI_ACTION_WITHDRAW", + L"AI_ACTION_FLANK_LEFT", + L"AI_ACTION_FLANK_RIGHT", + L"AI_ACTION_MOVE_TO_CLIMB", + + L"AI_ACTION_CHANGE_FACING", + + L"AI_ACTION_CHANGE_STANCE", + + L"AI_ACTION_YELLOW_ALERT", + L"AI_ACTION_RED_ALERT", + L"AI_ACTION_CREATURE_CALL", + L"AI_ACTION_PULL_TRIGGER", + + L"AI_ACTION_USE_DETONATOR", + L"AI_ACTION_FIRE_GUN", + L"AI_ACTION_TOSS_PROJECTILE", + L"AI_ACTION_KNIFE_STAB", + L"AI_ACTION_THROW_KNIFE", + + L"AI_ACTION_GIVE_AID", + L"AI_ACTION_WAIT", + L"AI_ACTION_PENDING_ACTION", + L"AI_ACTION_DROP_ITEM", + L"AI_ACTION_COWER", + + L"AI_ACTION_STOP_COWERING", + L"AI_ACTION_OPEN_OR_CLOSE_DOOR", + L"AI_ACTION_UNLOCK_DOOR", + L"AI_ACTION_LOCK_DOOR", + L"AI_ACTION_LOWER_GUN", + + L"AI_ACTION_ABSOLUTELY_NONE", + L"AI_ACTION_CLIMB_ROOF", + L"AI_ACTION_END_TURN", + L"AI_ACTION_END_COWER_AND_MOVE", + L"AI_ACTION_TRAVERSE_DOWN", + L"AI_ACTION_OFFER_SURRENDER", + L"AI_ACTION_RAISE_GUN", + L"AI_ACTION_STEAL_MOVE", + + L"AI_ACTION_RELOAD_GUN", + + L"AI_ACTION_JUMP_WINDOW", + L"AI_ACTION_FREE_PRISONER", + L"AI_ACTION_USE_SKILL", + L"AI_ACTION_DOCTOR", + L"AI_ACTION_DOCTOR_SELF", + L"AI_ACTION_SELFDETONATE", + L"AI_ACTION_STOP_MEDIC" +}; + // sevenfm UINT32 guiAIStartCounter = 0, guiAILastCounter = 0; //UINT8 gubAISelectedSoldier = NOBODY; BOOLEAN gfLogsEnabled = TRUE; +bool gLogDecideActionRed = false; -void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction ) +void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, bool doLog, INT8 bAction) { FILE* DebugFile; CHAR8 msg[1024]; CHAR8 buf[1024]; - if (!gfLogsEnabled || pSoldier == nullptr) + + if (!gfLogsEnabled || !doLog || pSoldier == nullptr) return; memset(buf, 0, 1024 * sizeof(char)); @@ -301,16 +379,19 @@ void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction ) } // also log to individual file for selected soldier - sprintf(buf, "Logs\\AI_Decisions [%d].txt", pSoldier->ubID); - if ((DebugFile = fopen(buf, "a+t")) != NULL) + if (pSoldier) { - if (bMsgType == AI_MSG_START) + sprintf(buf, "Logs\\AI_Decisions [%d].txt", pSoldier->ubID); + if ((DebugFile = fopen(buf, "a+t")) != NULL) { + if (bMsgType == AI_MSG_START) + { + fputs("\n", DebugFile); + } + fputs(msg, DebugFile); fputs("\n", DebugFile); + fclose(DebugFile); } - fputs(msg, DebugFile); - fputs("\n", DebugFile); - fclose(DebugFile); } } @@ -354,6 +435,27 @@ void DebugQuestInfo(STR szOutput) } } +static INT16 ShouldActionStayInProgress(SOLDIERTYPE* pSoldier) +{ + // this here should never happen, but it seems to (turns sometimes hang!) + if ((pSoldier->aiData.bAction == AI_ACTION_CHANGE_FACING) && (pSoldier->pathing.bDesiredDirection != pSoldier->aiData.usActionData)) + { + // don't try to pay any more APs for this, it was paid for once already! + pSoldier->pathing.bDesiredDirection = (INT8)pSoldier->aiData.usActionData; // turn to face direction in actionData + return(TRUE); + } + else if ((pSoldier->aiData.bAction == AI_ACTION_CHANGE_FACING) && (pSoldier->pathing.bDesiredDirection == pSoldier->aiData.usActionData)) + { + return(FALSE); + } + else if (pSoldier->aiData.bAction == AI_ACTION_END_TURN || pSoldier->aiData.bAction == AI_ACTION_NONE) + { + return(FALSE); + } + // needs more time to complete action + return(TRUE); +} + BOOLEAN InitAI( void ) { @@ -399,10 +501,6 @@ BOOLEAN InitAI( void ) return( TRUE ); } -BOOLEAN AimingGun(SOLDIERTYPE *pSoldier) -{ - return(FALSE); -} void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named inappropriately { @@ -645,6 +743,9 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named { // traversing offmap, ignore new situations } +// FIXME: Disabled temporarily to prevent AI actions constantly being cancelled during normal turn based combat. +// Need to find out when this conditional is actually needed. +#if 0 else if ( pSoldier->ubQuoteRecord == 0 && !gTacticalStatus.fAutoBandageMode ) { // don't force, don't want escorted mercs reacting to new opponents, etc. @@ -658,6 +759,7 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named } DecideAlertStatus( pSoldier ); } +#endif else { if ( pSoldier->ubQuoteRecord ) @@ -665,6 +767,15 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named // make sure we're not using combat AI pSoldier->aiData.bAlertStatus = STATUS_GREEN; } + + // Prevent AI deadlocking in case enemy is performing an action and player gets an interrupt. + // Without this, if player doesn't move any mercs, the AI soldier will wait until the deadlock is broken. + // By canceling the AI action, the AI can then reconsider actions. + //if (pSoldier->aiData.bAction == AI_ACTION_FIRE_GUN || pSoldier->aiData.bAction == AI_ACTION_KNIFE_MOVE || pSoldier->aiData.bAction == AI_ACTION_STEAL_MOVE || pSoldier->aiData.bAction == AI_ACTION_KNIFE_STAB) + { + DebugAI(AI_MSG_INFO, pSoldier, String("New Situation")); + CancelAIAction(pSoldier, FALSE); + } pSoldier->aiData.bNewSituation = WAS_NEW_SITUATION; } } @@ -674,7 +785,6 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named // might have been in 'was' state; no longer so... pSoldier->aiData.bNewSituation = NOT_NEW_SITUATION; } - #ifdef TESTAI DebugMsg( TOPIC_JA2AI, DBG_LEVEL_3,String( ".... HANDLING AI FOR %d",pSoldier->ubID)); #endif @@ -682,43 +792,81 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named /********* Start of new overall AI system ********/ - if (gfTurnBasedAI) { - time_t tCurrentTime = time(0); - UINT32 uiShortDelay = 10; - UINT32 uiDelay = (UINT32)gGameExternalOptions.gubDeadLockDelay; - UINT32 uiTime = (UINT32)(tCurrentTime - gtTimeSinceMercAIStart); - BOOLEAN fKeyPressed = _KeyDown(ESC); +#if 0 + { + // added by Flugente: static pointers, used to break out of an endless circles + static SOLDIERTYPE* pLastDecisionSoldier = NULL; + static INT16 lastdecisioncount = 0; + + // simple solution to prevent an endless clock: remember the last soldier that decided an action. If its the same one, increase the counter. + // if counter is high enough, end this guy's turn + if (pSoldier == pLastDecisionSoldier) + { + // we will only end our turn this way if this function was called over 100 times with same soldier without ending a turn. + // so many actions in a single turn smell of an endless clock. + // If we end a turn normally, the counter will be set back to 0, so this wont be a problem if you have a single soldier left for multiple turns + if (lastdecisioncount >= 600) + { + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, wszAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData); + DebugAI(AI_MSG_INFO, pSoldier, String("Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, szAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData)); + DebugAI(AI_MSG_INFO, pSoldier, String("Last action was %s ", szAction[pSoldier->aiData.bLastAction])); - if ((uiTime > uiDelay || uiTime > uiShortDelay && fKeyPressed) && !gfUIInDeadlock) - //if ( ( GetJA2Clock() - gTacticalStatus.uiTimeSinceMercAIStart ) > ( (UINT32)gGameExternalOptions.gubDeadLockDelay * 1000 ) && !gfUIInDeadlock ) + EndAIDeadlock(); + //EndAIGuysTurn(pSoldier); + lastdecisioncount = 0; + return; + } + else + ++lastdecisioncount; + } + else + { + pLastDecisionSoldier = pSoldier; + lastdecisioncount = 0; + } + } +#else { - // ATE: Display message that deadlock occured... - LiveMessage( "Breaking Deadlock" ); + time_t tCurrentTime = time(0); + UINT32 uiShortDelay = 10; + UINT32 uiDelay = (UINT32)gGameExternalOptions.gubDeadLockDelay; + UINT32 uiTime = (UINT32)(tCurrentTime - gtTimeSinceMercAIStart); + BOOLEAN fKeyPressed = _KeyDown(ESC); + + if ((uiTime > uiDelay || uiTime > uiShortDelay && fKeyPressed) && !gfUIInDeadlock) + //if ( ( GetJA2Clock() - gTacticalStatus.uiTimeSinceMercAIStart ) > ( (UINT32)gGameExternalOptions.gubDeadLockDelay * 1000 ) && !gfUIInDeadlock ) + { + // ATE: Display message that deadlock occured... + LiveMessage( "Breaking Deadlock" ); + + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, wszAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData); + DebugAI(AI_MSG_INFO, pSoldier, String("Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, szAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData)); + DebugAI(AI_MSG_INFO, pSoldier, String("Last action was %s ", szAction[pSoldier->aiData.bLastAction])); - ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Aborting AI deadlock for [%d] %s %s data %d", pSoldier->ubID, pSoldier->GetName(), utf8_to_wstring(std::string(szAction[pSoldier->aiData.bAction])), pSoldier->aiData.usActionData); - DebugAI(String("Aborting AI deadlock for [%d] %s data %d", pSoldier->ubID, szAction[pSoldier->aiData.bAction], pSoldier->aiData.usActionData)); #ifdef JA2TESTVERSION - // display deadlock message - gfUIInDeadlock = TRUE; - gUIDeadlockedSoldier = pSoldier->ubID; - DebugAI( String("DEADLOCK soldier %d action %s ABC %d", pSoldier->ubID, gzActionStr[pSoldier->aiData.bAction], gTacticalStatus.ubAttackBusyCount ) ); + // display deadlock message + gfUIInDeadlock = TRUE; + gUIDeadlockedSoldier = pSoldier->ubID; + DebugAI( String("DEADLOCK soldier %d action %s ABC %d", pSoldier->ubID, gzActionStr[pSoldier->aiData.bAction], gTacticalStatus.ubAttackBusyCount ) ); #else - // If we are in beta version, also report message! + // If we are in beta version, also report message! #ifdef JA2BETAVERSION - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_ERROR, L"Aborting AI deadlock for %d. Please sent DEBUG.TXT file and SAVE.", pSoldier->ubID ); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_ERROR, L"Aborting AI deadlock for %d. Please sent DEBUG.TXT file and SAVE.", pSoldier->ubID ); #endif - // just abort - EndAIDeadlock(); - if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL) ) - { - return; - } + // just abort + EndAIDeadlock(); + if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_UNDERAICONTROL) ) + { + return; + } #endif + } } +#endif } // We STILL do not want to issue new orders while an attack busy situation is going on. This can happen, for example, @@ -728,7 +876,7 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named return; } - if (pSoldier->aiData.bAction == AI_ACTION_NONE) + if (!pSoldier->aiData.bActionInProgress) { // being handled so turn off muzzle flash if ( pSoldier->flags.fMuzzleFlash ) @@ -864,6 +1012,12 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named { ActionDone(pSoldier); } + + if (!ShouldActionStayInProgress(pSoldier)) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Action %s was stuck as being in progress. Canceling action", szAction[pSoldier->aiData.bAction])); + ActionDone(pSoldier); + } } /********* End of new overall AI system @@ -1332,7 +1486,7 @@ void FreeUpNPCFromRoofClimb(SOLDIERTYPE *pSoldier ) void ActionDone(SOLDIERTYPE *pSoldier) { // if an action is currently selected - if (pSoldier->aiData.bAction != AI_ACTION_NONE) + //if (pSoldier->aiData.bAction != AI_ACTION_NONE) { if (pSoldier->flags.uiStatusFlags & SOLDIER_MONSTER) { @@ -1552,33 +1706,6 @@ INT16 ActionInProgress(SOLDIERTYPE *pSoldier) void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) { - // added by Flugente: static pointers, used to break out of an endless circles (currently only used for zombie AI) - static SOLDIERTYPE* pLastDecisionSoldier = NULL; - static INT16 lastdecisioncount = 0; - - // simple solution to prevent an endless clock: remember the last soldier that decided an action. If its the same one, increase the counter. - // if counter is high enough, end this guy's turn - if ( pSoldier == pLastDecisionSoldier ) - { - // we will only end our turn this way if this function was called over 100 times with same soldier without ending a turn. - // so many actions in a single turn smell of an endless clock. - // If we end a turn normally, the counter will be set back to 0, so this wont be a problem if you have a single soldier left for multiple turns - if ( lastdecisioncount >= 100 ) - { - // zombie is done doing harm... - EndAIGuysTurn( pSoldier); - lastdecisioncount = 0; - return ; - } - else - ++lastdecisioncount; - } - else - { - pLastDecisionSoldier = pSoldier; - lastdecisioncount = 0; - } - // yikes, this shouldn't occur! we should be trying to finish our move! // pSoldier->flags.fNoAPToFinishMove = FALSE; @@ -1611,6 +1738,8 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) #ifdef DEBUGBUSY AINumMessage("Busy with action, skipping guy#",pSoldier->ubID); #endif + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Busy with action %s, skipping guy [%d]", wszAction[pSoldier->aiData.bAction], pSoldier->ubID); + DebugAI(AI_MSG_INFO, pSoldier, String("Busy with action %s, skipping guy [%d]", wszAction[pSoldier->aiData.bAction], pSoldier->ubID)); // let it continue return; @@ -1736,42 +1865,41 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) { pSoldier->aiData.bAction = AI_ACTION_NONE; } + } - // if he chose to continue doing nothing - if (pSoldier->aiData.bAction == AI_ACTION_NONE) - { + // if he chose to continue doing nothing + if (pSoldier->aiData.bAction == AI_ACTION_NONE) + { #ifdef RECORDNET - fprintf(NetDebugFile,"\tMOVED BECOMING TRUE: Chose to do nothing, guynum %d\n",pSoldier->ubID); + fprintf(NetDebugFile,"\tMOVED BECOMING TRUE: Chose to do nothing, guynum %d\n",pSoldier->ubID); #endif - DebugMsg (TOPIC_JA2AI,DBG_LEVEL_3,"NPC has no action assigned"); - NPCDoesNothing(pSoldier); // sets pSoldier->moved to TRUE - return; - } - // to get here, we MUST have an action selected, but not in progress... - // see if we can afford to do this action - if (IsActionAffordable(pSoldier)) - { - NPCDoesAct(pSoldier); + DebugMsg (TOPIC_JA2AI,DBG_LEVEL_3,"NPC has no action assigned"); + NPCDoesNothing(pSoldier); // sets pSoldier->moved to TRUE + return; + } + // to get here, we MUST have an action selected, but not in progress... + // see if we can afford to do this action + if (IsActionAffordable(pSoldier)) + { + NPCDoesAct(pSoldier); - // perform the chosen action - pSoldier->aiData.bActionInProgress = ExecuteAction(pSoldier); // if started, mark us as busy + // perform the chosen action + pSoldier->aiData.bActionInProgress = ExecuteAction(pSoldier); // if started, mark us as busy - if ( !pSoldier->aiData.bActionInProgress && !TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination)) - { - // turn based... abort this guy's turn - EndAIGuysTurn( pSoldier ); - lastdecisioncount = 0; - } - } - else + if ( !pSoldier->aiData.bActionInProgress && !TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination)) { + // turn based... abort this guy's turn + EndAIGuysTurn( pSoldier ); + } + } + else + { #ifdef DEBUGDECISIONS - AINumMessage("HandleManAI - Not enough APs, skipping guy#",pSoldier->ubID); + AINumMessage("HandleManAI - Not enough APs, skipping guy#",pSoldier->ubID); #endif - HaltMoveForSoldierOutOfPoints( pSoldier); - return; - } + HaltMoveForSoldierOutOfPoints( pSoldier); + return; } } @@ -1804,6 +1932,7 @@ void RefreshAI(SOLDIERTYPE *pSoldier) if ((pSoldier->aiData.bAlertStatus == STATUS_BLACK) || (pSoldier->aiData.bAlertStatus == STATUS_RED)) { // always freshly rethink things at start of his turn + //CancelAIAction(pSoldier, FALSE); pSoldier->aiData.bNewSituation = IS_NEW_SITUATION; } else @@ -2285,14 +2414,24 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier) usHandItem = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS]); iRetCode = HandleItem( pSoldier, pSoldier->aiData.usActionData, pSoldier->bTargetLevel, usHandItem, FALSE ); + // If AI cannot shoot because of lack of APs, attempt to try again with lower aim. + // Usually happens when they have to turn before shooting. Without this, the game would cancel soldier's whole turn + if (iRetCode == ITEM_HANDLE_NOAPS && pSoldier->aiData.bAimTime > 0) + { + do + { + pSoldier->aiData.bAimTime -= 1; + iRetCode = HandleItem(pSoldier, pSoldier->aiData.usActionData, pSoldier->bTargetLevel, usHandItem, FALSE); + } while (iRetCode == ITEM_HANDLE_NOAPS && pSoldier->aiData.bAimTime > 0); + } + if ( iRetCode != ITEM_HANDLE_OK) { if ( iRetCode != ITEM_HANDLE_BROKEN ) // if the item broke, this is 'legal' and doesn't need reporting { - DebugAI( String( "AI %d got error code %ld from HandleItem, doing action %d, has %d APs... aborting deadlock!", pSoldier->ubID, iRetCode, pSoldier->aiData.bAction, pSoldier->bActionPoints ) ); + DebugAI(AI_MSG_INFO, pSoldier, String( "AI %d got error code %ld from HandleItem, doing action %d, has %d APs... aborting deadlock!", pSoldier->ubID, iRetCode, pSoldier->aiData.bAction, pSoldier->bActionPoints ) ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"AI %d got error code %ld from HandleItem, doing action %d... aborting deadlock!", pSoldier->ubID, iRetCode, pSoldier->aiData.bAction ); } - DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction: !ITEM_HANDLE_OK")); CancelAIAction( pSoldier, FORCE); #ifdef TESTAICONTROL if (gfTurnBasedAI) diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 8b42fb29b..e76d86df8 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -418,7 +418,7 @@ UINT16 DetermineMovementMode( SOLDIERTYPE * pSoldier, INT8 bAction ) if (IS_MERC_BODY_TYPE(pSoldier) && pSoldier->aiData.bAlertStatus >= STATUS_YELLOW && !InWaterGasOrSmoke(pSoldier, pSoldier->sGridNo) && - !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + !BOXER(pSoldier) && !TileIsOutOfBounds(sClosestThreat) && (pSoldier->bTeam == ENEMY_TEAM || pSoldier->bTeam == MILITIA_TEAM)) { @@ -1474,7 +1474,7 @@ INT32 ClosestKnownOpponent(SOLDIERTYPE *pSoldier, INT32 * psGridNo, INT8 * pbLev if (sClosestOpponent == NOWHERE || iRange < iClosestRange || - pClosestOpponent && !pClosestOpponent->IsZombie() && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && pClosestOpponent->stats.bLife < OKLIFE && pOpponent->stats.bLife >= OKLIFE) + pClosestOpponent && !pClosestOpponent->IsZombie() && !BOXER(pSoldier) && pClosestOpponent->stats.bLife < OKLIFE && pOpponent->stats.bLife >= OKLIFE) { iClosestRange = iRange; sClosestOpponent = sGridNo; @@ -2253,6 +2253,12 @@ BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo ) BOOLEAN InGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo ) { + // Armed vehicles and robots do not care about gas or smoke + if (ARMED_VEHICLE(pSoldier) || ENEMYROBOT(pSoldier)) + { + return FALSE; + } + // smoke if ( gpWorldLevelData[sGridNo].ubExtFlags[pSoldier->pathing.bLevel] & (MAPELEMENT_EXT_SMOKE | MAPELEMENT_EXT_SIGNAL_SMOKE | MAPELEMENT_EXT_DEBRIS_SMOKE | MAPELEMENT_EXT_FIRERETARDANT_SMOKE ) ) return TRUE; @@ -2300,6 +2306,12 @@ BOOLEAN InGas(SOLDIERTYPE *pSoldier, INT32 sGridNo) if (TileIsOutOfBounds(sGridNo)) return FALSE; + // Armed vehicles and robots do not care about gas or smoke + if (ARMED_VEHICLE(pSoldier) || ENEMYROBOT(pSoldier)) + { + return FALSE; + } + if (InGasSpot(pSoldier, sGridNo, pSoldier->pathing.bLevel)) { return TRUE; @@ -2658,7 +2670,7 @@ INT32 CalcManThreatValue( SOLDIERTYPE *pEnemy, INT32 sMyGrid, UINT8 ubReduceForC } // in boxing mode, let only a boxer be considered a threat. - if ( (gTacticalStatus.bBoxingState == BOXING) && !(pEnemy->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( (gTacticalStatus.bBoxingState == BOXING) && !BOXER(pEnemy) ) { iThreatValue = -999; return( iThreatValue ); @@ -4791,7 +4803,7 @@ BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* 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) || + gTacticalStatus.bBoxingState == BOXING && BOXER(pSoldier) && !BOXER(pOpponent) || pOpponent->ubBodyType == CROW) { return FALSE; @@ -4901,7 +4913,7 @@ BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier) if (!IS_MERC_BODY_TYPE(pSoldier) || pSoldier->aiData.bNeutral || fCivilian || - pSoldier->flags.uiStatusFlags & SOLDIER_BOXER || + BOXER(pSoldier) || ARMED_VEHICLE(pSoldier) || pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE || AM_A_ROBOT(pSoldier) || diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index e4a1e0ad7..d2d1e840d 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -40,12 +40,15 @@ // On the bottom here, there are these functions made ////////////////////////////////////////////////////////////////////// +extern bool gLogDecideActionRed; extern BOOLEAN gfHiddenInterrupt; extern BOOLEAN gfUseAlternateQueenPosition; extern UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady, BOOLEAN fHipStance ); extern void IncrementWatchedLoc(UINT8 ubID, INT32 sGridNo, INT8 bLevel); -void LogDecideInfo(SOLDIERTYPE *pSoldier); -void LogKnowledgeInfo(SOLDIERTYPE *pSoldier); +void LogDecideInfo(SOLDIERTYPE *pSoldier, bool doLog = true); +void LogKnowledgeInfo(SOLDIERTYPE *pSoldier, bool doLog); +INT8 DecideActionWearGasmask(SOLDIERTYPE* pSoldier); +ActionType DecideActionStuckInWaterOrGas(SOLDIERTYPE* pSoldier, BOOLEAN ubCanMove, BOOLEAN bInWater, BOOLEAN bInDeepWater, BOOLEAN bInGas); // global status time counters to determine what takes the most time @@ -70,14 +73,14 @@ STR8 gStr8Team[] = { "OUR_TEAM", "ENEMY_TEAM", "CREATURE_TEAM", "MILITIA_TEAM", STR8 gStr8Class[] = { "SOLDIER_CLASS_NONE", "SOLDIER_CLASS_ADMINISTRATOR", "SOLDIER_CLASS_ELITE", "SOLDIER_CLASS_ARMY", "SOLDIER_CLASS_GREEN_MILITIA", "SOLDIER_CLASS_REG_MILITIA", "SOLDIER_CLASS_ELITE_MILITIA", "SOLDIER_CLASS_CREATURE", "SOLDIER_CLASS_MINER", "SOLDIER_CLASS_ZOMBIE", "SOLDIER_CLASS_TANK", "SOLDIER_CLASS_JEEP", "SOLDIER_CLASS_BANDIT", "SOLDIER_CLASS_ROBOT" }; STR8 gStr8Knowledge[] = { "HEARD_3_TURNS_AGO", "HEARD_2_TURNS_AGO", "HEARD_LAST_TURN", "HEARD_THIS_TURN", "NOT_HEARD_OR_SEEN", "SEEN_CURRENTLY", "SEEN_THIS_TURN", "SEEN_LAST_TURN", "SEEN_2_TURNS_AGO", "SEEN_3_TURNS_AGO" }; -void DoneScheduleAction( SOLDIERTYPE * pSoldier ) +static void DoneScheduleAction( SOLDIERTYPE * pSoldier ) { pSoldier->aiData.fAIFlags &= (~AI_CHECK_SCHEDULE); pSoldier->bAIScheduleProgress = 0; PostNextSchedule( pSoldier ); } -INT8 DecideActionSchedule( SOLDIERTYPE * pSoldier ) +static INT8 DecideActionSchedule( SOLDIERTYPE * pSoldier ) { SCHEDULENODE * pSchedule; INT32 iScheduleIndex; @@ -715,7 +718,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) if ( gTacticalStatus.bBoxingState != NOT_BOXING ) { - if (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) + if (BOXER(pSoldier)) { if ( gTacticalStatus.bBoxingState == PRE_BOXING ) { @@ -806,12 +809,6 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) // check if standing in tear gas without a gas mask on, or in smoke bInGas = InGasOrSmoke( pSoldier, pSoldier->sGridNo ); - // Flugente: tanks do not care about gas - if ( ARMED_VEHICLE( pSoldier ) || ENEMYROBOT( pSoldier ) ) - { - bInGas = FALSE; - } - // if real-time, and not in the way, do nothing 90% of the time (for GUARDS!) // unless in water (could've started there), then we better swim to shore! @@ -2450,11 +2447,9 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) INT8 bActionReturned; INT32 iDummy; INT32 iChance; - INT32 sClosestOpponent = NOWHERE, sClosestFriend = NOWHERE; INT32 sClosestDisturbance = NOWHERE, sCheckGridNo; INT32 sDistVisible; UINT8 ubCanMove,ubOpponentDir; - INT8 bInWater, bInDeepWater, bInGas; INT8 bSeekPts = 0, bHelpPts = 0, bHidePts = 0, bWatchPts = 0; INT8 bHighestWatchLoc; ATTACKTYPE BestThrow, BestShot; @@ -2484,8 +2479,8 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("DecideActionRed: soldier orders = %d",pSoldier->aiData.bOrders)); - DebugAI(AI_MSG_START, pSoldier, String("[Red]")); - LogDecideInfo(pSoldier); + DebugAI(AI_MSG_START, pSoldier, String("[Red]"), gLogDecideActionRed); + LogDecideInfo(pSoldier, gLogDecideActionRed); // sevenfm: disable stealth mode pSoldier->bStealthMode = FALSE; @@ -2498,23 +2493,24 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( pSoldier->bActionPoints <= 0 ) //Action points can be negative { pSoldier->aiData.usActionData = NOWHERE; + pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; return(AI_ACTION_NONE); } // sevenfm: find closest opponent - sClosestOpponent = ClosestKnownOpponent(pSoldier, &sOpponentGridNo, &bOpponentLevel); - DebugAI(AI_MSG_INFO, pSoldier, String("sClosestOpponent %d", sClosestOpponent)); + INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, &sOpponentGridNo, &bOpponentLevel); + DebugAI(AI_MSG_INFO, pSoldier, String("sClosestOpponent %d", sClosestOpponent), gLogDecideActionRed); if (!SightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE)) { fCanBeSeen = TRUE; - DebugAI(AI_MSG_INFO, pSoldier, String("can be seen")); + DebugAI(AI_MSG_INFO, pSoldier, String("can be seen"), gLogDecideActionRed); } fProneSightCover = ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE); - DebugAI(AI_MSG_INFO, pSoldier, String("prone sight cover %d", fProneSightCover)); + DebugAI(AI_MSG_INFO, pSoldier, String("prone sight cover %d", fProneSightCover), gLogDecideActionRed); fAnyCover = AnyCoverAtSpot(pSoldier, pSoldier->sGridNo); - DebugAI(AI_MSG_INFO, pSoldier, String("any cover %d", fAnyCover)); + DebugAI(AI_MSG_INFO, pSoldier, String("any cover %d", fAnyCover), gLogDecideActionRed); if (!fProneSightCover || pSoldier->aiData.bUnderFire) { @@ -2533,6 +2529,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) !pSoldier->bBreathCollapsed && pSoldier->IsCowering()) { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop cowering"), gLogDecideActionRed); return AI_ACTION_STOP_COWERING; } @@ -2544,6 +2541,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) !pSoldier->bBreathCollapsed && pSoldier->IsGivingAid()) { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop giving aid"), gLogDecideActionRed); return AI_ACTION_STOP_MEDIC; } @@ -2581,41 +2579,19 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // determine if we happen to be in water (in which case we're in BIG trouble!) - bInWater = Water( pSoldier->sGridNo, pSoldier->pathing.bLevel ); - bInDeepWater = DeepWater( pSoldier->sGridNo, pSoldier->pathing.bLevel ); - - // check if standing in tear gas without a gas mask on - bInGas = InGasOrSmoke( pSoldier, pSoldier->sGridNo ); - - // Flugente: tanks do not care about gas - if ( ARMED_VEHICLE( pSoldier ) || ENEMYROBOT( pSoldier ) ) - { - bInGas = FALSE; - } + INT8 bInWater = Water( pSoldier->sGridNo, pSoldier->pathing.bLevel ); + INT8 bInDeepWater = DeepWater( pSoldier->sGridNo, pSoldier->pathing.bLevel ); //////////////////////////////////////////////////////////////////////////// // WHEN LEFT IN GAS, WEAR GAS MASK IF AVAILABLE AND NOT WORN //////////////////////////////////////////////////////////////////////////// - - if ( !bInGas && (gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y) ) - { - // only chance if we happen to be caught with our gas mask off - if ( PreRandom( 10 ) == 0 && WearGasMaskIfAvailable( pSoldier ) ) - { - // reevaluate - bInGas = InGasOrSmoke( pSoldier, pSoldier->sGridNo ); - } - } - - //Only put mask on in gas - if(bInGas && WearGasMaskIfAvailable(pSoldier))//dnl ch40 200909 - bInGas = InGasOrSmoke(pSoldier, pSoldier->sGridNo); + INT8 bInGas = DecideActionWearGasmask(pSoldier); //////////////////////////////////////////////////////////////////////////// // WHEN IN GAS, GO TO NEAREST REACHABLE SPOT OF UNGASSED LAND //////////////////////////////////////////////////////////////////////////// - // when in deep water, move to closest opponent + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Decide action if stuck in water or gas]"), gLogDecideActionRed); if (ubCanMove && bInDeepWater && !pSoldier->aiData.bNeutral && pSoldier->aiData.bOrders == SEEKENEMY) { // find closest reachable opponent, excluding opponents in deep water @@ -2623,6 +2599,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Move out of water towards closest opponent"), gLogDecideActionRed); return(AI_ACTION_LEAVE_WATER_GAS); } } @@ -2638,13 +2615,19 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) AIPopMessage(tempstr); #endif + DebugAI(AI_MSG_INFO, pSoldier, String("Leave for nearest (ungassed) land"), gLogDecideActionRed); return(AI_ACTION_LEAVE_WATER_GAS); } } + + //////////////////////////////////////////////////////////////////////////// + // REGULAR CIVILIANS COWER / RUN AWAY + //////////////////////////////////////////////////////////////////////////// //if (fCivilian && !(pSoldier->ubBodyType == COW || pSoldier->ubBodyType == CRIPPLECIV || pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) && gTacticalStatus.bBoxingState == NOT_BOXING) if (fCivilian && !(pSoldier->ubBodyType == COW || pSoldier->ubBodyType == CRIPPLECIV || pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE)) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Civilian decisions]"), gLogDecideActionRed); if (FindAIUsableObjClass(pSoldier, IC_WEAPON) == NO_SLOT) { // cower in fear!! @@ -2656,6 +2639,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( pSoldier->aiData.bLastAction == AI_ACTION_COWER ) { // do nothing + DebugAI(AI_MSG_INFO, pSoldier, String("Already cowering, do nothing"), gLogDecideActionRed); pSoldier->aiData.usActionData = NOWHERE; return( AI_ACTION_NONE ); } @@ -2665,12 +2649,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.usNextActionData = FindSpotMaxDistFromOpponents( pSoldier ); if (!TileIsOutOfBounds(pSoldier->aiData.usNextActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop cowering. Prepare for running away"), gLogDecideActionRed); pSoldier->aiData.bNextAction = AI_ACTION_RUN_AWAY; pSoldier->aiData.usActionData = ANIM_STAND; return( AI_ACTION_STOP_COWERING ); } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Do nothing"), gLogDecideActionRed); return( AI_ACTION_NONE ); } } @@ -2695,6 +2681,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( gfTurnBasedAI || gTacticalStatus.fEnemyInSector ) { // battle - cower!!! + DebugAI(AI_MSG_INFO, pSoldier, String("Start cowering"), gLogDecideActionRed); pSoldier->aiData.usActionData = ANIM_CROUCH; return( AI_ACTION_COWER ); } @@ -2720,20 +2707,20 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) !bInGas && pSoldier->CheckInitialAP() && !pSoldier->IsFlanking() && - !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + !BOXER(pSoldier) && (CanNPCAttack(pSoldier) == TRUE)) { BestThrow.ubPossible = FALSE; // by default, assume Throwing isn't possible - DebugAI(AI_MSG_TOPIC, pSoldier, String("[CheckIfTossPossible]")); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[CheckIfTossPossible]"), gLogDecideActionRed); CheckIfTossPossible(pSoldier,&BestThrow); - if (BestThrow.ubPossible) - DebugAI(AI_MSG_INFO, pSoldier, String("throw possible")); - else - DebugAI(AI_MSG_INFO, pSoldier, String("throw not possible")); + //////////////////////////////////////////////////////////////////////// + // CHECK IF THROWING A GRENADE OR USING A LAUNCHER/MORTAR AGAINST ENEMY IS POSSIBLE + //////////////////////////////////////////////////////////////////////// if (BestThrow.ubPossible) { + DebugAI(AI_MSG_INFO, pSoldier, String("throw possible"), gLogDecideActionRed); // sevenfm: allow using mortars, grenade launchers, flares and grenades in RED state if (Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].mortar || //Item[pSoldier->inv[ BestThrow.bWeaponIn ].usItem].cannon || @@ -2745,7 +2732,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if firing mortar make sure we have room if (Item[pSoldier->inv[BestThrow.bWeaponIn].usItem].mortar) { - DebugAI(AI_MSG_INFO, pSoldier, String("using mortar, check room to deploy")); + DebugAI(AI_MSG_INFO, pSoldier, String("using mortar, check room to deploy"), gLogDecideActionRed); ubOpponentDir = AIDirection(pSoldier->sGridNo, BestThrow.sTarget); // Get new gridno! @@ -2753,7 +2740,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!OKFallDirection(pSoldier, sCheckGridNo, pSoldier->pathing.bLevel, ubOpponentDir, pSoldier->usAnimState)) { - DebugAI(AI_MSG_INFO, pSoldier, String("no room to deploy mortar, check if we can move behind")); + DebugAI(AI_MSG_INFO, pSoldier, String("no room to deploy mortar, check if we can move behind"), gLogDecideActionRed); // can't fire! BestThrow.ubPossible = FALSE; @@ -2766,15 +2753,15 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) INT32 iPathCost = EstimatePlotPath(pSoldier, sCheckGridNo, FALSE, FALSE, FALSE, DetermineMovementMode(pSoldier, AI_ACTION_GET_CLOSER), pSoldier->bStealthMode, FALSE, 0); if (iPathCost != 0 && iPathCost + BestThrow.ubAPCost + GetAPsToLook(pSoldier) + GetAPsCrouch(pSoldier, FALSE) <= pSoldier->bActionPoints) { - DebugAI(AI_MSG_INFO, pSoldier, String("moving backwards to have more room to deploy mortar")); + DebugAI(AI_MSG_INFO, pSoldier, String("moving backwards to have more room to deploy mortar"), gLogDecideActionRed); pSoldier->aiData.usActionData = sCheckGridNo; - DebugAI(AI_MSG_INFO, pSoldier, String("prepare next action throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime)); + DebugAI(AI_MSG_INFO, pSoldier, String("prepare next action throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime), gLogDecideActionRed); // if necessary, swap the usItem if (BestThrow.bWeaponIn != HANDPOS) { - DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket")); + DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"), gLogDecideActionRed); RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER); } @@ -2796,19 +2783,19 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if still possible if (BestThrow.ubPossible) { - DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime)); + DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime), gLogDecideActionRed); // if necessary, swap the usItem if (BestThrow.bWeaponIn != HANDPOS) { - DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket")); + DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"), gLogDecideActionRed); RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER); } // sevenfm: correctly set weapon mode for attached GL if (IsGrenadeLauncherAttached(&pSoldier->inv[HANDPOS])) { - DebugAI(AI_MSG_INFO, pSoldier, String("set attached GL mode")); + DebugAI(AI_MSG_INFO, pSoldier, String("set attached GL mode"), gLogDecideActionRed); pSoldier->bWeaponMode = WM_ATTACHED_GL; } @@ -2816,6 +2803,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance && pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before throw"), gLogDecideActionRed); pSoldier->aiData.usActionData = BestThrow.ubStance; pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE; pSoldier->aiData.usNextActionData = BestThrow.sTarget; @@ -2830,12 +2818,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.bAimTime = BestThrow.ubAimTime; } + DebugAI(AI_MSG_INFO, pSoldier, String("Throw grenade / use launcher!"), gLogDecideActionRed); return(AI_ACTION_TOSS_PROJECTILE); } } } else // toss/throw/launch not possible { + DebugAI(AI_MSG_INFO, pSoldier, String("throw not possible"), gLogDecideActionRed); // WDS - Fix problem when there is no "best thrown" weapon (i.e., BestThrow.bWeaponIn == NO_SLOT) // if this dude has a longe-range weapon on him (longer than normal // sight range), and there's at least one other team-mate around, and @@ -2846,7 +2836,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) (gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) && (gTacticalStatus.ubSpottersCalledForBy == NOBODY)) { - DebugAI(AI_MSG_INFO, pSoldier, String("throw not possible, call for spotters!")); + DebugAI(AI_MSG_INFO, pSoldier, String("throw not possible, call for spotters!"), gLogDecideActionRed); // then call for spotters! Uses up the rest of his turn (whatever // that may be), but from now on, BLACK AI NPC may radio sightings! @@ -2857,8 +2847,10 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } } - // use smoke to cover friend - DebugAI(AI_MSG_TOPIC, pSoldier, String("[use smoke to cover friend]")); + + //////////////////////////////////////////////////////////////////////// + // THROW SMOKE TO PROVIDE COVER FOR FRIEND + //////////////////////////////////////////////////////////////////////// if (gfTurnBasedAI && SoldierAI(pSoldier) && !bInWater && @@ -2873,25 +2865,26 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) Chance(100 - min(100, 10 * CountPublicKnownEnemies(pSoldier, pSoldier->sGridNo, TACTICAL_RANGE))) && !GuySawEnemy(pSoldier, SEEN_LAST_TURN)) { - DebugAI(AI_MSG_INFO, pSoldier, String("check if we can cover friend with smoke")); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[use smoke to cover friend]"), gLogDecideActionRed); CheckTossFriendSmoke(pSoldier, &BestThrow); if (BestThrow.ubPossible) { - DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime)); + DebugAI(AI_MSG_INFO, pSoldier, String("Throw possible"), gLogDecideActionRed); + DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime), gLogDecideActionRed); // start retreating for several turns if (BestThrow.ubOpponent != NOBODY && !MercPtrs[BestThrow.ubOpponent]->IsFlanking()) { - DebugAI(AI_MSG_INFO, pSoldier, String("start retreat counter for %d", BestThrow.ubOpponent)); + DebugAI(AI_MSG_INFO, pSoldier, String("start retreat counter for %d", BestThrow.ubOpponent), gLogDecideActionRed); MercPtrs[BestThrow.ubOpponent]->RetreatCounterStart(2); } // if necessary, swap the usItem from holster into the hand position if (BestThrow.bWeaponIn != HANDPOS) { - DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket")); + DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"), gLogDecideActionRed); RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER); } @@ -2899,6 +2892,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance && pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before throw"), gLogDecideActionRed); pSoldier->aiData.usActionData = BestThrow.ubStance; pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE; pSoldier->aiData.usNextActionData = BestThrow.sTarget; @@ -2913,12 +2907,16 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.bAimTime = BestThrow.ubAimTime; } - DebugAI(AI_MSG_INFO, pSoldier, String("throw smoke grenade to cover friend %d at spot %d level %d", BestThrow.ubOpponent, BestThrow.sTarget, BestThrow.bTargetLevel)); + DebugAI(AI_MSG_INFO, pSoldier, String("throw smoke grenade to cover friend %d at spot %d level %d", BestThrow.ubOpponent, BestThrow.sTarget, BestThrow.bTargetLevel), gLogDecideActionRed); return(AI_ACTION_TOSS_PROJECTILE); } } + + //////////////////////////////////////////////////////////////////////// + // SNIPER / SUPPRESSION + //////////////////////////////////////////////////////////////////////// // sevenfm: moved can attack check here as only sniper/suppression code needs usable gun if(CanNPCAttack(pSoldier) == TRUE) { @@ -2927,9 +2925,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->bAimShotLocation = AIM_SHOT_RANDOM; CheckIfShotPossible(pSoldier, &BestShot); DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("decideactionred: is sniper shot possible? = %d, CTH = %d", BestShot.ubPossible, BestShot.ubChanceToReallyHit)); + DebugAI(AI_MSG_INFO, pSoldier, String("Is sniper shot possible? = %d, CTH = %d", BestShot.ubPossible, BestShot.ubChanceToReallyHit), gLogDecideActionRed); if (BestShot.ubPossible && BestShot.ubChanceToReallyHit > 50) { + DebugAI(AI_MSG_INFO, pSoldier, String("Sniper shot possible!"), gLogDecideActionRed); // then do it! The functions have already made sure that we have a // pair of worthy opponents, etc., so we're not just wasting our time @@ -2949,6 +2949,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } else // snipe not possible { + DebugAI(AI_MSG_INFO, pSoldier, String("Sniper shot NOT possible!"), gLogDecideActionRed); // if this dude has a long-range weapon on him (longer than normal // sight range), and there's at least one other team-mate around, and // spotters haven't already been called for, then DO SO! @@ -2973,6 +2974,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->bActionPoints = 0; DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "decideactionred: calling for sniper spotters"); + DebugAI(AI_MSG_INFO, pSoldier, String("Call for spotters"), gLogDecideActionRed); pSoldier->aiData.usActionData = NOWHERE; return(AI_ACTION_NONE); @@ -2982,6 +2984,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //SUPPRESSION FIRE //CheckIfShotPossible(pSoldier, &BestShot); //WarmSteel - No longer returns 0 when there IS actually a chance to hit. + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Suppression decisions]"), gLogDecideActionRed); //RELOADING // WarmSteel - Because of suppression fire, we need enough ammo to even consider suppressing @@ -3003,6 +3006,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) OBJECTTYPE * pAmmo = &(pSoldier->inv[bAmmoSlot]); if ((*pAmmo)[0]->data.ubShotsLeft > pSoldier->inv[BestShot.bWeaponIn][0]->data.gun.ubGunShotsLeft && GetAPsToReloadGunWithAmmo(pSoldier, &(pSoldier->inv[BestShot.bWeaponIn]), pAmmo) <= (INT16)pSoldier->bActionPoints) { + DebugAI(AI_MSG_INFO, pSoldier, String("Reload weapon"), gLogDecideActionRed); pSoldier->aiData.usActionData = BestShot.bWeaponIn; return AI_ACTION_RELOAD_GUN; } @@ -3016,6 +3020,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) INT8 bAmmoSlot = FindAmmoToReload(pSoldier, BestShot.bWeaponIn, NO_SLOT); if (bAmmoSlot != NO_SLOT) { + DebugAI(AI_MSG_INFO, pSoldier, String("Found spare ammo"), gLogDecideActionRed); fExtraClip = TRUE; } } @@ -3062,11 +3067,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // then do it! // if necessary, swap the usItem from holster into the hand position - DebugAI(AI_MSG_INFO, pSoldier, String("suppression fire possible! target %d level %d aim %d", BestShot.sTarget, BestShot.bTargetLevel, BestShot.ubAimTime)); + DebugAI(AI_MSG_INFO, pSoldier, String("suppression fire possible! target %d level %d aim %d", BestShot.sTarget, BestShot.bTargetLevel, BestShot.ubAimTime), gLogDecideActionRed); if (BestShot.bWeaponIn != HANDPOS) { - DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket")); + DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"), gLogDecideActionRed); RearrangePocket(pSoldier, HANDPOS, BestShot.bWeaponIn, FOREVER); } @@ -3087,7 +3092,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) !UsingNewCTHSystem() && Chance((100 - BestShot.ubChanceToReallyHit) * (100 - BestShot.ubChanceToReallyHit) / 100)) { - DebugAI(AI_MSG_INFO, pSoldier, String("set ubAimTime = 0 for OCTH suppression")); + DebugAI(AI_MSG_INFO, pSoldier, String("set ubAimTime = 0 for OCTH suppression"), gLogDecideActionRed); BestShot.ubAimTime = 0; } @@ -3132,7 +3137,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // Make sure we decided to fire at least one shot! ubBurstAPs = CalcAPsToAutofire(pSoldier->CalcActionPoints(), &(pSoldier->inv[BestShot.bWeaponIn]), pSoldier->bDoAutofire, pSoldier); - DebugAI(AI_MSG_INFO, pSoldier, String("autofire shots %d APcost %d burst AP %d aimtime %d reserve AP %d", pSoldier->bDoAutofire, BestShot.ubAPCost, ubBurstAPs, sActualAimAP, sReserveAP)); + DebugAI(AI_MSG_INFO, pSoldier, String("autofire shots %d APcost %d burst AP %d aimtime %d reserve AP %d", pSoldier->bDoAutofire, BestShot.ubAPCost, ubBurstAPs, sActualAimAP, sReserveAP), gLogDecideActionRed); // minimum 3 bullets if (pSoldier->bDoAutofire >= 3 && pSoldier->bActionPoints >= BestShot.ubAPCost + sActualAimAP + ubBurstAPs + sReserveAP) @@ -3145,7 +3150,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.bNextTargetLevel = BestShot.bTargetLevel; pSoldier->aiData.usActionData = BestShot.ubStance; - DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before shooting")); + DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before shooting"), gLogDecideActionRed); // show "suppression fire" message only if opponent cannot be seen after turning if (!LOS_Raised(pSoldier, MercPtrs[BestShot.ubOpponent], CALC_FROM_ALL_DIRS)) @@ -3161,11 +3166,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!LOS_Raised(pSoldier, MercPtrs[BestShot.ubOpponent], CALC_FROM_ALL_DIRS)) ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_SUPPRESSIONFIRE]); + DebugAI(AI_MSG_INFO, pSoldier, String("Suppression fire!"), gLogDecideActionRed); return(AI_ACTION_FIRE_GUN); } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Suppression not possible"), gLogDecideActionRed); pSoldier->bDoBurst = 0; pSoldier->bDoAutofire = 0; } @@ -3173,23 +3180,30 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } // suppression not possible, do something else - // Flugente: trait skills - // if we are a radio operator + + //////////////////////////////////////////////////////////////////////// + // RADIO OPERATOR + //////////////////////////////////////////////////////////////////////// if (HAS_SKILL_TRAIT(pSoldier, RADIO_OPERATOR_NT) > 0 && pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY, TRUE)) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Radio operator]"), gLogDecideActionRed); + UINT32 tmp; INT32 skilltargetgridno = 0; // call reinforcements if we haven't yet done so if (!gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition && MoreFriendsThanEnemiesinNearbysectors(pSoldier->bTeam, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Attempt to call reinforcements"), gLogDecideActionRed); // if frequencies are jammed... if (SectorJammed()) { + DebugAI(AI_MSG_INFO, pSoldier, String("Someone's jamming radio!"), gLogDecideActionRed); // if we are jamming, turn it off, otherwise, bad luck... if (pSoldier->IsJamming()) { + DebugAI(AI_MSG_INFO, pSoldier, String("Turn off radio jamming..."), gLogDecideActionRed); pSoldier->usAISkillUse = SKILLS_RADIO_TURNOFF; pSoldier->aiData.usActionData = skilltargetgridno; return(AI_ACTION_USE_SKILL); @@ -3199,12 +3213,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) else if (!(pSoldier->usSoldierFlagMask & SOLDIER_RAISED_REDALERT)) { // raise alarm! + DebugAI(AI_MSG_INFO, pSoldier, String("Call for reinforcements!"), gLogDecideActionRed); return(AI_ACTION_RED_ALERT); } } - // if we can't call in artillery, jam frequencies, so that the palyer can't use radio skills + // if we can't call in artillery, jam frequencies, so that the player can't use radio skills else if (!pSoldier->IsJamming() && !pSoldier->CanAnyArtilleryStrikeBeOrdered(&tmp)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Start jamming radio frequencies"), gLogDecideActionRed); pSoldier->usAISkillUse = SKILLS_RADIO_JAM; pSoldier->aiData.usActionData = skilltargetgridno; return(AI_ACTION_USE_SKILL); @@ -3280,16 +3296,21 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // Flugente: if we see one of our buddies captured, it is a clear sign of enemy activity! if ( gGameExternalOptions.fAllowPrisonerSystem && pSoldier->bTeam == ENEMY_TEAM ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Free friendly POWs]"), gLogDecideActionRed); UINT8 ubPerson = GetClosestFlaggedSoldierID( pSoldier, 20, ENEMY_TEAM, SOLDIER_POW, TRUE ); if ( ubPerson != NOBODY ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Found friendly POW"), gLogDecideActionRed); + // if we are close, we can release this guy // possible only if not handcuffed (binders can be opened, handcuffs not) if ( !HasItemFlag( (&(MercPtrs[ubPerson]->inv[HANDPOS]))->usItem, HANDCUFFS ) ) { if ( PythSpacesAway(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo) < 2 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("I am close enough to free POW"), gLogDecideActionRed); + // see if we are facing this person UINT8 ubDesiredMercDir = GetDirectionFromCenterCellXYGridNo(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo); @@ -3298,9 +3319,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { pSoldier->aiData.usActionData = ubDesiredMercDir; + DebugAI(AI_MSG_INFO, pSoldier, String("Change facing"), gLogDecideActionRed); return( AI_ACTION_CHANGE_FACING ); } + DebugAI(AI_MSG_INFO, pSoldier, String("Free POW"), gLogDecideActionRed); return(AI_ACTION_FREE_PRISONER); } else @@ -3309,6 +3332,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Move closer to POW"), gLogDecideActionRed); return(AI_ACTION_SEEK_FRIEND); } } @@ -3316,19 +3340,29 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } } + + //////////////////////////////////////////////////////////////////////////// + // PROVIDE / SEEK MEDICAL AID + //////////////////////////////////////////////////////////////////////////// + // if we are a doctor with medical gear, we might be able to help a wounded ally if ( pSoldier->CanMedicAI() ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Provide medical aid]"), gLogDecideActionRed); + UINT8 ubPerson = GetClosestWoundedSoldierID( pSoldier, gGameExternalOptions.sEnemyMedicsSearchRadius, pSoldier->bTeam); // are we ourselves the patient? if ( ubPerson == pSoldier->ubID ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Patch ourselves up!"), gLogDecideActionRed); + // if not already crouched, crouch down first if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_CROUCH && IsValidStance( pSoldier, ANIM_CROUCH ) && GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints ) { pSoldier->aiData.usActionData = ANIM_CROUCH; + DebugAI(AI_MSG_INFO, pSoldier, String("Crouch down"), gLogDecideActionRed); return(AI_ACTION_CHANGE_STANCE); } @@ -3336,8 +3370,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } else if ( ubPerson != NOBODY ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Someone else is injured"), gLogDecideActionRed); + if ( PythSpacesAway(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo) < 2 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Wounded soldier is nearby"), gLogDecideActionRed); + // see if we are facing this person UINT8 ubDesiredMercDir = GetDirectionFromCenterCellXYGridNo(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo); @@ -3346,6 +3384,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { pSoldier->aiData.usActionData = ubDesiredMercDir; + DebugAI(AI_MSG_INFO, pSoldier, String("Change facing"), gLogDecideActionRed); return( AI_ACTION_CHANGE_FACING ); } @@ -3354,17 +3393,21 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { pSoldier->aiData.usActionData = ANIM_CROUCH; + DebugAI(AI_MSG_INFO, pSoldier, String("Crouch down"), gLogDecideActionRed); return(AI_ACTION_CHANGE_STANCE); } + DebugAI(AI_MSG_INFO, pSoldier, String("Administer aid"), gLogDecideActionRed); return(AI_ACTION_DOCTOR); } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Wounded soldier is far"), gLogDecideActionRed); pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, MercPtrs[ubPerson]->sGridNo, 20, AI_ACTION_SEEK_FRIEND, 0); if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Try to move towards the wounded person"), gLogDecideActionRed); return(AI_ACTION_SEEK_FRIEND); } } @@ -3373,56 +3416,79 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if we are not a medic, but are wounded, seek a medic else if ( pSoldier->iHealableInjury >= gGameExternalOptions.sEnemyMedicsWoundMinAmount ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Seek medical aid]"), gLogDecideActionRed); + UINT8 ubPerson = GetClosestMedicSoldierID( pSoldier, gGameExternalOptions.sEnemyMedicsSearchRadius / 2, pSoldier->bTeam); if ( ubPerson != NOBODY ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Found a medic!"), gLogDecideActionRed); + if ( PythSpacesAway(pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo) > 1 ) { pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, MercPtrs[ubPerson]->sGridNo, 20, AI_ACTION_SEEK_FRIEND, 0); if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek aid"), gLogDecideActionRed); return(AI_ACTION_SEEK_FRIEND); } } } + else { DebugAI(AI_MSG_INFO, pSoldier, String("No medics around! :("), gLogDecideActionRed); } } + + //////////////////////////////////////////////////////////////////////////// + // VIP RETREAT + //////////////////////////////////////////////////////////////////////////// // VIPs run away (but not the GENERAL) if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP && pSoldier->ubProfile != GENERAL ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[VIP Retreat]"), gLogDecideActionRed); + // this is in red AI state - a firefight is going on, we try to escape pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents( pSoldier ); // if we don't know where our opponents are, we cannot run away from them... if ( TileIsOutOfBounds( pSoldier->aiData.usActionData ) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Don't know where enemies are, head for nearest map edge"), gLogDecideActionRed); // search for the closest map edge pSoldier->aiData.usActionData = FindClosestExitGrid( pSoldier, pSoldier->sGridNo, 200 ); } if ( !TileIsOutOfBounds( pSoldier->aiData.usActionData ) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Run away!"), gLogDecideActionRed); return AI_ACTION_RUN_AWAY; } + else { DebugAI(AI_MSG_INFO, pSoldier, String("No valid gridno found! Tried to head for gridno %d", pSoldier->aiData.usActionData), gLogDecideActionRed); } } + + //////////////////////////////////////////////////////////////////////////// + // PROTECT VIP + //////////////////////////////////////////////////////////////////////////// // are we a bodyguard? if ( pSoldier->usSoldierFlagMask & SOLDIER_BODYGUARD ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Bodyguard]"), gLogDecideActionRed); // is VIP still alive? UINT16 ubPerson = GetClosestFlaggedSoldierID( pSoldier, 100, pSoldier->bTeam, SOLDIER_VIP, FALSE ); if ( ubPerson != NOBODY ) { + DebugAI(AI_MSG_INFO, pSoldier, String("VIP found"), gLogDecideActionRed); // we want to stay close to him, but still be able to function properly... stay withing a 7-tile radius if ( SpacesAway( pSoldier->sGridNo, MercPtrs[ubPerson]->sGridNo ) > 7 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Attempt to get close "), gLogDecideActionRed); pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards( pSoldier, MercPtrs[ubPerson]->sGridNo, 20, AI_ACTION_SEEK_FRIEND, 0 ); if ( !TileIsOutOfBounds( pSoldier->aiData.usActionData ) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek VIP"), gLogDecideActionRed); return(AI_ACTION_SEEK_FRIEND); } } @@ -3433,7 +3499,6 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //////////////////////////////////////////////////////////////////////// // RED RETREAT //////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[retreat]")); if (gfTurnBasedAI && !fCivilian && !bInWater && @@ -3444,12 +3509,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->RetreatCounterValue() > 0 && (pSoldier->CheckInitialAP() || !fAnyCover || pSoldier->aiData.bUnderFire)) { - DebugAI(AI_MSG_TOPIC, pSoldier, String("search for retreat spot")); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[retreat]"), gLogDecideActionRed); + DebugAI(AI_MSG_TOPIC, pSoldier, String("search for retreat spot"), gLogDecideActionRed); INT32 sRetreatSpot = FindRetreatSpot(pSoldier); if (!TileIsOutOfBounds(sRetreatSpot)) { - DebugAI(AI_MSG_TOPIC, pSoldier, String("found retreat spot %d", sRetreatSpot)); + DebugAI(AI_MSG_TOPIC, pSoldier, String("found retreat spot %d", sRetreatSpot), gLogDecideActionRed); //BeginMultiPurposeLocator(sRetreatSpot, pSoldier->pathing.bLevel, FALSE); @@ -3458,14 +3524,16 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } } - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch and rest if running out of breath"); + //////////////////////////////////////////////////////////////////////// // CROUCH & REST IF RUNNING OUT OF BREATH //////////////////////////////////////////////////////////////////////// + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "decideactionred: crouch and rest if running out of breath"); // if our breath is running a bit low, and we're not in water or under fire if ((pSoldier->bBreath < 25) && !bInWater && !pSoldier->aiData.bUnderFire) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Running out of breath, try to rest]"), gLogDecideActionRed); // if not already crouched, try to crouch down first if (!fCivilian && !PTR_CROUCHED && IsValidStance( pSoldier, ANIM_CROUCH ) && gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE) { @@ -3478,6 +3546,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { pSoldier->aiData.usActionData = ANIM_CROUCH; + DebugAI(AI_MSG_INFO, pSoldier, String("Crouch"), gLogDecideActionRed); return(AI_ACTION_CHANGE_STANCE); } } @@ -3493,8 +3562,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( WeaponReady(pSoldier) && GetBPCostPer10APsForGunHolding( pSoldier ) > 0 ) { // unready - return(AI_ACTION_LOWER_GUN); + DebugAI(AI_MSG_INFO, pSoldier, String("Lower weapon"), gLogDecideActionRed); + return(AI_ACTION_LOWER_GUN); } + + DebugAI(AI_MSG_INFO, pSoldier, String("Rest"), gLogDecideActionRed); return(AI_ACTION_NONE); } @@ -3508,6 +3580,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if a guy is feeling REALLY discouraged, he may continue to run like hell if ((pSoldier->aiData.bAIMorale == MORALE_HOPELESS) && ubCanMove) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Low morale, attempting to run away]"), gLogDecideActionRed); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: run away"); //////////////////////////////////////////////////////////////////////// // RUN AWAY TO SPOT FARTHEST FROM KNOWN THREATS (ONLY IF MORALE HOPELESS) @@ -3522,13 +3595,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) sprintf(tempstr,"%s RUNNING AWAY to grid %d",pSoldier->name,pSoldier->aiData.usActionData); AIPopMessage(tempstr); #endif - + DebugAI(AI_MSG_INFO, pSoldier, String("Running away to grid %d", pSoldier->aiData.usActionData), gLogDecideActionRed); return(AI_ACTION_RUN_AWAY); } } - DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: radio red alert?"); //////////////////////////////////////////////////////////////////////////// // RADIO RED ALERT: determine %chance to call others and report contact //////////////////////////////////////////////////////////////////////////// @@ -3539,14 +3611,21 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: checking to radio red alert"); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Radio red alert]"), gLogDecideActionRed); // if there hasn't been an initial RED ALERT yet in this sector - if ( !(gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition) || NeedToRadioAboutPanicTrigger() ) + if (!(gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition) || NeedToRadioAboutPanicTrigger()) + { + DebugAI(AI_MSG_INFO, pSoldier, String("No previous alert radioed"), gLogDecideActionRed); // since I'm at STATUS RED, I obviously know we're being invaded! iChance = gbDiff[DIFF_RADIO_RED_ALERT][ SoldierDifficultyLevel( pSoldier ) ]; + } else // subsequent radioing (only to update enemy positions, request help) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Red alert already radioed"), gLogDecideActionRed); // base chance depends on how much new info we have to radio to the others iChance = 10 * WhatIKnowThatPublicDont(pSoldier,FALSE); // use 10 * for RED alert + } // if I actually know something they don't and I ain't swimming (deep water) if (iChance && !bInDeepWater) @@ -3597,6 +3676,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) #ifdef DEBUGDECISIONS AINumMessage("Chance to radio RED alert = ",iChance); #endif + DebugAI(AI_MSG_INFO, pSoldier, String("Chance to radio alert = %d", iChance), gLogDecideActionRed); if ((INT16) PreRandom(100) < iChance) { @@ -3605,12 +3685,16 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) #endif DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: decided to radio red alert"); + DebugAI(AI_MSG_INFO, pSoldier, String("Decided to radio red alert"), gLogDecideActionRed); return(AI_ACTION_RED_ALERT); } } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Self smoke when under fire]")); + + //////////////////////////////////////////////////////////////////////////// + // THROW A SMOKE GRENADE FOR COVER + //////////////////////////////////////////////////////////////////////////// if (gfTurnBasedAI && pSoldier->bActionPoints == pSoldier->bInitialActionPoints && pSoldier->aiData.bUnderFire && @@ -3623,13 +3707,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) (!fProneSightCover && !AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || pSoldier->TakenLargeHit()) && (pSoldier->TakenLargeHit() || pSoldier->ShockLevelPercent() > 20 + Random(80))) { - DebugAI(AI_MSG_INFO, pSoldier, String("check if soldier can cover himself with smoke")); - + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Self smoke when under fire]"), gLogDecideActionRed); CheckTossSelfSmoke(pSoldier, &BestThrow); if (BestThrow.ubPossible) { - DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime)); + DebugAI(AI_MSG_INFO, pSoldier, String("prepare throw at spot %d level %d aimtime %d", BestThrow.sTarget, BestThrow.bTargetLevel, BestThrow.ubAimTime), gLogDecideActionRed); // start retreating for several turns pSoldier->RetreatCounterStart(2); @@ -3637,7 +3720,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if necessary, swap the usItem from holster into the hand position if (BestThrow.bWeaponIn != HANDPOS) { - DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket")); + DebugAI(AI_MSG_INFO, pSoldier, String("rearrange pocket"), gLogDecideActionRed); RearrangePocket(pSoldier, HANDPOS, BestThrow.bWeaponIn, FOREVER); } @@ -3645,6 +3728,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (gAnimControl[pSoldier->usAnimState].ubEndHeight < BestThrow.ubStance && pSoldier->InternalIsValidStance(AIDirection(pSoldier->sGridNo, BestThrow.sTarget), BestThrow.ubStance)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Change stance before throw"), gLogDecideActionRed); pSoldier->aiData.usActionData = BestThrow.ubStance; pSoldier->aiData.bNextAction = AI_ACTION_TOSS_PROJECTILE; pSoldier->aiData.usNextActionData = BestThrow.sTarget; @@ -3659,8 +3743,10 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.bAimTime = BestThrow.ubAimTime; } + DebugAI(AI_MSG_INFO, pSoldier, String("Throw smoke!"), gLogDecideActionRed); return(AI_ACTION_TOSS_PROJECTILE); } + else { DebugAI(AI_MSG_INFO, pSoldier, String("Throw not possible"), gLogDecideActionRed); } } // sevenfm: no Main Red AI for civilians @@ -3670,7 +3756,10 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: main red ai"); - // sevenfm: avoid light if spot is dangerous and no friends see my closest enemy + + //////////////////////////////////////////////////////////////////////////// + // AVOID LIGHT IF SPOT IS DANGEROUS AND NO FRIENDS SEE MY CLOSEST ENEMY + //////////////////////////////////////////////////////////////////////////// if (ubCanMove && InLightAtNight( pSoldier->sGridNo, pSoldier->pathing.bLevel ) && pSoldier->aiData.bOrders != STATIONARY && @@ -3682,6 +3771,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { // move as if leaving water or gas + DebugAI(AI_MSG_INFO, pSoldier, String("Move out of light"), gLogDecideActionRed); return( AI_ACTION_LEAVE_WATER_GAS ); } } @@ -3709,6 +3799,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_PRONE && !pSoldier->aiData.bUnderFire ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Continue flanking]"), gLogDecideActionRed); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: continue flanking"); INT16 currDir = GetDirectionFromGridNo ( sFlankGridNo, pSoldier ); INT16 origDir = pSoldier->origDir; @@ -3721,16 +3812,23 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // stop flanking condition if ( (currDir - origDir) >= MinFlankDirections(pSoldier) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop flanking, left"), gLogDecideActionRed); pSoldier->numFlanks = MAX_FLANKS_RED; } else { pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_LEFT); - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) ) //&& (currDir - origDir) < 2 ) + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) //&& (currDir - origDir) < 2 ) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Flank left"), gLogDecideActionRed); return AI_ACTION_FLANK_LEFT ; + } else + { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop flanking left, tile out of bounds"), gLogDecideActionRed); pSoldier->numFlanks = MAX_FLANKS_RED; + } } } else @@ -3741,16 +3839,23 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // stop flanking condition if ( (origDir - currDir) >= MinFlankDirections(pSoldier) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop flanking, right"), gLogDecideActionRed); pSoldier->numFlanks = MAX_FLANKS_RED; } else { pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sFlankGridNo , AI_ACTION_FLANK_RIGHT); - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) )//&& (origDir - currDir) < 2 ) + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))//&& (origDir - currDir) < 2 ) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Flank right"), gLogDecideActionRed); return AI_ACTION_FLANK_RIGHT ; + } else + { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop flanking right, tile ouf of bounds"), gLogDecideActionRed); pSoldier->numFlanks = MAX_FLANKS_RED; + } } } } @@ -3762,10 +3867,12 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( pSoldier->numFlanks == MAX_FLANKS_RED ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: stop flanking"); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Stop flanking]"), gLogDecideActionRed); // start end flank approach with full APs if( gfTurnBasedAI && pSoldier->bActionPoints < pSoldier->bInitialActionPoints ) { + DebugAI(AI_MSG_INFO, pSoldier, String("AP not full, wait a turn"), gLogDecideActionRed); return(AI_ACTION_END_TURN); } @@ -3777,6 +3884,8 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) ( PythSpacesAway( pSoldier->sGridNo, sFlankGridNo ) > MIN_FLANK_DIST_RED || !LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) ) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Move towards enemy"), gLogDecideActionRed); + pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sFlankGridNo,GetAPsCrouch( pSoldier, TRUE),AI_ACTION_SEEK_OPPONENT,0); // sevenfm: avoid going into water, gas or light @@ -3789,37 +3898,44 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) && !LocationToLocationLineOfSightTest( pSoldier->sGridNo, pSoldier->pathing.bLevel, sFlankGridNo, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Can be seen in new position, prepare crouch & shot"), gLogDecideActionRed); + // reserve APs for a possible crouch plus a shot INT32 sCautiousGridNo = InternalGoAsFarAsPossibleTowards(pSoldier, sFlankGridNo, (INT8) (MinAPsToAttack( pSoldier, sFlankGridNo, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE) + GetAPsToLook(pSoldier)), AI_ACTION_SEEK_OPPONENT, FLAG_CAUTIOUS ); if (!TileIsOutOfBounds(sCautiousGridNo)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy to cautiosgridno %d", sCautiousGridNo), gLogDecideActionRed); pSoldier->aiData.usActionData = sCautiousGridNo; pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; return(AI_ACTION_SEEK_OPPONENT); } + + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy to gridno %d", pSoldier->aiData.usActionData), gLogDecideActionRed); return(AI_ACTION_SEEK_OPPONENT); } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy"), gLogDecideActionRed); return(AI_ACTION_SEEK_OPPONENT); } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Can't advance, stop flanking"), gLogDecideActionRed); // if we cannot advance to spot, stop trying pSoldier->numFlanks++; } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Stop flanking"), gLogDecideActionRed); // stop pSoldier->numFlanks++; } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Set watched location]")); if (pSoldier->CheckInitialAP() && pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] && gfTurnBasedAI && @@ -3835,6 +3951,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) !SoldierToVirtualSoldierLineOfSightTest(pSoldier, sClosestDisturbance, pSoldier->pathing.bLevel, ANIM_STAND, TRUE, CALC_FROM_ALL_DIRS) && CountFriendsBlack(pSoldier, sClosestDisturbance) == 0) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Set watched location]"), gLogDecideActionRed); gubNPCAPBudget = 0; gubNPCDistLimit = 0; @@ -3844,8 +3961,8 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) INT16 sLoop; INT32 sLastSeenSpot = NOWHERE; - DebugAI(AI_MSG_INFO, pSoldier, String("found path to %d, path size %d ", sClosestDisturbance, pSoldier->pathing.usPathDataSize)); - DebugAI(AI_MSG_INFO, pSoldier, String("check path for seen spots")); + DebugAI(AI_MSG_INFO, pSoldier, String("found path to %d, path size %d ", sClosestDisturbance, pSoldier->pathing.usPathDataSize), gLogDecideActionRed); + DebugAI(AI_MSG_INFO, pSoldier, String("check path for seen spots"), gLogDecideActionRed); sCheckGridNo = pSoldier->sGridNo; @@ -3862,7 +3979,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if found last seen spot if (!TileIsOutOfBounds(sLastSeenSpot)) { - DebugAI(AI_MSG_INFO, pSoldier, String("last seen spot %d level %d", sLastSeenSpot, pSoldier->pathing.bLevel)); + DebugAI(AI_MSG_INFO, pSoldier, String("last seen spot %d level %d", sLastSeenSpot, pSoldier->pathing.bLevel), gLogDecideActionRed); IncrementWatchedLoc(pSoldier->ubID, sLastSeenSpot, pSoldier->pathing.bLevel); } } @@ -3876,6 +3993,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (ubCanMove && pSoldier->bActionPoints > APBPConstants[MAX_AP_CARRIED]) { DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("decideactionred: checking hide/seek/help/watch points... orders = %d, attitude = %d", pSoldier->aiData.bOrders, pSoldier->aiData.bAttitude)); + DebugAI(AI_MSG_INFO, pSoldier, String("checking hide/seek/help/watch points... orders = %d, attitude = %d", pSoldier->aiData.bOrders, pSoldier->aiData.bAttitude), gLogDecideActionRed); // calculate initial points for watch based on highest watch loc bWatchPts = GetHighestWatchedLocPoints(pSoldier->ubID); @@ -3972,6 +4090,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("decideactionred: hide = %d, seek = %d, watch = %d, help = %d",bHidePts,bSeekPts,bWatchPts,bHelpPts)); + DebugAI(AI_MSG_INFO, pSoldier, String("hide = %d, seek = %d, watch = %d, help = %d", bHidePts, bSeekPts, bWatchPts, bHelpPts), gLogDecideActionRed); // while one of the three main RED REACTIONS remains viable while ((bSeekPts > -90) || (bHelpPts > -90) || (bHidePts > -90) ) { @@ -3994,6 +4113,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) (gAnimControl[pSoldier->usAnimState].ubHeight != ANIM_PRONE || !GuySawEnemy( pSoldier )) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: seek opponent"); + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy"), gLogDecideActionRed); ////////////////////////////////////////////////////////////////////// // SEEK CLOSEST DISTURBANCE: GO DIRECTLY TOWARDS CLOSEST KNOWN OPPONENT ////////////////////////////////////////////////////////////////////// @@ -4043,6 +4163,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( APBPConstants[AP_CLIMBROOF] + MinAPsToAttack( pSoldier, sClosestDisturbance, ADDTURNCOST,0))) { + DebugAI(AI_MSG_INFO, pSoldier, String("Climb roof at gridno %d", sClosestDisturbance), gLogDecideActionRed); return( AI_ACTION_CLIMB_ROOF ); } } @@ -4057,6 +4178,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) INT32 usClimbPoint = sClosestDisturbance; if (!TileIsOutOfBounds(usClimbPoint)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Move towards climb spot %d", usClimbPoint), gLogDecideActionRed); pSoldier->aiData.usActionData = usClimbPoint; return( AI_ACTION_MOVE_TO_CLIMB ); } @@ -4070,6 +4192,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) BOOLEAN fOvercrowded = FALSE; if( CountNearbyFriends(pSoldier, pSoldier->sGridNo, TACTICAL_RANGE / 4) > 2 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Soldier position %d is overcrowded", pSoldier->sGridNo), gLogDecideActionRed); fOvercrowded = TRUE; } @@ -4088,6 +4211,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] && ( CountFriendsInDirection( pSoldier, sClosestDisturbance ) > 1 || NightTime() || fOvercrowded) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Possibly start flanking]"), gLogDecideActionRed); INT8 action = AI_ACTION_SEEK_OPPONENT; INT16 dist = PythSpacesAway ( pSoldier->sGridNo, sClosestDisturbance ); if ( dist > MIN_FLANK_DIST_RED && dist < MAX_FLANK_DIST_RED ) @@ -4099,26 +4223,36 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) case 1: case 2: case 3: - if ( pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT ) + if (pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Try to flank left"), gLogDecideActionRed); action = AI_ACTION_FLANK_LEFT ; + } break; default: - if ( pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT ) + if (pSoldier->aiData.bLastAction != AI_ACTION_FLANK_LEFT && pSoldier->aiData.bLastAction != AI_ACTION_FLANK_RIGHT) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Try to flank right"), gLogDecideActionRed); action = AI_ACTION_FLANK_RIGHT ; + } break; } if (action == AI_ACTION_SEEK_OPPONENT) { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy instead"), gLogDecideActionRed); return action; } } else - return AI_ACTION_SEEK_OPPONENT ; - + { + DebugAI(AI_MSG_INFO, pSoldier, String("Distance not suitable, seek enemy instead"), gLogDecideActionRed); + return AI_ACTION_SEEK_OPPONENT; + } pSoldier->aiData.usActionData = FindFlankingSpot (pSoldier, sClosestDisturbance, action ); if (TileIsOutOfBounds(pSoldier->aiData.usActionData) || pSoldier->numFlanks >= MAX_FLANKS_RED ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Flanking spot %d out of bounds or numFlanks >= MAX_FLANKS_RED", pSoldier->aiData.usActionData), gLogDecideActionRed); pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier,sClosestDisturbance,GetAPsCrouch( pSoldier, TRUE), AI_ACTION_SEEK_OPPONENT,0); //pSoldier->numFlanks = 0; if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS ) ) @@ -4128,6 +4262,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Reserved AP for crouch & shot, seek enemy"), gLogDecideActionRed); pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; return(AI_ACTION_SEEK_OPPONENT); @@ -4136,11 +4271,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) else { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy"), gLogDecideActionRed); return(AI_ACTION_SEEK_OPPONENT); } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Found flanking spot %d", pSoldier->aiData.usActionData), gLogDecideActionRed); if ( action == AI_ACTION_FLANK_LEFT ) pSoldier->flags.lastFlankLeft = TRUE; else @@ -4160,11 +4297,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->aiData.bOrders = FARPATROL; } + DebugAI(AI_MSG_INFO, pSoldier, String("Start flanking"), gLogDecideActionRed); return(action); } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Not flanking, move up towards enemy"), gLogDecideActionRed); // let's be a bit cautious about going right up to a location without enough APs to shoot if ( PythSpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || LocationToLocationLineOfSightTest( pSoldier->aiData.usActionData, pSoldier->pathing.bLevel, sClosestDisturbance, pSoldier->pathing.bLevel, TRUE, CALC_FROM_ALL_DIRS ) ) { @@ -4173,6 +4312,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Reserved AP for crouch & shot, seek enemy"), gLogDecideActionRed); pSoldier->aiData.fAIFlags |= AI_CAUTIOUS; pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; return(AI_ACTION_SEEK_OPPONENT); @@ -4180,6 +4320,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Seek enemy"), gLogDecideActionRed); return(AI_ACTION_SEEK_OPPONENT); } break; @@ -4199,7 +4340,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if WATCHING is possible and at least as desirable as anything else if ((bWatchPts > -90) && (bWatchPts >= bSeekPts) && (bWatchPts >= bHelpPts) && (bWatchPts >= bHidePts )) { - DebugAI(AI_MSG_INFO, pSoldier, String("[watch]")); + DebugAI(AI_MSG_INFO, pSoldier, String("[watch]"), gLogDecideActionRed); // take a look at our highest watch point... if it's still visible, turn to face it and then wait bHighestWatchLoc = GetHighestVisibleWatchedLoc( pSoldier->ubID ); @@ -4207,7 +4348,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { // see if we need turn to face that location ubOpponentDir = AIDirection(pSoldier->sGridNo, gsWatchedLoc[pSoldier->ubID][bHighestWatchLoc]); - DebugAI(AI_MSG_INFO, pSoldier, String("Highest watch location: [%d] %d %d watch dir: %d", bHighestWatchLoc, gsWatchedLoc[pSoldier->ubID][bHighestWatchLoc], gbWatchedLocLevel[pSoldier->ubID][bHighestWatchLoc], ubOpponentDir)); + DebugAI(AI_MSG_INFO, pSoldier, String("Highest watch location: [%d] %d %d watch dir: %d", bHighestWatchLoc, gsWatchedLoc[pSoldier->ubID][bHighestWatchLoc], gbWatchedLocLevel[pSoldier->ubID][bHighestWatchLoc], ubOpponentDir), gLogDecideActionRed); // consider at least crouching if (gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_STAND && @@ -4216,7 +4357,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { pSoldier->aiData.usActionData = ANIM_CROUCH; - DebugAI(AI_MSG_INFO, pSoldier, String("crouch to watch")); + DebugAI(AI_MSG_INFO, pSoldier, String("crouch to watch"), gLogDecideActionRed); return(AI_ACTION_CHANGE_STANCE); } @@ -4226,7 +4367,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) (pSoldier->bBreath > OKBREATH * 2 || GetBPCostPer10APsForGunHolding(pSoldier, TRUE) < 50) && pSoldier->bActionPoints >= GetAPsToReadyWeapon(pSoldier, PickSoldierReadyAnimation(pSoldier, FALSE, FALSE))) { - DebugAI(AI_MSG_INFO, pSoldier, String("raise weapon")); + DebugAI(AI_MSG_INFO, pSoldier, String("raise weapon"), gLogDecideActionRed); return AI_ACTION_RAISE_GUN; } @@ -4237,7 +4378,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { // turn pSoldier->aiData.usActionData = ubOpponentDir; - DebugAI(AI_MSG_INFO, pSoldier, String("turn to watched location")); + DebugAI(AI_MSG_INFO, pSoldier, String("turn to watched location"), gLogDecideActionRed); return(AI_ACTION_CHANGE_FACING); } @@ -4251,11 +4392,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { pSoldier->aiData.usActionData = ANIM_PRONE; pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; - DebugAI(AI_MSG_INFO, pSoldier, String("go prone, end turn")); + DebugAI(AI_MSG_INFO, pSoldier, String("go prone, end turn"), gLogDecideActionRed); return(AI_ACTION_CHANGE_STANCE); } - DebugAI(AI_MSG_INFO, pSoldier, String("watch at %d level %d", gsWatchedLoc[pSoldier->ubID][bHighestWatchLoc], gbWatchedLocLevel[pSoldier->ubID][bHighestWatchLoc])); + DebugAI(AI_MSG_INFO, pSoldier, String("watch at %d level %d", gsWatchedLoc[pSoldier->ubID][bHighestWatchLoc], gbWatchedLocLevel[pSoldier->ubID][bHighestWatchLoc]), gLogDecideActionRed); return(AI_ACTION_NONE); } @@ -4268,10 +4409,11 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if HELPING is possible and at least as desirable as seeking or hiding if ((bHelpPts > -90) && (bHelpPts >= bSeekPts) && (bHelpPts >= bHidePts) && (bHelpPts >= bWatchPts )) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Help a friend]"), gLogDecideActionRed); #ifdef AI_TIMING_TESTS uiStartTime = GetJA2Clock(); #endif - sClosestFriend = ClosestReachableFriendInTrouble(pSoldier, &fClimb ); + INT32 sClosestFriend = ClosestReachableFriendInTrouble(pSoldier, &fClimb ); #ifdef AI_TIMING_TESTS uiEndTime = GetJA2Clock(); @@ -4283,6 +4425,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //if (!TileIsOutOfBounds(sClosestFriend) && PythSpacesAway(pSoldier->sGridNo, sClosestFriend) > pSoldier->GetMaxDistanceVisible(sClosestFriend, 0, CALC_FROM_ALL_DIRS )) if (!TileIsOutOfBounds(sClosestFriend)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Closest friend at gridno %d", sClosestFriend), gLogDecideActionRed); ////////////////////////////////////////////////////////////////////// // GO DIRECTLY TOWARDS CLOSEST FRIEND UNDER FIRE OR WHO LAST RADIOED ////////////////////////////////////////////////////////////////////// @@ -4295,6 +4438,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) pSoldier->name,sClosestFriend,pSoldier->aiData.usActionData); AIPopMessage(tempstr); #endif + DebugAI(AI_MSG_INFO, pSoldier, String("Seeking friend, moving to %d", pSoldier->aiData.usActionData), gLogDecideActionRed); if ( !ENEMYROBOT(pSoldier) && fClimb )//&& pSoldier->aiData.usActionData == sClosestFriend) { @@ -4313,6 +4457,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if (IsActionAffordable(pSoldier) && pSoldier->bActionPoints >= ( APBPConstants[AP_CLIMBROOF] + MinAPsToAttack( pSoldier, sClosestFriend, ADDTURNCOST,0))) { + DebugAI(AI_MSG_INFO, pSoldier, String("Climb roof"), gLogDecideActionRed); return( AI_ACTION_CLIMB_ROOF ); } } @@ -4323,6 +4468,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //if (!TileIsOutOfBounds(sClimbPoint)) { //pSoldier->aiData.usActionData = sClimbPoint; + DebugAI(AI_MSG_INFO, pSoldier, String("Move towards climb point"), gLogDecideActionRed); return( AI_ACTION_MOVE_TO_CLIMB ); } } @@ -4331,6 +4477,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //{ // return( AI_ACTION_CLIMB_ROOF ); //} + DebugAI(AI_MSG_INFO, pSoldier, String("Seek friend"), gLogDecideActionRed); return(AI_ACTION_SEEK_FRIEND); } } @@ -4349,6 +4496,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if HIDING is possible and at least as desirable as seeking or helping if ((bHidePts > -90) && (bHidePts >= bSeekPts) && (bHidePts >= bHelpPts) && (bHidePts >= bWatchPts )) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Take cover]"), gLogDecideActionRed); //sClosestOpponent = ClosestKnownOpponent( pSoldier, NULL, NULL ); // if an opponent is known (not necessarily reachable or conscious) if (!SkipCoverCheck && !TileIsOutOfBounds(sClosestOpponent)) @@ -4371,6 +4519,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // let's be a bit cautious about going right up to a location without enough APs to shoot if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Found a cover spot at %d", pSoldier->aiData.usActionData), gLogDecideActionRed); sClosestDisturbance = ClosestReachableDisturbance(pSoldier, &fClimb); if (!TileIsOutOfBounds(sClosestDisturbance) && ( SpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) < 5 || SpacesAway( pSoldier->aiData.usActionData, sClosestDisturbance ) + 5 < SpacesAway( pSoldier->sGridNo, sClosestDisturbance ) ) ) { @@ -4378,11 +4527,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // ensure will we have enough APs for a possible crouch plus a shot if ( InternalGoAsFarAsPossibleTowards( pSoldier, pSoldier->aiData.usActionData, (INT8) (MinAPsToAttack( pSoldier, sClosestOpponent, ADDTURNCOST,0) + GetAPsCrouch( pSoldier, TRUE)), AI_ACTION_TAKE_COVER, 0 ) == pSoldier->aiData.usActionData ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Moving to cover, reserve AP for crouch & shot"), gLogDecideActionRed); return(AI_ACTION_TAKE_COVER); } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Moving to cover"), gLogDecideActionRed); return(AI_ACTION_TAKE_COVER); } } @@ -4407,6 +4558,8 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if we're currently under fire (presumably, attacker is hidden) if (pSoldier->aiData.bUnderFire) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Under fire]"), gLogDecideActionRed); + // only try to run if we've actually been hit recently & noticably so // otherwise, presumably our current cover is pretty good & sufficient // HEADROCK HAM B2.6: New value here helps us change the ratio of running away due to shock. This @@ -4427,6 +4580,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (bShock > 0) { + DebugAI(AI_MSG_INFO, pSoldier, String("Soldier is shocked, attempt to run away"), gLogDecideActionRed); // look for best place to RUN AWAY to (farthest from the closest threat) pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier); @@ -4438,6 +4592,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) #endif DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: run away!"); + DebugAI(AI_MSG_INFO, pSoldier, String("Running away to gridno %d", pSoldier->aiData.usActionData), gLogDecideActionRed); return(AI_ACTION_RUN_AWAY); } } @@ -4446,6 +4601,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // UNDER FIRE, DON'T WANNA/CAN'T RUN AWAY, SO CROUCH //////////////////////////////////////////////////////////////////////////// + DebugAI(AI_MSG_INFO, pSoldier, String("Under fire, try to change stance"), gLogDecideActionRed); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"decideactionred: crouch or go prone"); // if not in water and not already crouched if (gAnimControl[pSoldier->usAnimState].ubHeight == ANIM_STAND && IsValidStance(pSoldier, ANIM_CROUCH)) @@ -4457,6 +4613,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) sprintf(tempstr, "%s CROUCHES (STATUS RED)", pSoldier->name); AIPopMessage(tempstr); #endif + DebugAI(AI_MSG_INFO, pSoldier, String("Crouching"), gLogDecideActionRed); pSoldier->aiData.usActionData = ANIM_CROUCH; return(AI_ACTION_CHANGE_STANCE); @@ -4467,6 +4624,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // maybe go prone if (PreRandom(2) == 0 && IsValidStance(pSoldier, ANIM_PRONE)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Go prone"), gLogDecideActionRed); pSoldier->aiData.usActionData = ANIM_PRONE; return(AI_ACTION_CHANGE_STANCE); } @@ -4482,24 +4640,27 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //!TileIsOutOfBounds( sClosestNoise ) && PythSpacesAway(pSoldier->sGridNo, sClosestNoise) < TACTICAL_RANGE / 2) ) //CorpseWarning(pSoldier, pSoldier->sGridNo, pSoldier->pathing.bLevel) { - DebugAI(AI_MSG_TOPIC, pSoldier, String("[civilians run away]")); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[civilians run away]"), gLogDecideActionRed); // look for best place to RUN AWAY to (farthest from the closest threat) pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier); - DebugAI(AI_MSG_INFO, pSoldier, String("found run away spot %d", pSoldier->aiData.usActionData)); + DebugAI(AI_MSG_INFO, pSoldier, String("found run away spot %d", pSoldier->aiData.usActionData), gLogDecideActionRed); if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Running away!"), gLogDecideActionRed); return(AI_ACTION_RUN_AWAY); } //else if (!pSoldier->SkipCoverCheck() && gfTurnBasedAI) // only do in turnbased else if (!SkipCoverCheck && gfTurnBasedAI) // only do in turnbased { + DebugAI(AI_MSG_INFO, pSoldier, String("Can't run away, try to take cover"), gLogDecideActionRed); // try to take cover pSoldier->aiData.bAIMorale = MORALE_WORRIED; pSoldier->aiData.usActionData = FindBestNearbyCover(pSoldier, MORALE_WORRIED, &iDummy); if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) { + DebugAI(AI_MSG_INFO, pSoldier, String("Take cover"), gLogDecideActionRed); return(AI_ACTION_TAKE_COVER); } } @@ -4518,6 +4679,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!TileIsOutOfBounds(sClosestOpponent)) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Look around towards enemy]"), gLogDecideActionRed); // determine direction from this soldier to the closest opponent ubOpponentDir = GetDirectionFromCenterCellXYGridNo(pSoldier->sGridNo, sClosestOpponent); @@ -4550,13 +4712,15 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) sprintf(tempstr,"%s - TURNS TOWARDS CLOSEST ENEMY to face direction %d",pSoldier->name,pSoldier->aiData.usActionData); AIPopMessage(tempstr); #endif - if ( pSoldier->aiData.bOrders == SNIPER && + DebugAI(AI_MSG_INFO, pSoldier, String("Turn towards closest enemy, face direction %d", pSoldier->aiData.usActionData), gLogDecideActionRed); + if ( pSoldier->aiData.bOrders == SNIPER && !WeaponReady(pSoldier) && PickSoldierReadyAnimation(pSoldier, FALSE, FALSE) != INVALID_ANIMATION && (pSoldier->bBreath > 15 || GetBPCostPer10APsForGunHolding( pSoldier, TRUE ) < 50) ) { if (!gfTurnBasedAI || GetAPsToReadyWeapon( pSoldier, READY_RIFLE_CROUCH ) <= pSoldier->bActionPoints) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun, sniper"), gLogDecideActionRed); pSoldier->aiData.bNextAction = AI_ACTION_RAISE_GUN; } } @@ -4572,13 +4736,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if ( Random(100) < 35 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun, scoped weapon"), gLogDecideActionRed); pSoldier->aiData.bNextAction = AI_ACTION_RAISE_GUN; } } } } //////////////////////////////////////////////////////////////////////////// - return(AI_ACTION_CHANGE_FACING); } } @@ -4588,16 +4752,19 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) !WeaponReady(pSoldier) && PickSoldierReadyAnimation(pSoldier, FALSE, FALSE) != INVALID_ANIMATION) { + DebugAI(AI_MSG_INFO, pSoldier, String("Facing enemy already"), gLogDecideActionRed); if ((!gfTurnBasedAI || GetAPsToReadyWeapon( pSoldier, pSoldier->usAnimState ) <= pSoldier->bActionPoints) && (pSoldier->bBreath > 15 || GetBPCostPer10APsForGunHolding( pSoldier, TRUE ) < 50)) { if ( pSoldier->aiData.bOrders == SNIPER ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun, sniper"), gLogDecideActionRed); return AI_ACTION_RAISE_GUN; } else if (IsScoped(&pSoldier->inv[HANDPOS])) { if ( Random(100) < 40 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun, scoped weapon"), gLogDecideActionRed); return AI_ACTION_RAISE_GUN; } } @@ -4605,6 +4772,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if ( Random(100) < 20 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun"), gLogDecideActionRed); return AI_ACTION_RAISE_GUN; } } @@ -4616,6 +4784,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( ARMED_VEHICLE( pSoldier ) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Armed vehicle]"), gLogDecideActionRed); // try turning in a random direction as we still can't see anyone. if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) { @@ -4626,11 +4795,13 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) ubOpponentDir = GetDirectionFromCenterCellXYGridNo(pSoldier->sGridNo, sClosestDisturbance); if ( pSoldier->ubDirection == ubOpponentDir ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Already facing closest disturbance, face a random direction"), gLogDecideActionRed); ubOpponentDir = (UINT8) PreRandom( NUM_WORLD_DIRECTIONS ); } } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Closest disturbance out of bounds, face a random direction"), gLogDecideActionRed); ubOpponentDir = (UINT8) PreRandom( NUM_WORLD_DIRECTIONS ); } @@ -4650,6 +4821,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if ( gfTurnBasedAI ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Ending turn to limit facing changes"), gLogDecideActionRed); pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; } else @@ -4659,12 +4831,14 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } } + DebugAI(AI_MSG_INFO, pSoldier, String("Turn towards closest disturbance, direction %d", pSoldier->aiData.usActionData), gLogDecideActionRed); return(AI_ACTION_CHANGE_FACING); } } } // that's it for tanks + DebugAI(AI_MSG_INFO, pSoldier, String("Do nothing"), gLogDecideActionRed); return( AI_ACTION_NONE ); } @@ -4692,6 +4866,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) CountFriendsBlack(pSoldier) == 0 ) { // abort! abort! + DebugAI(AI_MSG_INFO, pSoldier, String("Unsafe location, do nothing"), gLogDecideActionRed); pSoldier->aiData.bAction = AI_ACTION_NONE; } @@ -4748,6 +4923,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // if not in water and not already crouched, try to crouch down first if (!fCivilian && !bInWater && (gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND) && IsValidStance( pSoldier, ANIM_CROUCH ) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Crouch]"), gLogDecideActionRed); //sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL); //if ( ( !TileIsOutOfBounds(sClosestOpponent) && PythSpacesAway( pSoldier->sGridNo, sClosestOpponent ) < (MaxNormalDistanceVisible() * 3) / 2 ) || PreRandom( 4 ) == 0 ) @@ -4768,22 +4944,24 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) // determine direction from this soldier to the closest opponent ubOpponentDir = GetDirectionFromCenterCellXYGridNo(pSoldier->sGridNo, sClosestOpponent); - if (!WeaponReady(pSoldier) && - pSoldier->ubDirection == ubOpponentDir && - PickSoldierReadyAnimation(pSoldier, FALSE, FALSE) != INVALID_ANIMATION) + if (!WeaponReady(pSoldier) && + pSoldier->ubDirection == ubOpponentDir && + PickSoldierReadyAnimation(pSoldier, FALSE, FALSE) != INVALID_ANIMATION) + { + if (IsScoped(&pSoldier->inv[HANDPOS])) { - if (IsScoped(&pSoldier->inv[HANDPOS])) + if ( Random(100) < 40 ) { - if ( Random(100) < 40 ) - { - pSoldier->aiData.bNextAction = AI_ACTION_RAISE_GUN; - } + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun, scoped weapon"), gLogDecideActionRed); + pSoldier->aiData.bNextAction = AI_ACTION_RAISE_GUN; } } } - //////////////////////////////////////////////////////////////////////////// + } + //////////////////////////////////////////////////////////////////////////// + DebugAI(AI_MSG_INFO, pSoldier, String("Change stance to crouch"), gLogDecideActionRed); pSoldier->aiData.usActionData = ANIM_CROUCH; return(AI_ACTION_CHANGE_STANCE); } @@ -4796,6 +4974,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if ( !fCivilian && pSoldier->aiData.bUnderFire && pSoldier->bActionPoints >= (pSoldier->bInitialActionPoints - GetAPsToLook( pSoldier ) ) && IsValidStance( pSoldier, ANIM_PRONE ) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Under fire, go prone]"), gLogDecideActionRed); sClosestDisturbance = MostImportantNoiseHeard( pSoldier, NULL, NULL, NULL ); if (!TileIsOutOfBounds(sClosestDisturbance)) @@ -4805,6 +4984,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if ( !gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Face direction %d", ubOpponentDir), gLogDecideActionRed); pSoldier->aiData.usActionData = ubOpponentDir; return( AI_ACTION_CHANGE_FACING ); } @@ -4812,6 +4992,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) else if ( (!gfTurnBasedAI || GetAPsToChangeStance( pSoldier, ANIM_PRONE ) <= pSoldier->bActionPoints ) && pSoldier->InternalIsValidStance( ubOpponentDir, ANIM_PRONE ) ) { // go prone, end turn + DebugAI(AI_MSG_INFO, pSoldier, String("Go prone & end turn"), gLogDecideActionRed); pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; pSoldier->aiData.usActionData = ANIM_PRONE; return( AI_ACTION_CHANGE_STANCE ); @@ -4825,6 +5006,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) //////////////////////////////////////////////////////////////////////////// if ( pSoldier->aiData.bOrders == SNIPER ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Sniper]"), gLogDecideActionRed); if ( pSoldier->sniper == 0 ) { DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionRed: sniper raising gun...")); @@ -4833,6 +5015,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) if (!WeaponReady(pSoldier) && PickSoldierReadyAnimation(pSoldier, FALSE, FALSE) != INVALID_ANIMATION) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun, sniper"), gLogDecideActionRed); pSoldier->sniper = 1; return AI_ACTION_RAISE_GUN; } @@ -4840,6 +5023,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) } else { + DebugAI(AI_MSG_INFO, pSoldier, String("Switch to yellow state"), gLogDecideActionRed); pSoldier->sniper = 0; return(DecideActionYellow(pSoldier)); } @@ -4858,6 +5042,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) { if ( Random(100) < 35 ) { + DebugAI(AI_MSG_INFO, pSoldier, String("Raise gun"), gLogDecideActionRed); return( AI_ACTION_RAISE_GUN ); } } @@ -4874,6 +5059,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier) #ifdef DEBUGDECISIONS AINameMessage(pSoldier,"- DOES NOTHING (RED)",1000); #endif + DebugAI(AI_MSG_INFO, pSoldier, String("Do nothing"), gLogDecideActionRed); pSoldier->aiData.usActionData = NOWHERE; return(AI_ACTION_NONE); @@ -4886,11 +5072,9 @@ BOOLEAN SoldierCondFalse(SOLDIERTYPE *pSoldier) { return FALSE; } INT8 DecideActionBlack(SOLDIERTYPE *pSoldier) { INT32 iCoverPercentBetter, iOffense, iDefense, iChance; - INT32 sClosestOpponent = NOWHERE,sBestCover = NOWHERE;//dnl ch58 160813 - INT32 sClosestDisturbance; -INT16 ubMinAPCost; - UINT8 ubCanMove; - INT8 bInWater,bInDeepWater,bInGas; + INT32 sBestCover = NOWHERE;//dnl ch58 160813 + INT32 sClosestDisturbance; + INT16 ubMinAPCost; INT8 bDirection; UINT8 ubBestAttackAction = AI_ACTION_NONE; INT8 bCanAttack,bActionReturned; @@ -4906,15 +5090,19 @@ INT16 ubMinAPCost; ATTACKTYPE BestShot, BestThrow, BestStab ,BestAttack;//dnl ch69 150913 BOOLEAN fCivilian = (PTR_CIVILIAN && (pSoldier->ubCivilianGroup == NON_CIV_GROUP || pSoldier->aiData.bNeutral || (pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) ); - BOOLEAN fClimb; INT16 ubBurstAPs; UINT8 ubOpponentDir; - INT32 sCheckGridNo; + INT32 sCheckGridNo; BOOLEAN fAllowCoverCheck = FALSE; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack"); + INT32 sOpponentGridNo; + INT8 bOpponentLevel; + INT32 sClosestOpponent = ClosestKnownOpponent(pSoldier, &sOpponentGridNo, &bOpponentLevel); + DebugAI(AI_MSG_INFO, pSoldier, String("sClosestOpponent %d", sClosestOpponent)); + // sevenfm: disable stealth mode pSoldier->bStealthMode = FALSE; // disable reverse movement mode @@ -4929,14 +5117,15 @@ INT16 ubMinAPCost; } // if we have absolutely no action points, we can't do a thing under BLACK! - if (!pSoldier->bActionPoints) + if (pSoldier->bActionPoints <= 0) { pSoldier->aiData.usActionData = NOWHERE; + pSoldier->aiData.bNextAction = AI_ACTION_END_TURN; return(AI_ACTION_NONE); } // can this guy move to any of the neighbouring squares ? (sets TRUE/FALSE) - ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier)); + UINT8 ubCanMove = (pSoldier->bActionPoints >= MinPtsToMove(pSoldier)); if( pSoldier->flags.uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) { @@ -5005,7 +5194,8 @@ INT16 ubMinAPCost; } } - if ( pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) + INT8 bInWater, bInDeepWater, bInGas; + if (BOXER(pSoldier) ) { if ( gTacticalStatus.bBoxingState == PRE_BOXING ) { @@ -5034,34 +5224,14 @@ INT16 ubMinAPCost; bInWater = Water( pSoldier->sGridNo, pSoldier->pathing.bLevel ); bInDeepWater = WaterTooDeepForAttacks( pSoldier->sGridNo, pSoldier->pathing.bLevel ); - // check if standing in tear gas without a gas mask on - bInGas = InGasOrSmoke( pSoldier, pSoldier->sGridNo ); - - // Flugente: tanks do not care about gas - if ( ARMED_VEHICLE( pSoldier ) || ENEMYROBOT( pSoldier ) ) - { - bInGas = FALSE; - } - // calculate our morale pSoldier->aiData.bAIMorale = CalcMorale(pSoldier); //////////////////////////////////////////////////////////////////////////// // WHEN LEFT IN GAS, WEAR GAS MASK IF AVAILABLE AND NOT WORN //////////////////////////////////////////////////////////////////////////// + bInGas = DecideActionWearGasmask(pSoldier); - if ( !bInGas && (gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y) ) - { - // only chance if we happen to be caught with our gas mask off - if ( PreRandom( 10 ) == 0 && WearGasMaskIfAvailable( pSoldier ) ) - { - bInGas = FALSE; - } - } - - //Only put mask on in gas - if(bInGas && WearGasMaskIfAvailable(pSoldier))//dnl ch40 200909 - bInGas = InGasOrSmoke(pSoldier, pSoldier->sGridNo); //////////////////////////////////////////////////////////////////////////// // IF GASSED, OR REALLY TIRED (ON THE VERGE OF COLLAPSING), TRY TO RUN AWAY @@ -5083,6 +5253,7 @@ INT16 ubMinAPCost; AIPopMessage(tempstr); #endif + DebugAI(AI_MSG_INFO, pSoldier, String("Gassed or low on breath, run away to grid %d", pSoldier->aiData.usActionData)); return(AI_ACTION_RUN_AWAY); } } @@ -5104,58 +5275,10 @@ INT16 ubMinAPCost; //////////////////////////////////////////////////////////////////////////// // STUCK IN WATER OR GAS, NO COVER, GO TO NEAREST SPOT OF UNGASSED LAND //////////////////////////////////////////////////////////////////////////// - - // when in deep water, move to closest opponent - if (ubCanMove && bInDeepWater && !pSoldier->aiData.bNeutral && pSoldier->aiData.bOrders == SEEKENEMY) - { - // find closest reachable opponent, excluding opponents in deep water - pSoldier->aiData.usActionData = ClosestReachableDisturbance(pSoldier, &fClimb); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { - return(AI_ACTION_LEAVE_WATER_GAS); - } - } - - // if soldier in water/gas has enough APs left to move at least 1 square - if (ubCanMove && (bInGas || bInDeepWater || FindBombNearby(pSoldier, pSoldier->sGridNo, BOMB_DETECTION_RANGE) || RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel))) + auto decision = DecideActionStuckInWaterOrGas(pSoldier, ubCanMove, bInWater, bInDeepWater, bInGas); + if (decision != AI_ACTION_LAST) { - pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { -#ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - SEEKING NEAREST UNGASSED LAND at grid %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); -#endif - - return(AI_ACTION_LEAVE_WATER_GAS); - } - - // couldn't find ANY land within 25 tiles(!), this should never happen... - - // look for best place to RUN AWAY to (farthest from the closest threat) - pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier); - - if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) - { -#ifdef DEBUGDECISIONS - sprintf(tempstr,"%s - NO LAND NEAR, RUNNING AWAY to grid %d",pSoldier->name,pSoldier->aiData.usActionData); - AIPopMessage(tempstr); -#endif - - return(AI_ACTION_RUN_AWAY); - } - - // GIVE UP ON LIFE! MERCS MUST HAVE JUST CORNERED A HELPLESS ENEMY IN A - // GAS FILLED ROOM (OR IN WATER MORE THAN 25 TILES FROM NEAREST LAND...) - if ( bInGas && gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) - { - pSoldier->bBreath = pSoldier->bBreathMax; - pSoldier->aiData.bAIMorale = MORALE_FEARLESS; // Can't move, can't get away, go nuts instead... - } - else - pSoldier->aiData.bAIMorale = MORALE_HOPELESS; + return decision; } // offer surrender? @@ -5177,11 +5300,11 @@ INT16 ubMinAPCost; //////////////////////////////////////////////////////////////////////////// // NPCs in water/tear gas without masks are not permitted to shoot/stab/throw - if ((pSoldier->bActionPoints < 2) || bInDeepWater || bInGas || pSoldier->aiData.bRTPCombat == RTP_COMBAT_REFRAIN) + if ((pSoldier->bActionPoints < 2) || bInDeepWater || bInGas) { bCanAttack = FALSE; } - else if (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) + else if (BOXER(pSoldier)) { bCanAttack = TRUE; fTryPunching = TRUE; @@ -5195,7 +5318,7 @@ INT16 ubMinAPCost; { if (fCivilian) { - if ( ( bCanAttack == NOSHOOT_NOWEAPON) && !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && pSoldier->ubBodyType != COW && pSoldier->ubBodyType != CRIPPLECIV && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) ) + if ( ( bCanAttack == NOSHOOT_NOWEAPON) && !BOXER(pSoldier) && pSoldier->ubBodyType != COW && pSoldier->ubBodyType != CRIPPLECIV && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) ) { // cower in fear!! if ( pSoldier->flags.uiStatusFlags & SOLDIER_COWERING ) @@ -5288,7 +5411,9 @@ INT16 ubMinAPCost; } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Self smoke when under fire]")); + //////////////////////////////////////////////////////////////////////////// + // THROW A SMOKE GRENADE FOR COVER + //////////////////////////////////////////////////////////////////////////// if (SoldierAI(pSoldier) && gfTurnBasedAI && pSoldier->bActionPoints == pSoldier->bInitialActionPoints && @@ -5342,6 +5467,11 @@ INT16 ubMinAPCost; } } + + //////////////////////////////////////////////////////////////////////////// + // LOOK FOR A WEAPON + //////////////////////////////////////////////////////////////////////////// + // if we don't have a gun, look around for a weapon! if (FindAIUsableObjClass( pSoldier, IC_GUN ) == ITEM_NOT_FOUND && ubCanMove && !pSoldier->aiData.bNeutral) { @@ -5353,8 +5483,10 @@ INT16 ubMinAPCost; } } - // Flugente: trait skills - // if we are a radio operator + + //////////////////////////////////////////////////////////////////////////// + // RADIO OPERATOR TRAIT + //////////////////////////////////////////////////////////////////////////// if ( HAS_SKILL_TRAIT( pSoldier, RADIO_OPERATOR_NT ) > 0 && pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY, TRUE) ) { // check: would it be possible to call in artillery from neighbouring sectors? @@ -5412,6 +5544,11 @@ INT16 ubMinAPCost; } } + + + //////////////////////////////////////////////////////////////////////////// + // VIP RETREAT + //////////////////////////////////////////////////////////////////////////// // VIPs run away (but not the GENERAL) if ( pSoldier->usSoldierFlagMask & SOLDIER_VIP && pSoldier->ubProfile != GENERAL ) { @@ -5427,10 +5564,15 @@ INT16 ubMinAPCost; if ( !TileIsOutOfBounds( pSoldier->aiData.usActionData ) ) { + DebugAI(AI_MSG_INFO, pSoldier, String("[VIP Retreat] grid# %d", pSoldier->aiData.usActionData)); return AI_ACTION_RUN_AWAY; } } + + //////////////////////////////////////////////////////////////////////////// + // DETERMINE BEST ATTACK + //////////////////////////////////////////////////////////////////////////// BestShot.ubPossible = FALSE; // by default, assume Shooting isn't possible BestThrow.ubPossible = FALSE; // by default, assume Throwing isn't possible BestStab.ubPossible = FALSE; // by default, assume Stabbing isn't possible @@ -5476,7 +5618,8 @@ INT16 ubMinAPCost; (pSoldier->aiData.bAttitude != AGGRESSIVE || Chance((100 - BestShot.ubChanceToReallyHit) / 2))) { // get the location of the closest CONSCIOUS reachable opponent - sClosestDisturbance = ClosestReachableDisturbance(pSoldier, &fClimb); + BOOLEAN fClimbDummy; + sClosestDisturbance = ClosestReachableDisturbance(pSoldier, &fClimbDummy); // if we found one if (!TileIsOutOfBounds(sClosestDisturbance)) @@ -5663,7 +5806,7 @@ INT16 ubMinAPCost; if (BestStab.ubPossible) { - if (!(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER)) + if (!BOXER(pSoldier)) { // if we have not enough APs to deal at least two or three punches, // reduce the attack value as one punch ain't much @@ -5760,7 +5903,7 @@ INT16 ubMinAPCost; // cautious boxer approach, reserve AP for two attacks (only if not attacking from the back) if (BestStab.ubPossible && - (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pSoldier) && SpacesAway(pSoldier->sGridNo, BestStab.sTarget) > 2 && BestStab.ubOpponent != NOBODY && MercPtrs[BestStab.ubOpponent] && @@ -5777,7 +5920,7 @@ INT16 ubMinAPCost; // try to avoid frontal attack if (BestStab.ubPossible && - (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) && + BOXER(pSoldier) && SpacesAway(pSoldier->sGridNo, BestStab.sTarget) > 1 && BestStab.ubOpponent != NOBODY && MercPtrs[BestStab.ubOpponent] && @@ -5937,7 +6080,10 @@ INT16 ubMinAPCost; UINT16 usRange = BestAttack.bWeaponIn==NO_SLOT ? 0 : GetModifiedGunRange(pSoldier->inv[BestAttack.bWeaponIn].usItem);//dnl ch69 150913 INT32 sClosestThreat = ClosestKnownOpponent(pSoldier, NULL, NULL); - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Black Retreat]")); + + ////////////////////////////////////////////////////////////////////////// + // STATUS BLACK RETREAT + ////////////////////////////////////////////////////////////////////////// if (gfTurnBasedAI && !bInWater && ubCanMove && @@ -5950,6 +6096,7 @@ INT16 ubMinAPCost; (ubBestAttackAction == AI_ACTION_NONE || ubBestAttackAction == AI_ACTION_FIRE_GUN && (UINT8)BestAttack.ubChanceToReallyHit < Random(10 + pSoldier->ShockLevelPercent() / 4)) && (pSoldier->CheckInitialAP() || !AnyCoverAtSpot(pSoldier, pSoldier->sGridNo) || pSoldier->aiData.bUnderFire)) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Black Retreat]")); DebugAI(AI_MSG_TOPIC, pSoldier, String("search for retreat spot")); INT32 sRetreatSpot = FindRetreatSpot(pSoldier); @@ -5964,11 +6111,13 @@ INT16 ubMinAPCost; } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Black cover advance]")); - // Black cover advance + + ////////////////////////////////////////////////////////////////////////// + // STATUS BLACK ADVANCE TO COVER + ////////////////////////////////////////////////////////////////////////// if (SoldierAI(pSoldier) && gfTurnBasedAI && - !pSoldier->bActionPoints == pSoldier->bInitialActionPoints && + //!pSoldier->bActionPoints == pSoldier->bInitialActionPoints && pSoldier->bInitialActionPoints > APBPConstants[AP_MINIMUM] && !gfHiddenInterrupt && !gTacticalStatus.fInterruptOccurred && @@ -5992,9 +6141,10 @@ INT16 ubMinAPCost; ubBestAttackAction == AI_ACTION_FIRE_GUN && BestAttack.ubChanceToReallyHit == 1 || !AnyCoverAtSpot(pSoldier, pSoldier->sGridNo))) { - DebugAI(AI_MSG_INFO, pSoldier, String("find cover advance spot")); + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Black cover advance]")); - INT32 sClosestDisturbance = ClosestReachableDisturbance(pSoldier, &fClimb); + BOOLEAN fClimbDummy; + INT32 sClosestDisturbance = ClosestReachableDisturbance(pSoldier, &fClimbDummy); if (!TileIsOutOfBounds(sClosestDisturbance)) { @@ -6011,7 +6161,8 @@ INT16 ubMinAPCost; // check that we can reach desired location pSoldier->aiData.usActionData = InternalGoAsFarAsPossibleTowards(pSoldier, sAdvanceSpot, 0, AI_ACTION_GET_CLOSER, 0); - if (pSoldier->aiData.usActionData == sAdvanceSpot) + //if (pSoldier->aiData.usActionData == sAdvanceSpot) + if (pSoldier->aiData.usActionData != NOWHERE) { DebugAI(AI_MSG_INFO, pSoldier, String("cover advance spot ok")); pSoldier->aiData.usActionData = sAdvanceSpot; @@ -6038,7 +6189,6 @@ INT16 ubMinAPCost; // check path to closest disturbance if (gfTurnBasedAI && pSoldier->bActionPoints >= APBPConstants[AP_MINIMUM] && - pSoldier->bActionPoints == pSoldier->bInitialActionPoints && !TileIsOutOfBounds(sClosestDisturbance) && RangeChangeDesire(pSoldier) > 3 && !AICheckIsSniper(pSoldier) && @@ -6121,8 +6271,11 @@ INT16 ubMinAPCost; } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Allow taking cover]")); - if ( (pSoldier->bActionPoints == pSoldier->bInitialActionPoints) && + + //////////////////////////////////////////////////////////////////////////// + // POSSIBLY FORGET THE ATTACK AND TAKE COVER + //////////////////////////////////////////////////////////////////////////// + if ( //(pSoldier->bActionPoints == pSoldier->bInitialActionPoints) && (ubBestAttackAction == AI_ACTION_FIRE_GUN) && (pSoldier->aiData.bShock == 0) && (pSoldier->stats.bLife >= pSoldier->stats.bLifeMax / 2) && @@ -6130,9 +6283,9 @@ INT16 ubMinAPCost; (PythSpacesAway( pSoldier->sGridNo, BestAttack.sTarget ) > usRange / CELL_X_SIZE ) && (RangeChangeDesire( pSoldier ) >= 4) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Allow taking cover]")); // okay, really got to wonder about this... could taking cover be an option? - if (ubCanMove && pSoldier->aiData.bOrders != STATIONARY && !gfHiddenInterrupt && - !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) + if (ubCanMove && pSoldier->aiData.bOrders != STATIONARY && !gfHiddenInterrupt && !BOXER(pSoldier) ) { // make militia a bit more cautious // 3 (UINT16) CONVERSIONS HERE TO AVOID ERRORS. GOTTHARD 7/15/08 @@ -6155,11 +6308,9 @@ INT16 ubMinAPCost; } } } - } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"LOOK FOR SOME KIND OF COVER BETTER THAN WHAT WE HAVE NOW"); - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Find cover]")); //////////////////////////////////////////////////////////////////////////// // LOOK FOR SOME KIND OF COVER BETTER THAN WHAT WE HAVE NOW //////////////////////////////////////////////////////////////////////////// @@ -6171,9 +6322,10 @@ INT16 ubMinAPCost; if ( (ubCanMove && !SkipCoverCheck && !gfHiddenInterrupt && ((ubBestAttackAction == AI_ACTION_NONE) || pSoldier->aiData.bLastAttackHit) && (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.fAIFlags & AI_RTP_OPTION_CAN_SEEK_COVER) && - !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) + !BOXER(pSoldier) ) || fAllowCoverCheck ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Find cover]")); // sevenfm: if not found yet if(TileIsOutOfBounds(sBestCover)) { @@ -6188,7 +6340,6 @@ INT16 ubMinAPCost; #endif DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideActionBlack: DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER)"); - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Decide attack/cover]")); ////////////////////////////////////////////////////////////////////////// // IF NECESSARY, DECIDE BETWEEN ATTACKING AND DEFENDING (TAKING COVER) ////////////////////////////////////////////////////////////////////////// @@ -6196,6 +6347,7 @@ INT16 ubMinAPCost; // if both are possible if ((ubBestAttackAction != AI_ACTION_NONE) && ( !TileIsOutOfBounds(sBestCover))) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Decide attack/cover]")); // gotta compare their merits and select the more desirable option iOffense = BestAttack.ubChanceToReallyHit; iDefense = iCoverPercentBetter; @@ -6264,12 +6416,16 @@ INT16 ubMinAPCost; } } + + ////////////////////////////////////////////////////////////////////////// + // PREPARE ATTACK + ////////////////////////////////////////////////////////////////////////// DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: is attack still desirable? ubBestAttackAction = %d",ubBestAttackAction)); - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Attack]")); // if attack is still desirable (meaning it's also preferred to taking cover) if (ubBestAttackAction != AI_ACTION_NONE) { + //DebugAI(AI_MSG_TOPIC, pSoldier, String("[Attack]")); DebugAI(AI_MSG_TOPIC, pSoldier, String("[Prepare attack]")); // if we wanted to be REALLY mean, we could look at chance to hit and decide whether // to shoot at the head... @@ -6294,7 +6450,7 @@ INT16 ubMinAPCost; if (IsGunBurstCapable( &pSoldier->inv[BestAttack.bWeaponIn], FALSE, pSoldier ) && !(Menptr[BestShot.ubOpponent].stats.bLife < OKLIFE) && // don't burst at downed targets pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft > 1 && - (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.bRTPCombat == RTP_COMBAT_AGGRESSIVE) ) + pSoldier->bTeam != gbPlayerNum ) { DebugAI(AI_MSG_INFO, pSoldier, String("enough APs to burst, random chance of doing so")); @@ -6409,6 +6565,8 @@ INT16 ubMinAPCost; (!gGameExternalOptions.fAISafeSuppression || CheckSuppressionDirection(pSoldier, BestShot.sTarget, BestShot.bTargetLevel))) { pSoldier->aiData.bAimTime--; + if (pSoldier->aiData.bAimTime < 0) { pSoldier->aiData.bAimTime = 0; } + sActualAimAP = CalcAPCostForAiming(pSoldier, BestAttack.sTarget, (INT8)pSoldier->aiData.bAimTime); DebugAI(AI_MSG_INFO, pSoldier, String("reduce aim to %d, recalc autofire, aim AP %d", pSoldier->aiData.bAimTime, sActualAimAP)); goto L_NEWAIM; @@ -6539,7 +6697,18 @@ INT16 ubMinAPCost; 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; @@ -6688,6 +6857,7 @@ INT16 ubMinAPCost; { pSoldier->aiData.usActionData = BestAttack.sTarget; pSoldier->bTargetLevel = BestAttack.bTargetLevel; + DebugAI(AI_MSG_INFO, pSoldier, String("Fire weapon!")); return(AI_ACTION_FIRE_GUN); } } @@ -6729,14 +6899,17 @@ INT16 ubMinAPCost; } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[End of Tank AI]")); // end of tank AI if ( !gGameExternalOptions.fEnemyTanksCanMoveInTactical && ARMED_VEHICLE( pSoldier ) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[End of Tank AI]")); return( AI_ACTION_NONE ); } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Window jump]")); + + ////////////////////////////////////////////////////////////////////// + // CLIMB ROOF / JUMP THROUGH WINDOW + ////////////////////////////////////////////////////////////////////// // 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. @@ -6749,6 +6922,7 @@ INT16 ubMinAPCost; sClosestOpponent = ClosestSeenOpponentWithRoof(pSoldier, &targetGridNo, &targetbLevel); if ( !TileIsOutOfBounds(sClosestOpponent) && !TileIsOutOfBounds(targetGridNo) && SameBuilding( pSoldier->sGridNo, targetGridNo ) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Window jump]")); if ( targetbLevel == pSoldier->pathing.bLevel && targetbLevel == 0 ) { ////////////////////////////////////////////////////////////////////// @@ -6825,9 +6999,11 @@ INT16 ubMinAPCost; } } - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Make boxer close if possible]")); - // try to make boxer close if possible - if (pSoldier->flags.uiStatusFlags & SOLDIER_BOXER ) + + ////////////////////////////////////////////////////////////////////// + // BOXER CLOSE IN ON OPPONENT + ////////////////////////////////////////////////////////////////////// + if (BOXER(pSoldier)) { DebugAI(AI_MSG_TOPIC, pSoldier, String("[Make boxer close if possible]")); @@ -6972,13 +7148,13 @@ INT16 ubMinAPCost; return(AI_ACTION_NONE); } + //////////////////////////////////////////////////////////////////////////// // IF A LOCATION WITH BETTER COVER IS AVAILABLE & REACHABLE, GO FOR IT! //////////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Take cover]")); - if (!TileIsOutOfBounds(sBestCover)) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Take cover]")); #ifdef DEBUGDECISIONS STR tempstr=""; sprintf ( tempstr,"%s - TAKING COVER at gridno %d (%d%% better)\n", @@ -6996,15 +7172,16 @@ INT16 ubMinAPCost; } return(AI_ACTION_TAKE_COVER); } - + + //////////////////////////////////////////////////////////////////////////// // IF THINGS ARE REALLY HOPELESS, OR UNARMED, TRY TO RUN AWAY //////////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Run away]")); // if soldier has enough APs left to move at least 1 square's worth if ( ubCanMove && (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.fAIFlags & AI_RTP_OPTION_CAN_RETREAT) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Run away]")); if ((pSoldier->aiData.bAIMorale == MORALE_HOPELESS) || !bCanAttack) { // look for best place to RUN AWAY to (farthest from the closest threat) @@ -7023,11 +7200,11 @@ INT16 ubMinAPCost; } } + //////////////////////////////////////////////////////////////////////////// // IF SPOTTERS HAVE BEEN CALLED FOR, AND WE HAVE SOME NEW SIGHTINGS, RADIO! //////////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Radio sightings]")); // if we're a computer merc, and we have the action points remaining to RADIO // (we never want NPCs to choose to radio if they would have to wait a turn) // and we're not swimming in deep water, and somebody has called for spotters @@ -7036,6 +7213,7 @@ INT16 ubMinAPCost; (pSoldier->aiData.bOppCnt > 1) && !fCivilian && (gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) && !bInDeepWater) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Radio sightings]")); // base chance depends on how much new info we have to radio to the others iChance = 25 * WhatIKnowThatPublicDont(pSoldier,TRUE); // just count them @@ -7061,10 +7239,10 @@ INT16 ubMinAPCost; //////////////////////////////////////////////////////////////////////////// // CROUCH IF NOT CROUCHING ALREADY //////////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Crouch if not crouching already]")); // if not in water and not already crouched, try to crouch down first if (!gfTurnBasedAI || GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) <= pSoldier->bActionPoints) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Crouch if not crouching already]")); if ( !fCivilian && !gfHiddenInterrupt && IsValidStance( pSoldier, ANIM_CROUCH ) && ubBestAttackAction != AI_ACTION_KNIFE_MOVE && ubBestAttackAction != AI_ACTION_KNIFE_STAB && ubBestAttackAction != AI_ACTION_STEAL_MOVE) // SANDRO - if knife attack don't crouch { // determine the location of the known closest opponent @@ -7081,7 +7259,7 @@ INT16 ubMinAPCost; { // we might want to turn before lying down! if ( (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints - GetAPsToChangeStance( pSoldier, (INT8) pSoldier->aiData.usActionData )) && - (((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) && !AimingGun(pSoldier)) ) + ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) ) { // if we have a closest seen opponent if (!TileIsOutOfBounds(sClosestOpponent)) @@ -7126,16 +7304,17 @@ INT16 ubMinAPCost; } } + //////////////////////////////////////////////////////////////////////////// // TURN TO FACE CLOSEST KNOWN OPPONENT (IF NOT FACING THERE ALREADY) //////////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Turn to closest known opponent]")); if (!gfTurnBasedAI || GetAPsToLook( pSoldier ) <= pSoldier->bActionPoints) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Turn to closest known opponent]")); // hopeless guys shouldn't waste their time this way, UNLESS they CAN move // but chose not to to get this far (which probably means they're cornered) // ALSO, don't bother turning if we're already aiming a gun - if ( !gfHiddenInterrupt && ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) && !AimingGun(pSoldier)) + if ( !gfHiddenInterrupt && ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) ) { // determine the location of the known closest opponent // (don't care if he's conscious, don't care if he's reachable at all) @@ -7170,9 +7349,9 @@ INT16 ubMinAPCost; //////////////////////////////////////////////////////////////////////////// // RADIO RED ALERT: determine %chance to call others and report contact //////////////////////////////////////////////////////////////////////////// - DebugAI(AI_MSG_TOPIC, pSoldier, String("[Report contacts]")); if ( !(pSoldier->usSoldierFlagMask & SOLDIER_RAISED_REDALERT) && pSoldier->bTeam == MILITIA_TEAM && (pSoldier->bActionPoints >= APBPConstants[AP_RADIO]) && (gTacticalStatus.Team[pSoldier->bTeam].bMenInSector > 1) ) { + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Report contacts]")); // if there hasn't been an initial RED ALERT yet in this sector if ( !(gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition) || NeedToRadioAboutPanicTrigger() ) @@ -7266,7 +7445,6 @@ INT16 ubMinAPCost; // by default, if everything else fails, just stand in place and wait pSoldier->aiData.usActionData = NOWHERE; return(AI_ACTION_NONE); - } void DecideAlertStatus( SOLDIERTYPE *pSoldier ) @@ -9597,7 +9775,7 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier ) //////////////////////////////////////////////////////////////////////////// // NPCs in water/tear gas without masks are not permitted to shoot/stab/throw - if ( (pSoldier->bActionPoints < 2) || bInDeepWater || pSoldier->aiData.bRTPCombat == RTP_COMBAT_REFRAIN ) + if ( (pSoldier->bActionPoints < 2) || bInDeepWater ) { bCanAttack = FALSE; } @@ -9892,8 +10070,7 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier ) (RangeChangeDesire( pSoldier ) >= 4) ) { // okay, really got to wonder about this... could taking cover be an option? - if ( ubCanMove && pSoldier->aiData.bOrders != STATIONARY && !gfHiddenInterrupt && - !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER) ) + if ( ubCanMove && pSoldier->aiData.bOrders != STATIONARY && !gfHiddenInterrupt && !BOXER(pSoldier) ) { // make militia a bit more cautious // 3 (UINT16) CONVERSIONS HERE TO AVOID ERRORS. GOTTHARD 7/15/08 @@ -9924,9 +10101,8 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier ) if ( (ubCanMove && !SkipCoverCheck && !gfHiddenInterrupt && ((ubBestAttackAction == AI_ACTION_NONE) || pSoldier->aiData.bLastAttackHit) && - (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.fAIFlags & AI_RTP_OPTION_CAN_SEEK_COVER) && - !(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER)) - || fAllowCoverCheck ) + (pSoldier->bTeam != gbPlayerNum || pSoldier->aiData.fAIFlags & AI_RTP_OPTION_CAN_SEEK_COVER) && !BOXER(pSoldier)) || + fAllowCoverCheck ) { sBestCover = FindBestNearbyCover( pSoldier, pSoldier->aiData.bAIMorale, &iCoverPercentBetter ); } @@ -10292,7 +10468,7 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier ) // hopeless guys shouldn't waste their time this way, UNLESS they CAN move // but chose not to to get this far (which probably means they're cornered) // ALSO, don't bother turning if we're already aiming a gun - if ( !gfHiddenInterrupt && ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) && !AimingGun( pSoldier ) ) + if ( !gfHiddenInterrupt && ((pSoldier->aiData.bAIMorale > MORALE_HOPELESS) || ubCanMove) ) { // determine the location of the known closest opponent // (don't care if he's conscious, don't care if he's reachable at all) @@ -10340,21 +10516,21 @@ extern UINT32 guiTurnCnt; extern UINT32 guiReinforceTurn; extern UINT32 guiArrived; -void LogDecideInfo(SOLDIERTYPE *pSoldier) +void LogDecideInfo(SOLDIERTYPE *pSoldier, bool doLog) { - DebugAI(AI_MSG_INFO, pSoldier, String("Turn num %d aware %d", guiTurnCnt, gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition)); - DebugAI(AI_MSG_INFO, pSoldier, String("current team %d interrupt occurred %d", gTacticalStatus.ubCurrentTeam, gTacticalStatus.fInterruptOccurred)); - DebugAI(AI_MSG_INFO, pSoldier, String("AP=%d/%d %s %s %s %s %s", pSoldier->bActionPoints, pSoldier->bInitialActionPoints, gStr8AlertStatus[pSoldier->aiData.bAlertStatus], gStr8Orders[pSoldier->aiData.bOrders], gStr8Attitude[pSoldier->aiData.bAttitude], gStr8Team[pSoldier->bTeam], gStr8Class[pSoldier->ubSoldierClass])); - DebugAI(AI_MSG_INFO, pSoldier, String("Health %d/%d Breath %d/%d Shock %d Tolerance %d AI Morale %d Morale %d", pSoldier->stats.bLife, pSoldier->stats.bLifeMax, pSoldier->bBreath, pSoldier->bBreathMax, pSoldier->aiData.bShock, CalcSuppressionTolerance(pSoldier), pSoldier->aiData.bAIMorale, pSoldier->aiData.bMorale)); - DebugAI(AI_MSG_INFO, pSoldier, String("Spot %d level %d opponents %d", pSoldier->sGridNo, pSoldier->pathing.bLevel, pSoldier->aiData.bOppCnt)); - DebugAI(AI_MSG_INFO, pSoldier, String("ubServiceCount %d ubServicePartner %d fDoingSurgery %d", pSoldier->ubServiceCount, pSoldier->ubServicePartner, pSoldier->fDoingSurgery)); + DebugAI(AI_MSG_INFO, pSoldier, String("Turn num %d aware %d", guiTurnCnt, gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("current team %d interrupt occurred %d", gTacticalStatus.ubCurrentTeam, gTacticalStatus.fInterruptOccurred), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("AP=%d/%d %s %s %s %s %s", pSoldier->bActionPoints, pSoldier->bInitialActionPoints, gStr8AlertStatus[pSoldier->aiData.bAlertStatus], gStr8Orders[pSoldier->aiData.bOrders], gStr8Attitude[pSoldier->aiData.bAttitude], gStr8Team[pSoldier->bTeam], gStr8Class[pSoldier->ubSoldierClass]), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("Health %d/%d Breath %d/%d Shock %d Tolerance %d AI Morale %d Morale %d", pSoldier->stats.bLife, pSoldier->stats.bLifeMax, pSoldier->bBreath, pSoldier->bBreathMax, pSoldier->aiData.bShock, CalcSuppressionTolerance(pSoldier), pSoldier->aiData.bAIMorale, pSoldier->aiData.bMorale), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("Spot %d level %d opponents %d", pSoldier->sGridNo, pSoldier->pathing.bLevel, pSoldier->aiData.bOppCnt), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("ubServiceCount %d ubServicePartner %d fDoingSurgery %d", pSoldier->ubServiceCount, pSoldier->ubServicePartner, pSoldier->fDoingSurgery), doLog); if (pSoldier->IsCowering()) { - DebugAI(AI_MSG_INFO, pSoldier, String("Cowering")); + DebugAI(AI_MSG_INFO, pSoldier, String("Cowering"), doLog); } if (pSoldier->IsGivingAid()) { - DebugAI(AI_MSG_INFO, pSoldier, String("Giving aid")); + DebugAI(AI_MSG_INFO, pSoldier, String("Giving aid"), doLog); } //CHAR8 str8[1024]; @@ -10364,18 +10540,18 @@ void LogDecideInfo(SOLDIERTYPE *pSoldier) { if (!TileIsOutOfBounds(gsWatchedLoc[pSoldier->ubID][bLoop])) { - DebugAI(AI_MSG_INFO, pSoldier, String("Watched location %d level %d points %d", gsWatchedLoc[pSoldier->ubID][bLoop], gbWatchedLocLevel[pSoldier->ubID][bLoop], gubWatchedLocPoints[pSoldier->ubID][bLoop])); + DebugAI(AI_MSG_INFO, pSoldier, String("Watched location %d level %d points %d", gsWatchedLoc[pSoldier->ubID][bLoop], gbWatchedLocLevel[pSoldier->ubID][bLoop], gubWatchedLocPoints[pSoldier->ubID][bLoop]), doLog); } } - LogKnowledgeInfo(pSoldier); + LogKnowledgeInfo(pSoldier, doLog); - DebugAI(AI_MSG_INFO, pSoldier, String("What I know %d", WhatIKnowThatPublicDont(pSoldier, FALSE))); - DebugAI(AI_MSG_INFO, pSoldier, String("Has Gun %d, Short range weapon %d, Gun Range %d, Gun Ammo %d, Gun Scoped %d ", AICheckHasGun(pSoldier), AICheckShortWeaponRange(pSoldier), AIGunRange(pSoldier), AIGunAmmo(pSoldier), AIGunScoped(pSoldier))); - DebugAI(AI_MSG_INFO, pSoldier, String("RetreatCounter %d", pSoldier->RetreatCounterValue())); + DebugAI(AI_MSG_INFO, pSoldier, String("What I know %d", WhatIKnowThatPublicDont(pSoldier, FALSE)), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("Has Gun %d, Short range weapon %d, Gun Range %d, Gun Ammo %d, Gun Scoped %d ", AICheckHasGun(pSoldier), AICheckShortWeaponRange(pSoldier), AIGunRange(pSoldier), AIGunAmmo(pSoldier), AIGunScoped(pSoldier)), doLog); + DebugAI(AI_MSG_INFO, pSoldier, String("RetreatCounter %d", pSoldier->RetreatCounterValue()), doLog); } -void LogKnowledgeInfo(SOLDIERTYPE *pSoldier) +void LogKnowledgeInfo(SOLDIERTYPE *pSoldier, bool doLog) { //CHAR8 str8[1024]; //memset(str8, 0, 1024 * sizeof(char)); @@ -10388,7 +10564,7 @@ void LogKnowledgeInfo(SOLDIERTYPE *pSoldier) { //wcstombs(str8, MercPtrs[oppID]->GetName(), wcslen(MercPtrs[oppID]->GetName())+1); //wcstombs(str8, MercPtrs[oppID]->GetName(), 1024 - 1); - DebugAI(AI_MSG_INFO, pSoldier, String("public opponent [%d] knowledge %s gridno %d level %d", oppID, gStr8Knowledge[gbPublicOpplist[pSoldier->bTeam][oppID] - OLDEST_HEARD_VALUE], gsPublicLastKnownOppLoc[pSoldier->bTeam][oppID], gbPublicLastKnownOppLevel[pSoldier->bTeam][oppID])); + DebugAI(AI_MSG_INFO, pSoldier, String("public opponent [%d] knowledge %s gridno %d level %d", oppID, gStr8Knowledge[gbPublicOpplist[pSoldier->bTeam][oppID] - OLDEST_HEARD_VALUE], gsPublicLastKnownOppLoc[pSoldier->bTeam][oppID], gbPublicLastKnownOppLevel[pSoldier->bTeam][oppID]), doLog); //swprintf( pStrInfo, L"%s[%d] %s %s\n", pStrInfo, oppID, MercPtrs[oppID]->GetName(), SeenStr(gbPublicOpplist[pSoldier->bTeam][oppID]) ); } } @@ -10400,8 +10576,98 @@ void LogKnowledgeInfo(SOLDIERTYPE *pSoldier) { //wcstombs(str8, MercPtrs[oppID]->GetName(), wcslen(MercPtrs[oppID]->GetName())+1); //wcstombs(str8, MercPtrs[oppID]->GetName(), 1024 - 1); - DebugAI(AI_MSG_INFO, pSoldier, String("personal opponent [%d] knowledge %s gridno %d level %d", oppID, gStr8Knowledge[pSoldier->aiData.bOppList[oppID] - OLDEST_HEARD_VALUE], gsLastKnownOppLoc[pSoldier->ubID][oppID], gbLastKnownOppLevel[pSoldier->ubID][oppID])); + DebugAI(AI_MSG_INFO, pSoldier, String("personal opponent [%d] knowledge %s gridno %d level %d", oppID, gStr8Knowledge[pSoldier->aiData.bOppList[oppID] - OLDEST_HEARD_VALUE], gsLastKnownOppLoc[pSoldier->ubID][oppID], gbLastKnownOppLevel[pSoldier->ubID][oppID]), doLog); //swprintf( pStrInfo, L"%s[%d] %s %s\n", pStrInfo, oppID, MercPtrs[oppID]->GetName(), SeenStr(pSoldier->aiData.bOppList[oppID]) ); } } } + + +INT8 DecideActionWearGasmask(SOLDIERTYPE *pSoldier) +{ + // check if standing in tear gas without a gas mask on + INT8 bInGas = InGasOrSmoke(pSoldier, pSoldier->sGridNo); + + if (!bInGas && (gWorldSectorX == TIXA_SECTOR_X && gWorldSectorY == TIXA_SECTOR_Y)) + { + // only chance if we happen to be caught with our gas mask off + if (PreRandom(10) == 0 && WearGasMaskIfAvailable(pSoldier)) + { + bInGas = FALSE; + } + } + + //Only put mask on in gas + if (bInGas && WearGasMaskIfAvailable(pSoldier)) { bInGas = InGasOrSmoke(pSoldier, pSoldier->sGridNo); } + + return bInGas; +} + +ActionType DecideActionStuckInWaterOrGas(SOLDIERTYPE *pSoldier, BOOLEAN ubCanMove, BOOLEAN bInWater, BOOLEAN bInDeepWater, BOOLEAN bInGas) +{ + DebugAI(AI_MSG_TOPIC, pSoldier, String("[Decide action if stuck in water or gas]")); + + // when in deep water, move to closest opponent + if (ubCanMove && (bInDeepWater || bInWater) && !pSoldier->aiData.bNeutral && (pSoldier->aiData.bOrders == SEEKENEMY || pSoldier->aiData.bAction == AI_ACTION_SEEK_OPPONENT || pSoldier->aiData.bLastAction == AI_ACTION_SEEK_OPPONENT)) + { + // find closest reachable opponent, excluding opponents in deep water + BOOLEAN fClimbDummy; + pSoldier->aiData.usActionData = ClosestReachableDisturbance(pSoldier, &fClimbDummy); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Move out of water towards closest opponent")); + return(AI_ACTION_LEAVE_WATER_GAS); + } + } + + // if soldier in water/gas has enough APs left to move at least 1 square + if (ubCanMove && (bInGas || bInWater || bInDeepWater || FindBombNearby(pSoldier, pSoldier->sGridNo, BOMB_DETECTION_RANGE) || RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel))) + { + pSoldier->aiData.usActionData = FindNearestUngassedLand(pSoldier); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { +#ifdef DEBUGDECISIONS + sprintf(tempstr, "%s - SEEKING NEAREST UNGASSED LAND at grid %d", pSoldier->name, pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + DebugAI(AI_MSG_INFO, pSoldier, String("Leave for nearest (ungassed) land")); + return(AI_ACTION_LEAVE_WATER_GAS); + } + + // couldn't find ANY land within 25 tiles(!), this should never happen... + + // look for best place to RUN AWAY to (farthest from the closest threat) + pSoldier->aiData.usActionData = FindSpotMaxDistFromOpponents(pSoldier); + + if (!TileIsOutOfBounds(pSoldier->aiData.usActionData)) + { +#ifdef DEBUGDECISIONS + sprintf(tempstr, "%s - NO LAND NEAR, RUNNING AWAY to grid %d", pSoldier->name, pSoldier->aiData.usActionData); + AIPopMessage(tempstr); +#endif + + DebugAI(AI_MSG_INFO, pSoldier, String("NO LAND NEAR, RUNNING AWAY to grid %d", pSoldier->aiData.usActionData)); + return(AI_ACTION_RUN_AWAY); + } + + // GIVE UP ON LIFE! MERCS MUST HAVE JUST CORNERED A HELPLESS ENEMY IN A + // GAS FILLED ROOM (OR IN WATER MORE THAN 25 TILES FROM NEAREST LAND...) + if ((bInGas || bInWater || bInDeepWater) && gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE) + { + DebugAI(AI_MSG_INFO, pSoldier, String("Cornered! Go berserk!")); + pSoldier->bBreath = pSoldier->bBreathMax; + pSoldier->aiData.bAIMorale = MORALE_FEARLESS; // Can't move, can't get away, go nuts instead... + } + else + { + DebugAI(AI_MSG_INFO, pSoldier, String("Cornered! Give up on life..")); + pSoldier->aiData.bAIMorale = MORALE_HOPELESS; + } + } + + + return AI_ACTION_LAST; +} diff --git a/TacticalAI/FindLocations.cpp b/TacticalAI/FindLocations.cpp index 37fcd9846..02f1f5ab0 100644 --- a/TacticalAI/FindLocations.cpp +++ b/TacticalAI/FindLocations.cpp @@ -15,10 +15,7 @@ #include "Render Fun.h" #include "Boxing.h" #include "Text.h" - #ifdef _DEBUG - #include "renderworld.h" - #include "video.h" - #endif + #include "renderworld.h" #include "worldman.h" #include "strategicmap.h" #include "environment.h" @@ -27,6 +24,7 @@ #include "GameSettings.h" #include "Soldier Profile.h" #include "rotting corpses.h" // sevenfm +#include //////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -34,17 +32,7 @@ // were changed to GetAPsCrouch() and GetAPsProne() // - also all "APBPConstants[AP_PICKUP_ITEM]" were replaced by GetBasicAPsToPickupItem() //////////////////////////////////////////////////////////////////////////////////////////////////////// - - INT16 * gsCoverValue = NULL; -#ifdef _DEBUG - - INT16 gsBestCover; - #ifndef PATHAI_VISIBLE_DEBUG - // NB Change this to true to get visible cover debug -- CJC - BOOLEAN gfDisplayCoverValues = FALSE; - #endif - extern void RenderCoverDebug( void ); -#endif +INT16 gsBestCover; INT16 gubAIPathCosts[19][19]; @@ -612,8 +600,92 @@ UINT8 NumberOfTeamMatesAdjacent( SOLDIERTYPE * pSoldier, INT32 sGridNo ) return( ubCount ); } -INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentBetter) +static void CalculateCoverValue(SOLDIERTYPE* pSoldier, const INT32 sGridNo, const INT32 iPathCost, const INT32 iMyThreatValue, const UINT32 uiThreatCnt, const UINT8 ubDiff, const BOOLEAN fNight, const UINT8 ubBackgroundLightPercent, const INT32 morale, INT32 &iCoverValue, INT32& iCoverScale) +{ + iCoverValue = 0; + iCoverScale = 0; + + // sevenfm: check sight cover + BOOLEAN fProneCover = TRUE; + + // for every opponent that threatens, consider this spot's cover vs. him + for (UINT32 uiLoop = 0; uiLoop < uiThreatCnt; uiLoop++) + { + // calculate the range we would be at from this opponent + INT32 iThreatRange = Threat[uiLoop].iOrigRange; + if (sGridNo != pSoldier->sGridNo) + { + iThreatRange = GetRangeInCellCoordsFromGridNoDiff(sGridNo, Threat[uiLoop].sGridNo); + } + + // if this threat would be within 20 tiles, count it + if (iThreatRange <= MAX_THREAT_RANGE) + { + iCoverValue += CalcCoverValue( + pSoldier, sGridNo, iMyThreatValue, + (pSoldier->bActionPoints - iPathCost), + uiLoop, iThreatRange, morale, &iCoverScale + ); + } + + // sevenfm: sight test + if (gGameExternalOptions.fAIBetterCover) + { + if (LocationToLocationLineOfSightTest(Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, sGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS)) + //if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, sGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, -1 ) != 0 ) + { + fProneCover = FALSE; + } + } + + //sprintf(tempstr,"iCoverValue after opponent %d is now %d",iLoop,iCoverValue); + //PopMessage(tempstr); + } + + // reduce cover for each person adjacent to this gridno who is on our team, + // by 10% (so locations next to several people will be very much frowned upon + iCoverValue -= (abs(iCoverValue) / 5) * NumberOfTeamMatesAdjacent(pSoldier, sGridNo); + + if (gGameExternalOptions.fAIBetterCover) + { + // sevenfm: when defending (range change <= 3), prefer locations with sight cover + if (RangeChangeDesire(pSoldier) < 4) + { + if (fProneCover) { iCoverValue += abs(iCoverValue) / __max(2, 2 * RangeChangeDesire(pSoldier)); } + } + + // sevenfm: check for nearby friends, add bonus/penalty 10% + UINT8 ubNearbyFriends = __max(0, CountNearbyFriends(pSoldier, sGridNo, 7)); + iCoverValue -= ubNearbyFriends * abs(iCoverValue) / (6 - ubDiff); + + // sevenfm: penalize locations with fresh corpses + if (GetNearestRottingCorpseAIWarning(sGridNo) > 0) + { + iCoverValue -= abs(iCoverValue) / (6 - 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 + { + // reduce cover at nighttime based on how bright the light is at that location + // using the difference in sighting distance between the background and the + // light for this tile + //ubLightPercentDifference = (gbLightSighting[ 0 ][ LightTrueLevel( sGridNo, pSoldier->pathing.bLevel ) ] - ubBackgroundLightPercent ); + UINT8 ubLightPercentDifference = (gGameExternalOptions.ubBrightnessVisionMod[LightTrueLevel(sGridNo, pSoldier->pathing.bLevel)] - ubBackgroundLightPercent); + iCoverValue -= (abs(iCoverValue) / 100) * ubLightPercentDifference; + } +} + +INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentBetter, INT32 targetGridNo) { + if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL()) + { + ResetDebugInfoValues(); + } + DebugMsg(TOPIC_JA2AI,DBG_LEVEL_3,String("FindBestNearbyCover")); // all 32-bit integers for max. speed @@ -649,6 +721,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) { @@ -677,12 +754,6 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB iBestCoverValue = -1; -#if defined( _DEBUG ) && defined( COVER_DEBUG ) - if (gfDisplayCoverValues) - { - memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX ); - } -#endif //NameMessage(pSoldier,"looking for some cover..."); @@ -710,9 +781,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) @@ -764,85 +835,27 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB // calculate our current cover value in the place we are now, since the // cover we are searching for must be better than what we have now! - iCurrentCoverValue = 0; - iCurrentScale = 0; - - // sevenfm: sight cover - fProneCover = TRUE; + CalculateCoverValue(pSoldier, targetGridNo, 0, iMyThreatValue, uiThreatCnt, ubDiff, fNight, ubBackgroundLightPercent, morale, iCurrentCoverValue, iCurrentScale); - // for every opponent that threatens, consider this spot's cover vs. him - for (uiLoop = 0; uiLoop < uiThreatCnt; uiLoop++) + if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL()) { - // if this threat is CURRENTLY within 20 tiles - 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); - } - // 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 ) ) - { - fProneCover = FALSE; - } - } - //sprintf(tempstr,"iCurrentCoverValue after opponent %d is now %d",iLoop,iCurrentCoverValue); - //PopMessage(tempstr); + gRenderDebugInfoValues[targetGridNo] = (INT32)(iCurrentCoverValue / 100); } - // reduce cover for each person adjacent to this gridno who is on our team, - // by 10% (so locations next to several people will be very much frowned upon - if ( iCurrentCoverValue >= 0 ) - { - iCurrentCoverValue -= (iCurrentCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, pSoldier->sGridNo ); - } - else - { - // when negative, must add a negative to decrease the total - iCurrentCoverValue += (iCurrentCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, pSoldier->sGridNo ); - } - - if( gGameExternalOptions.fAIBetterCover ) - { - // sevenfm: when defending (range change <= 3), prefer locations with sight cover - if( RangeChangeDesire(pSoldier) < 4 ) - { - if( fProneCover ) - { - iCurrentCoverValue += abs(iCurrentCoverValue) / __max(2, 2*RangeChangeDesire(pSoldier)); - } - } - - // sevenfm: check for nearby friends, add bonus/penalty - ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, pSoldier->sGridNo, 5 )); - iCurrentCoverValue -= ubNearbyFriends * abs(iCurrentCoverValue) / (10-ubDiff); - - // sevenfm: penalize locations with fresh corpses - if(GetNearestRottingCorpseAIWarning( pSoldier->sGridNo ) > 0) - { - iCurrentCoverValue -= abs(iCurrentCoverValue) / (8-ubDiff); - } - - // sevenfm: penalize locations near red smoke - iCurrentCoverValue -= abs(iCurrentCoverValue) * RedSmokeDanger(pSoldier->sGridNo, pSoldier->pathing.bLevel) / 100; - } - #ifdef DEBUGCOVER // AINumMessage("Search Range = ",iSearchRange); #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 +865,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 +912,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 +925,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 +935,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 +992,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 +1001,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; @@ -1026,92 +1039,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB // OK, this place shows potential. How useful is it as cover? // EVALUATE EACH GRID #, remembering the BEST PROTECTED ONE - iCoverValue = 0; - iCoverScale = 0; - - // sevenfm: check sight cover - fProneCover = TRUE; - - // for every opponent that threatens, consider this spot's cover vs. him - for (uiLoop = 0; uiLoop < uiThreatCnt; uiLoop++) - { - // calculate the range we would be at from this opponent - iThreatRange = GetRangeInCellCoordsFromGridNoDiff( sGridNo, Threat[uiLoop].sGridNo ); - // if this threat would be within 20 tiles, count it - if (iThreatRange <= MAX_THREAT_RANGE) - { - iCoverValue += CalcCoverValue(pSoldier,sGridNo,iMyThreatValue, - (pSoldier->bActionPoints - iPathCost), - uiLoop,iThreatRange,morale,&iCoverScale); - } - - // sevenfm: sight test - if( gGameExternalOptions.fAIBetterCover ) - { - if ( LocationToLocationLineOfSightTest( Threat[uiLoop].sGridNo, Threat[uiLoop].pOpponent->pathing.bLevel, sGridNo, pSoldier->pathing.bLevel, TRUE, MAX_VISION_RANGE, STANDING_LOS_POS, PRONE_LOS_POS ) ) - //if ( SoldierToVirtualSoldierLineOfSightTest( Threat[uiLoop].pOpponent, sGridNo, pSoldier->pathing.bLevel, ANIM_PRONE, TRUE, -1 ) != 0 ) - { - fProneCover = FALSE; - } - } - - //sprintf(tempstr,"iCoverValue after opponent %d is now %d",iLoop,iCoverValue); - //PopMessage(tempstr); - } - - // reduce cover for each person adjacent to this gridno who is on our team, - // by 10% (so locations next to several people will be very much frowned upon - if ( iCoverValue >= 0 ) - { - iCoverValue -= (iCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, sGridNo ); - } - else - { - // when negative, must add a negative to decrease the total - iCoverValue += (iCoverValue / 10) * NumberOfTeamMatesAdjacent( pSoldier, sGridNo ); - } - - if( gGameExternalOptions.fAIBetterCover ) - { - // sevenfm: when defending (range change <= 3), prefer locations with sight cover - if( RangeChangeDesire(pSoldier) < 4 ) - { - if( fProneCover ) - iCoverValue += abs(iCoverValue) / __max(2, 2*RangeChangeDesire(pSoldier)); - } - - // sevenfm: check for nearby friends in 10 radius, add bonus/penalty 10% - ubNearbyFriends = __min(5, CountNearbyFriends( pSoldier, sGridNo, 5 )); - iCoverValue -= ubNearbyFriends * abs(iCoverValue) / (10-ubDiff); - - // sevenfm: penalize locations with fresh corpses - if(GetNearestRottingCorpseAIWarning( sGridNo ) > 0) - { - 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 - { - // reduce cover at nighttime based on how bright the light is at that location - // using the difference in sighting distance between the background and the - // light for this tile - //ubLightPercentDifference = (gbLightSighting[ 0 ][ LightTrueLevel( sGridNo, pSoldier->pathing.bLevel ) ] - ubBackgroundLightPercent ); - ubLightPercentDifference = (gGameExternalOptions.ubBrightnessVisionMod[ LightTrueLevel( sGridNo, pSoldier->pathing.bLevel ) ] - ubBackgroundLightPercent ); - - if ( iCoverValue >= 0 ) - { - iCoverValue -= (iCoverValue / 100) * ubLightPercentDifference; - } - else - { - iCoverValue += (iCoverValue / 100) * ubLightPercentDifference; - } - } - + CalculateCoverValue(pSoldier, sGridNo, iPathCost, iMyThreatValue, uiThreatCnt, ubDiff, fNight, ubBackgroundLightPercent, morale, iCoverValue, iCoverScale); #ifdef DEBUGCOVER // if there ARE multiple opponents @@ -1121,12 +1049,10 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB } #endif -#if defined( _DEBUG ) && defined( COVER_DEBUG ) - if (gfDisplayCoverValues) + if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL()) { - gsCoverValue[sGridNo] = (INT16) (iCoverValue / 100); + gRenderDebugInfoValues[sGridNo] = (INT32) (iCoverValue / 100); } -#endif // if this is better than the best place found so far @@ -1163,34 +1089,15 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB gubNPCAPBudget = 0; gubNPCDistLimit = 0; - #if defined( _DEBUG ) && !defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues) + if (gRenderDebugInfoMode == DEBUG_COVERVALUE && DEBUG_CHEAT_LEVEL()) { - // do a locate? - LocateSoldier( pSoldier->ubID, SETLOCATORFAST ); gsBestCover = sBestCover; - SetRenderFlags( RENDER_FLAG_FULL ); - RenderWorld(); - RenderCoverDebug( ); - InvalidateScreen( ); - EndFrameBufferRender(); - RefreshScreen( NULL ); - /* - iLoop = GetJA2Clock(); - do - { - - } while( ( GetJA2Clock( ) - iLoop ) < 2000 ); - */ + InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y); } - #endif // if a better cover location was found if (!TileIsOutOfBounds(sBestCover)) { - #if defined( _DEBUG ) && !defined( PATHAI_VISIBLE_DEBUG ) - gsBestCover = sBestCover; - #endif // cover values already take the AP cost of getting there into account in // a BIG way, so no need to worry about that here, even small improvements // are actually very significant once we get our APs back (if we live!) diff --git a/TacticalAI/Movement.cpp b/TacticalAI/Movement.cpp index ec5daaaf2..3d9c7e1cc 100644 --- a/TacticalAI/Movement.cpp +++ b/TacticalAI/Movement.cpp @@ -432,7 +432,7 @@ INT8 RandomPointPatrolAI(SOLDIERTYPE *pSoldier) INT32 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, INT16 bReserveAPs, INT8 bAction, INT8 fFlags ) { #ifdef DEBUGDECISIONS - STR16 tempstr; + STR16 tempstr; #endif INT32 sLoop,sAPCost; INT32 sTempDest,sGoToGrid; @@ -583,156 +583,152 @@ INT32 InternalGoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, IN } } - // HAVE FOUND AN OK destination AND PLOTTED A VALID BEST PATH TO IT + // HAVE FOUND AN OK destination AND PLOTTED A VALID BEST PATH TO IT #ifdef DEBUGDECISIONS - AINumMessage("Chosen legal destination is gridno ",sDesGrid); - AINumMessage("Tracing along path, pathRouteToGo = ",pSoldier->pathing.usPathIndex); + AINumMessage("Chosen legal destination is gridno ",sDesGrid); + AINumMessage("Tracing along path, pathRouteToGo = ",pSoldier->pathing.usPathIndex); #endif - sGoToGrid = pSoldier->sGridNo; // start back where soldier is standing now - sAPCost = 0; // initialize path cost counter + sGoToGrid = pSoldier->sGridNo; // start back where soldier is standing now + sAPCost = 0; // initialize path cost counter - // 0verhaul: If the destination is within the patrol route, allow it. This is necessary to allow an errant soldier - // to return to his patrol route if flanking or other actions have pulled him beyond his allowed range from origin. - if (SpacesAway(sOrigin,sDesGrid) <= usMaxDist) - { - fAllowDest = TRUE; - } + // 0verhaul: If the destination is within the patrol route, allow it. This is necessary to allow an errant soldier + // to return to his patrol route if flanking or other actions have pulled him beyond his allowed range from origin. + if (SpacesAway(sOrigin,sDesGrid) <= usMaxDist) + { + fAllowDest = TRUE; + } - // we'll only go as far along the plotted route as is within our - // permitted roaming range, and we'll stop as soon as we're down to <= 5 APs + // we'll only go as far along the plotted route as is within our + // permitted roaming range, and we'll stop as soon as we're down to <= 5 APs sTempDest = pSoldier->sGridNo; - for (sLoop = 0; sLoop < (pSoldier->pathing.usPathDataSize - pSoldier->pathing.usPathIndex); sLoop++) + for (sLoop = 0; sLoop < (pSoldier->pathing.usPathDataSize - pSoldier->pathing.usPathIndex); sLoop++) { - // what is the next gridno in the path? + // what is the next gridno in the path? - //sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->pathing.usPathingData[sLoop] + 1) ) ); - //sTempDest = NewGridNo( sGoToGrid,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) ); - sTempDest = NewGridNo( sTempDest,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) ); + //sTempDest = NewGridNo( sGoToGrid,DirectionInc( (INT16) (pSoldier->pathing.usPathingData[sLoop] + 1) ) ); + //sTempDest = NewGridNo( sGoToGrid,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) ); + sTempDest = NewGridNo( sTempDest,DirectionInc( (UINT8) (pSoldier->pathing.usPathingData[sLoop]) ) ); - // this should NEVER be out of bounds - if (sTempDest == sGoToGrid) - { + // this should NEVER be out of bounds + if (sTempDest == sGoToGrid) + { #ifdef BETAVERSION - sprintf(tempstr,"GoAsFarAsPossibleTowards: ERROR - gridno along valid route is invalid! guynum %d, sTempDest = %d",pSoldier->ubID,sTempDest); + sprintf(tempstr,"GoAsFarAsPossibleTowards: ERROR - gridno along valid route is invalid! guynum %d, sTempDest = %d",pSoldier->ubID,sTempDest); #ifdef RECORDNET - fprintf(NetDebugFile,"\n\t%s\n",tempstr); + fprintf(NetDebugFile,"\n\t%s\n",tempstr); #endif - PopMessage(tempstr); - SaveGame(ERROR_SAVE); + PopMessage(tempstr); + SaveGame(ERROR_SAVE); #endif + break; // quit here, sGoToGrid is where we are going + } - break; // quit here, sGoToGrid is where we are going - } - - // if this takes us beyond our permitted "roaming range" - // but if it brings us closer, then allow it! - if (SpacesAway(sOrigin,sTempDest) > usMaxDist && !fAllowDest) - break; // quit here, sGoToGrid is where we are going + // if this takes us beyond our permitted "roaming range" + // but if it brings us closer, then allow it! + if (SpacesAway(sOrigin,sTempDest) > usMaxDist && !fAllowDest) + break; // quit here, sGoToGrid is where we are going - if ( usRoomRequired ) - { - if ( !( InARoom( sTempDest, &usTempRoom ) && usTempRoom == usRoomRequired ) ) + if ( usRoomRequired ) { - // quit here, limited by room! - break; + if ( !( InARoom( sTempDest, &usTempRoom ) && usTempRoom == usRoomRequired ) ) + { + // quit here, limited by room! + break; + } } - } - if ( (fFlags & FLAG_STOPSHORT) && SpacesAway( sDesGrid, sTempDest ) <= STOPSHORTDIST ) - { - break; // quit here, sGoToGrid is where we are going - } + if ( (fFlags & FLAG_STOPSHORT) && SpacesAway( sDesGrid, sTempDest ) <= STOPSHORTDIST ) + { + break; // quit here, sGoToGrid is where we are going + } - // CAN'T CALL PathCost() HERE! IT CALLS findBestPath() and overwrites - // pathRouteToGo !!! Gotta calculate the cost ourselves - Ian - // - //ubAPsLeft = pSoldier->bActionPoints - PathCost(pSoldier,sTempDest,FALSE,FALSE,FALSE,FALSE,FALSE); + // CAN'T CALL PathCost() HERE! IT CALLS findBestPath() and overwrites + // pathRouteToGo !!! Gotta calculate the cost ourselves - Ian + // + //ubAPsLeft = pSoldier->bActionPoints - PathCost(pSoldier,sTempDest,FALSE,FALSE,FALSE,FALSE,FALSE); - if (gfTurnBasedAI) - { - // if we're just starting the "costing" process (first gridno) - if (sLoop == 0) - { - if (pSoldier->usUIMovementMode == RUNNING) + if (gfTurnBasedAI) + { + // if we're just starting the "costing" process (first gridno) + if (sLoop == 0) { - sAPCost += GetAPsStartRun( pSoldier ); // changed by SANDRO - } + if (pSoldier->usUIMovementMode == RUNNING) + { + sAPCost += GetAPsStartRun( pSoldier ); // changed by SANDRO + } } - // ATE: Direction here? - sAPCost += EstimateActionPointCost( pSoldier, sTempDest, (INT8) pSoldier->pathing.usPathingData[sLoop], pSoldier->usUIMovementMode, (INT8) sLoop, (INT8) pSoldier->pathing.usPathDataSize ); - - bAPsLeft = pSoldier->bActionPoints - sAPCost; - } + // ATE: Direction here? + sAPCost += EstimateActionPointCost( pSoldier, sTempDest, (INT8) pSoldier->pathing.usPathingData[sLoop], pSoldier->usUIMovementMode, (INT8) sLoop, (INT8) pSoldier->pathing.usPathDataSize ); + bAPsLeft = pSoldier->bActionPoints - sAPCost; + } - // if this gridno is NOT a legal NPC destination - // DONT'T test path again - that would replace the traced path! - Ian - // NOTE: It's OK to go *THROUGH* water to try and get to the destination! - // sevenfm: jump over fence code - if current gridno is not legal destination, check next gridno - if (!LegalNPCDestination(pSoldier,sTempDest,IGNORE_PATH,WATEROK,0)) - { - // break; - continue; - } + // if this gridno is NOT a legal NPC destination + // DONT'T test path again - that would replace the traced path! - Ian + // NOTE: It's OK to go *THROUGH* water to try and get to the destination! + // sevenfm: jump over fence code - if current gridno is not legal destination, check next gridno + if (!LegalNPCDestination(pSoldier,sTempDest,IGNORE_PATH,WATEROK,0)) + { + // break; + continue; + } - // if after this, we have <= 5 APs remaining, that's far enough, break out - // (the idea is to preserve APs so we can crouch or react if - // necessary, and benefit from the carry-over next turn if not needed) - // This routine is NOT used by any GREEN AI, so such caution is warranted! + // if after this, we have <= 5 APs remaining, that's far enough, break out + // (the idea is to preserve APs so we can crouch or react if + // necessary, and benefit from the carry-over next turn if not needed) + // This routine is NOT used by any GREEN AI, so such caution is warranted! - if ( gfTurnBasedAI && (bAPsLeft < bReserveAPs) ) - break; - else + if ( gfTurnBasedAI && (bAPsLeft < bReserveAPs) ) + break; + else { - sGoToGrid = sTempDest; // we're OK up to here + sGoToGrid = sTempDest; // we're OK up to here - // if exactly 5 APs left, don't bother checking any further - if ( gfTurnBasedAI && (bAPsLeft == bReserveAPs) ) - break; + // if exactly 5 APs left, don't bother checking any further + if ( gfTurnBasedAI && (bAPsLeft == bReserveAPs) ) + break; } } - // if it turned out we couldn't go even 1 tile towards the desired gridno - if (sGoToGrid == pSoldier->sGridNo) + // if it turned out we couldn't go even 1 tile towards the desired gridno + if (sGoToGrid == pSoldier->sGridNo) { #ifdef DEBUGDECISIONS - sprintf(tempstr,"%s will go NOWHERE, path doesn't meet criteria",pSoldier->name); - AIPopMessage(tempstr); + sprintf(tempstr,"%s will go NOWHERE, path doesn't meet criteria",pSoldier->name); + AIPopMessage(tempstr); #endif - pSoldier->pathing.usPathIndex = pSoldier->pathing.usPathDataSize = 0; - return(NOWHERE); // then go nowhere - } - else - { - // possible optimization - stored path IS good if we're going all the way - if (sGoToGrid == sDesGrid) - { - pSoldier->pathing.bPathStored = TRUE; - pSoldier->pathing.sFinalDestination = sGoToGrid; + pSoldier->pathing.usPathIndex = pSoldier->pathing.usPathDataSize = 0; + return(NOWHERE); // then go nowhere } - else if ( pSoldier->pathing.usPathIndex == 0 ) + else { - // we can hack this surely! -- CJC - pSoldier->pathing.bPathStored = TRUE; - pSoldier->pathing.sFinalDestination = sGoToGrid; - pSoldier->pathing.usPathDataSize = sLoop + 1; - } + // possible optimization - stored path IS good if we're going all the way + if (sGoToGrid == sDesGrid) + { + pSoldier->pathing.bPathStored = TRUE; + pSoldier->pathing.sFinalDestination = sGoToGrid; + } + else if ( pSoldier->pathing.usPathIndex == 0 ) + { + // we can hack this surely! -- CJC + pSoldier->pathing.bPathStored = TRUE; + pSoldier->pathing.sFinalDestination = sGoToGrid; + pSoldier->pathing.usPathDataSize = sLoop + 1; + } #ifdef DEBUGDECISIONS ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"%d to %d with %d APs left", pSoldier->ubID, sGoToGrid, pSoldier->bActionPoints ); #endif - - return( sGoToGrid ); } } diff --git a/TacticalAI/ZombieDecideAction.cpp b/TacticalAI/ZombieDecideAction.cpp index 16c7f321f..ee917bea1 100644 --- a/TacticalAI/ZombieDecideAction.cpp +++ b/TacticalAI/ZombieDecideAction.cpp @@ -33,7 +33,7 @@ #include "Text.h" extern BOOLEAN gfHiddenInterrupt; -extern void LogDecideInfo(SOLDIERTYPE *pSoldier); +extern void LogDecideInfo(SOLDIERTYPE *pSoldier, bool doLog = true); extern STR8 gStr8AlertStatus[]; extern STR8 gStr8Attitude[]; diff --git a/TacticalAI/ai.h b/TacticalAI/ai.h index c97d19362..d2a32b5b8 100644 --- a/TacticalAI/ai.h +++ b/TacticalAI/ai.h @@ -12,10 +12,6 @@ extern INT16 gubAIPathCosts[19][19]; #define AI_PATHCOST_RADIUS 9 -extern BOOLEAN gfDisplayCoverValues; -//extern INT16 gsCoverValue[WORLD_MAX]; -extern INT16 * gsCoverValue; - // AI actions enum CreatureCalls @@ -182,7 +178,7 @@ BOOLEAN CanAutoBandage( BOOLEAN fDoFullCheck ); void DebugAI( STR szOutput ); enum { AI_MSG_START, AI_MSG_DECIDE, AI_MSG_INFO, AI_MSG_TOPIC }; -void DebugAI(INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction = -1); +void DebugAI(INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, bool doLog = true, INT8 bAction = -1); void DebugQuestInfo(STR szOutput); INT8 DecideAction(SOLDIERTYPE *pSoldier); INT8 DecideActionBlack(SOLDIERTYPE *pSoldier); @@ -199,7 +195,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 ); diff --git a/TileEngine/renderworld.cpp b/TileEngine/renderworld.cpp index 85eb7bd9d..5e03e74b5 100644 --- a/TileEngine/renderworld.cpp +++ b/TileEngine/renderworld.cpp @@ -23,6 +23,7 @@ #include "LogicalBodyTypes/BodyTypeDB.h" #include "Utilities.h" +#include UINT32 guiShieldGraphic = 0; BOOLEAN fShieldGraphicInit = FALSE; @@ -552,22 +553,25 @@ void ResetRenderParameters( ); void RenderRoomInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); - -#ifdef _DEBUG -//extern UINT8 gubFOVDebugInfoInfo[ WORLD_MAX ]; -//extern UINT8 gubGridNoMarkers[ WORLD_MAX ]; -extern UINT8 * gubFOVDebugInfoInfo; -extern UINT8 * gubGridNoMarkers; -extern UINT8 gubGridNoValue; -extern BOOLEAN gfDisplayCoverValues; -extern BOOLEAN gfDisplayGridNoVisibleValues = 0; -//extern INT16 gsCoverValue[ WORLD_MAX ]; -extern INT16 * gsCoverValue; -extern INT16 gsBestCover; -void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); -void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); -void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); -#endif +//extern UINT8 * gubFOVDebugInfoInfo; +//extern UINT8 * gubGridNoMarkers; +//extern UINT8 gubGridNoValue; +//extern BOOLEAN gfDisplayGridNoVisibleValues = 0; +//void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); +//void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ); +extern INT16 gsBestCover; +INT32* gRenderDebugInfoValues = nullptr; +void RenderDebugInfo(INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS); +void ResetDebugInfoValues() +{ + if (gRenderDebugInfoValues) + { + for (size_t i = 0; i < WORLD_MAX; i++) + { + gRenderDebugInfoValues[i] = 0x7FFFFFFF; + } + } +} void DeleteFromWorld( UINT16 usTileIndex, UINT32 uiRenderTiles, UINT16 usIndex ); @@ -3363,24 +3367,11 @@ UINT32 cnt = 0; RenderRoomInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); } -#ifdef _DEBUG - if( gRenderFlags&RENDER_FLAG_FOVDEBUG ) - { - RenderFOVDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); - } - else if (gfDisplayCoverValues) - { - RenderCoverDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); - } - else if (gfDisplayGridNoVisibleValues) + if (DEBUG_CHEAT_LEVEL() && gTacticalStatus.Team[OUR_TEAM].bTeamActive) { - RenderGridNoVisibleDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); + RenderDebugInfo(gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS); } -#endif - -//#endif - //RenderStaticWorldRect( gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_END_Y ); //AddBaseDirtyRect(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_END_Y ); @@ -8105,7 +8096,6 @@ void RenderRoomInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPoi #ifdef _DEBUG - void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ) { INT8 bXOddFlag = 0; @@ -8215,7 +8205,8 @@ void RenderFOVDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStar } -void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ) +// This one just renders tile gridnos. Not much use nowadays +void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ) { INT8 bXOddFlag = 0; INT16 sAnchorPosX_M, sAnchorPosY_M; @@ -8223,8 +8214,8 @@ void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sSt INT16 sTempPosX_M, sTempPosY_M; INT16 sTempPosX_S, sTempPosY_S; BOOLEAN fEndRenderRow = FALSE, fEndRenderCol = FALSE; - UINT16 usTileIndex; INT16 sX, sY; + INT32 usTileIndex;//dnl ch56 141009 UINT32 uiDestPitchBYTES; UINT8 *pDestBuf; @@ -8264,25 +8255,19 @@ void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sSt sY -= gpWorldLevelData[ usTileIndex ].sHeight; sY += gsRenderHeight; - if (gsCoverValue[ usTileIndex] != 0x7F7F) + SetFont( SMALLCOMPFONT ); + SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, gsVIEWPORT_END_Y, FALSE ); + + if ( !GridNoOnVisibleWorldTile( usTileIndex ) ) { - SetFont( SMALLCOMPFONT ); - SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, gsVIEWPORT_END_Y, FALSE ); - if (usTileIndex == gsBestCover) - { - SetFontForeground( FONT_MCOLOR_RED ); - } - else if (gsCoverValue[ usTileIndex ] < 0) - { - SetFontForeground( FONT_MCOLOR_WHITE ); - } - else - { - SetFontForeground( FONT_GRAY3 ); - } - mprintf_buffer( pDestBuf, uiDestPitchBYTES, TINYFONT1, sX, sY , L"%d", gsCoverValue[ usTileIndex ] ); - SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE ); + SetFontForeground( FONT_MCOLOR_RED ); } + else + { + SetFontForeground( FONT_GRAY3 ); + } + mprintf_buffer( pDestBuf, uiDestPitchBYTES, TINYFONT1, sX, sY , L"%d", usTileIndex ); + SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE ); } @@ -8321,112 +8306,138 @@ void RenderCoverDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sSt UnLockVideoSurface( FRAME_BUFFER ); } +#endif -void RenderGridNoVisibleDebugInfo( INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS ) +void RenderDebugInfo(INT16 sStartPointX_M, INT16 sStartPointY_M, INT16 sStartPointX_S, INT16 sStartPointY_S, INT16 sEndXS, INT16 sEndYS) { - INT8 bXOddFlag = 0; - INT16 sAnchorPosX_M, sAnchorPosY_M; - INT16 sAnchorPosX_S, sAnchorPosY_S; - INT16 sTempPosX_M, sTempPosY_M; - INT16 sTempPosX_S, sTempPosY_S; - BOOLEAN fEndRenderRow = FALSE, fEndRenderCol = FALSE; - INT16 sX, sY; - INT32 usTileIndex;//dnl ch56 141009 - UINT32 uiDestPitchBYTES; - UINT8 *pDestBuf; - + INT8 bXOddFlag = 0; + INT16 sTempPosX_M, sTempPosY_M; + INT16 sTempPosX_S, sTempPosY_S; + BOOLEAN fEndRenderRow = FALSE, fEndRenderCol = FALSE; // Begin Render Loop - sAnchorPosX_M = sStartPointX_M; - sAnchorPosY_M = sStartPointY_M; - sAnchorPosX_S = sStartPointX_S; - sAnchorPosY_S = sStartPointY_S; + INT16 sAnchorPosX_M = sStartPointX_M; + INT16 sAnchorPosY_M = sStartPointY_M; + INT16 sAnchorPosX_S = sStartPointX_S; + INT16 sAnchorPosY_S = sStartPointY_S; - pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + UINT32 uiDestPitchBYTES; + UINT8* pDestBuf = LockVideoSurface(FRAME_BUFFER, &uiDestPitchBYTES); + + const auto mode = gRenderDebugInfoMode; do { - fEndRenderRow = FALSE; sTempPosX_M = sAnchorPosX_M; sTempPosY_M = sAnchorPosY_M; sTempPosX_S = sAnchorPosX_S; sTempPosY_S = sAnchorPosY_S; - if(bXOddFlag > 0) + if (bXOddFlag > 0) sTempPosX_S += 20; - do { - - usTileIndex=FASTMAPROWCOLTOPOS( sTempPosY_M, sTempPosX_M ); - - if ( usTileIndex < GRIDSIZE ) + UINT16 usTileIndex = FASTMAPROWCOLTOPOS(sTempPosY_M, sTempPosX_M); + if (usTileIndex < GRIDSIZE) { - sX = sTempPosX_S + ( WORLD_TILE_X / 2 ) - 5; - sY = sTempPosY_S + ( WORLD_TILE_Y / 2 ) - 5; + INT16 sX = sTempPosX_S + (WORLD_TILE_X / 2) - 5; + INT16 sY = sTempPosY_S + (WORLD_TILE_Y / 2) - 5; // Adjust for interface level - sY -= gpWorldLevelData[ usTileIndex ].sHeight; + sY -= gpWorldLevelData[usTileIndex].sHeight; sY += gsRenderHeight; - SetFont( SMALLCOMPFONT ); - SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, gsVIEWPORT_END_Y, FALSE ); - if ( !GridNoOnVisibleWorldTile( usTileIndex ) ) + if (gRenderDebugInfoValues[usTileIndex] != 0x7FFFFFFF) { - SetFontForeground( FONT_MCOLOR_RED ); - } - else - { - SetFontForeground( FONT_GRAY3 ); + SetFont(SMALLCOMPFONT); + SetFontDestBuffer(FRAME_BUFFER, 0, 0, SCREEN_WIDTH, gsVIEWPORT_END_Y, FALSE); + + + //////////////////////////// + // Debug mode specific setup + switch (mode) + { + case DEBUG_PATHFINDING: + if (gRenderDebugInfoValues[usTileIndex] < 0) + { + SetFontForeground(FONT_LTRED); + } + else + { + SetFontForeground(FONT_LTGREEN); + } + break; + case DEBUG_THREATVALUE: + //TODO implement + goto exit_loop; + break; + case DEBUG_COVERVALUE: + if (usTileIndex == gsBestCover) + { + SetFontForeground(FONT_YELLOW); + } + else if (gRenderDebugInfoValues[usTileIndex] < 0) + { + SetFontForeground(FONT_LTRED); + } + else + { + SetFontForeground(FONT_LTGREEN); + } + break; + + default: + goto exit_loop; + break; + } + //////////////////////////// + + + mprintf_buffer(pDestBuf, uiDestPitchBYTES, TINYFONT1, sX, sY, L"%d", gRenderDebugInfoValues[usTileIndex]); + SetFontDestBuffer(FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE); } - mprintf_buffer( pDestBuf, uiDestPitchBYTES, TINYFONT1, sX, sY , L"%d", usTileIndex ); - SetFontDestBuffer( FRAME_BUFFER , 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE ); } sTempPosX_S += 40; - sTempPosX_M ++; - sTempPosY_M --; + sTempPosX_M++; + sTempPosY_M--; - if ( sTempPosX_S >= sEndXS ) + if (sTempPosX_S >= sEndXS) { fEndRenderRow = TRUE; } - } while( !fEndRenderRow ); + } while (!fEndRenderRow); - if ( bXOddFlag > 0 ) + if (bXOddFlag > 0) { - sAnchorPosY_M ++; + sAnchorPosY_M++; } else { - sAnchorPosX_M ++; + sAnchorPosX_M++; } bXOddFlag = !bXOddFlag; sAnchorPosY_S += 10; - if ( sAnchorPosY_S >= sEndYS ) + if (sAnchorPosY_S >= sEndYS) { fEndRenderCol = TRUE; } + } while (!fEndRenderCol); - } - while( !fEndRenderCol ); - - UnLockVideoSurface( FRAME_BUFFER ); - + exit_loop: + UnLockVideoSurface(FRAME_BUFFER); } -#endif - void ExamineZBufferRect( INT16 sLeft, INT16 sTop, INT16 sRight, INT16 sBottom) { @@ -9088,21 +9099,3 @@ void SetRenderCenter( INT16 sNewX, INT16 sNewY ) gfScrollInertia = FALSE; } - -#ifdef _DEBUG -void RenderFOVDebug( ) -{ - RenderFOVDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); -} - -void RenderCoverDebug( ) -{ - RenderCoverDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); -} - -void RenderGridNoVisibleDebug( ) -{ - RenderGridNoVisibleDebugInfo( gsStartPointX_M, gsStartPointY_M, gsStartPointX_S, gsStartPointY_S, gsEndXS, gsEndYS ); -} - -#endif diff --git a/TileEngine/renderworld.h b/TileEngine/renderworld.h index 140e5d5f4..4ba08f78e 100644 --- a/TileEngine/renderworld.h +++ b/TileEngine/renderworld.h @@ -216,6 +216,16 @@ void SetRenderCenter( INT16 sNewX, INT16 sNewY ); #ifdef _DEBUG void RenderFOVDebug( ); #endif +enum RenderDebugInfoModes +{ + DEBUG_PATHFINDING, + DEBUG_THREATVALUE, + DEBUG_COVERVALUE, + DEBUG_OFF +}; +void ResetDebugInfoValues(); +extern INT32* gRenderDebugInfoValues; + BOOLEAN Zero8BPPDataTo16BPPBufferTransparent( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex ); BOOLEAN Blt8BPPDataTo16BPPBufferTransZIncClipZSameZBurnsThrough( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion, INT16 sZStripIndex ); @@ -227,4 +237,4 @@ BOOLEAN Blt8BPPDataTo16BPPBufferTransZIncObscureClip(UINT16 *pBuffer, UINT32 uiD BOOLEAN Blt8BPPDataTo16BPPBufferTransZTransShadowIncObscureClip(UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion, INT16 sZIndex, UINT16 *p16BPPPalette, BOOLEAN fIgnoreShadows = FALSE); BOOLEAN Blt8BPPDataTo16BPPBufferTransZTransShadowIncObscureClipAlpha(UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, HVOBJECT hAlphaVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion, INT16 sZIndex, UINT16 *p16BPPPalette, BOOLEAN fIgnoreShadows = FALSE); -#endif \ No newline at end of file +#endif diff --git a/TileEngine/worlddef.cpp b/TileEngine/worlddef.cpp index 628a35e3d..012568a3a 100644 --- a/TileEngine/worlddef.cpp +++ b/TileEngine/worlddef.cpp @@ -49,6 +49,7 @@ #include "Button Defines.h" #include "Animation Data.h" #endif +#include #define SET_MOVEMENTCOST( a, b, c, d ) ( ( gubWorldMovementCosts[ a ][ b ][ c ] < d ) ? ( gubWorldMovementCosts[ a ][ b ][ c ] = d ) : 0 ); @@ -84,7 +85,6 @@ extern UINT8 *gubFOVDebugInfoInfo; extern INT16 gsFullTileDirections[MAX_FULLTILE_DIRECTIONS]; extern INT32 dirDelta[8]; extern INT16 DirIncrementer[8]; -extern INT16 *gsCoverValue; extern INT32 gsTempActionGridNo; extern INT32 gsOverItemsGridNo; extern INT32 gsOutOfRangeGridNo; @@ -320,8 +320,6 @@ void DeinitializeWorld() TrashWorld(); if(gubGridNoMarkers) MemFree(gubGridNoMarkers); - if(gsCoverValue) - MemFree(gsCoverValue); if(gubBuildingInfo) MemFree(gubBuildingInfo); if(gusWorldRoomInfo) @@ -4313,10 +4311,12 @@ void SetWorldSize(INT32 nWorldRows, INT32 nWorldCols) gubGridNoMarkers = (UINT8*)MemAlloc(WORLD_MAX); memset(gubGridNoMarkers, 0, sizeof(UINT8)*WORLD_MAX); - if(gsCoverValue) - MemFree(gsCoverValue); - gsCoverValue = (INT16*)MemAlloc(sizeof(INT16)*WORLD_MAX); - memset(gsCoverValue, 0x7F, sizeof(INT16)*WORLD_MAX); + if (DEBUG_CHEAT_LEVEL()) + { + MemFree(gRenderDebugInfoValues); + gRenderDebugInfoValues = (INT32*)MemAlloc(sizeof(INT32) * WORLD_MAX); + ResetDebugInfoValues(); + } // Init building structures and variables if(gubBuildingInfo)