From 64ccb0b14993f067bccf00b27378d3e229965455 Mon Sep 17 00:00:00 2001 From: Asdow <20314541+Asdow@users.noreply.github.com> Date: Sat, 12 Nov 2022 23:05:48 +0200 Subject: [PATCH 1/2] Enabled astars pathing --- Tactical/Handle UI.cpp | 7 +++++++ Tactical/PATHAI.H | 2 +- Tactical/PATHAI.cpp | 8 +++++--- TileEngine/worlddef.cpp | 6 +++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index bd72fab86..1db29597a 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -94,6 +94,13 @@ #include "Map Screen Interface.h" // added by Flugente for SquadNames #include "Keys.h" // added by silversurfer for door handling from the side +#ifdef USE_ASTAR_PATHS +#include "AIInternals.h" +extern BOOLEAN gubWorldTileInLight[MAX_ALLOWED_WORLD_MAX]; +extern BOOLEAN gubIsCorpseThere[MAX_ALLOWED_WORLD_MAX]; +extern INT32 gubMerkCanSeeThisTile[MAX_ALLOWED_WORLD_MAX]; +#endif + ////////////////////////////////////////////////////////////////////////////// // SANDRO - In this file, all APBPConstants[AP_CROUCH] and APBPConstants[AP_PRONE] were changed to GetAPsCrouch() and GetAPsProne() // On the bottom here, there are these functions made diff --git a/Tactical/PATHAI.H b/Tactical/PATHAI.H index 2f0f949cc..8f43e2d68 100644 --- a/Tactical/PATHAI.H +++ b/Tactical/PATHAI.H @@ -12,7 +12,7 @@ // WANNE: Please do not use ASTAR pathing, // because it is a HUGH PERFORMANCE KILLER on big maps!! -//#define USE_ASTAR_PATHS +#define USE_ASTAR_PATHS #ifdef USE_ASTAR_PATHS diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index 829fee54a..8d1cd421f 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -55,9 +55,11 @@ class SOLDIERTYPE; #ifdef USE_ASTAR_PATHS #include "BinaryHeap.hpp" -#include "AIInternals.h" #include "opplist.h" #include "weapons.h" +extern BOOLEAN gubWorldTileInLight[MAX_ALLOWED_WORLD_MAX]; +extern BOOLEAN gubIsCorpseThere[MAX_ALLOWED_WORLD_MAX]; +extern INT32 gubMerkCanSeeThisTile[MAX_ALLOWED_WORLD_MAX]; #endif //#include "dnlprocesstalk.h"//dnl??? @@ -651,7 +653,7 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , fCloseGoodEnough = ( (fFlags & PATH_CLOSE_GOOD_ENOUGH) != 0); fConsiderPersonAtDestAsObstacle = (BOOLEAN)( fPathingForPlayer && fPathAroundPeople && !(fFlags & PATH_IGNORE_PERSON_AT_DEST) ); - if ( fNonSwimmer && Water( dest ) ) + if ( fNonSwimmer && Water( dest, 0 ) ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, water" ) ); return( 0 ); @@ -1416,7 +1418,7 @@ INT16 AStarPathfinder::CalcAP(int const terrainCost, UINT8 const direction) } // Flugente: dragging someone - if ( pSoldier->IsDraggingSomeone( ) ) + if ( pSoldier->IsDragging( false ) ) { movementAPCost *= gItemSettings.fDragAPCostModifier; } diff --git a/TileEngine/worlddef.cpp b/TileEngine/worlddef.cpp index bd0bc3c58..19760d5f1 100644 --- a/TileEngine/worlddef.cpp +++ b/TileEngine/worlddef.cpp @@ -153,9 +153,9 @@ UINT8 (*gubWorldMovementCosts)[MAXDIR][2] = NULL;//dnl ch43 260909 // Flugente: this stuff is only ever used in AStar pathing and is a unnecessary waste of resources otherwise, so I'm putting an end to this #ifdef USE_ASTAR_PATHS //ddd to speed up search of illuminated tiles in PATH AI -BOOLEAN gubWorldTileInLight[ MAX_ALLOWED_WORLD_MAX ]; -BOOLEAN gubIsCorpseThere[ MAX_ALLOWED_WORLD_MAX ]; -INT32 gubMerkCanSeeThisTile[ MAX_ALLOWED_WORLD_MAX ]; +BOOLEAN gubWorldTileInLight[ MAX_ALLOWED_WORLD_MAX ]; +BOOLEAN gubIsCorpseThere[ MAX_ALLOWED_WORLD_MAX ]; +INT32 gubMerkCanSeeThisTile[ MAX_ALLOWED_WORLD_MAX ]; //ddd #endif From e7c0577e3bc45e54d2a0d95130d249b07b29a5b0 Mon Sep 17 00:00:00 2001 From: Asdow <20314541+Asdow@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:44:17 +0200 Subject: [PATCH 2/2] Add toggleable pathfinding choice to ingame options You can choose between original and A* pathfinding algorithms --- GameSettings.cpp | 5 +- GameSettings.h | 1 + Tactical/Handle UI.cpp | 110 +++--- Tactical/PATHAI.H | 8 - Tactical/PATHAI.cpp | 42 +- TileEngine/Buildings.cpp | 830 ++++++++++++++++----------------------- TileEngine/worlddef.cpp | 3 - TileEngine/worlddef.h | 12 - Utils/_ChineseText.cpp | 2 + Utils/_DutchText.cpp | 2 + Utils/_EnglishText.cpp | 2 + Utils/_FrenchText.cpp | 2 + Utils/_GermanText.cpp | 2 + Utils/_ItalianText.cpp | 2 + Utils/_PolishText.cpp | 2 + Utils/_RussianText.cpp | 2 + 16 files changed, 433 insertions(+), 594 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 805f90848..778046307 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -333,6 +333,7 @@ BOOLEAN LoadGameSettings() gGameSettings.fOptions[TOPTION_TOGGLE_TURN_MODE] = FALSE; gGameSettings.fOptions[TOPTION_ALT_START_AIM] = iniReader.ReadBoolean("JA2 Game Settings", "TOPTION_ALT_START_AIM" , FALSE); // Start at max aiming level instead of default no aiming + gGameSettings.fOptions[TOPTION_ALT_PATHFINDING] = iniReader.ReadBoolean("JA2 Game Settings", "TOPTION_ALT_PATHFINDING" , FALSE); // A* pathfinding gGameSettings.fOptions[TOPTION_MERCENARY_FORMATIONS] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_MERCENARY_FORMATIONS" , TRUE ); // Flugente: mercenary formations gGameSettings.fOptions[TOPTION_SHOW_ENEMY_LOCATION] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_SHOW_ENEMY_LOCATION" , FALSE); // sevenfm: show locations of known enemies gGameSettings.fOptions[TOPTION_REPORT_MISS_MARGIN] = iniReader.ReadBoolean("JA2 Game Settings","TOPTION_REPORT_MISS_MARGIN" , FALSE ); // HEADROCK HAM 4: Shot offset report @@ -614,7 +615,8 @@ BOOLEAN SaveGameSettings() settings << "TOPTION_ENABLE_INVENTORY_POPUPS = " << (gGameSettings.fOptions[TOPTION_ENABLE_INVENTORY_POPUPS] ? "TRUE" : "FALSE" ) << endl; // the_bob : enable popups for picking items from sector inv settings << "TOPTION_MERCENARY_FORMATIONS = " << (gGameSettings.fOptions[TOPTION_MERCENARY_FORMATIONS] ? "TRUE" : "FALSE" ) << endl; settings << "TOPTION_SHOW_ENEMY_LOCATION = " << (gGameSettings.fOptions[TOPTION_SHOW_ENEMY_LOCATION] ? "TRUE" : "FALSE" ) << endl; - settings << "TOPTION_ALT_START_AIM = " << (gGameSettings.fOptions[TOPTION_ALT_START_AIM] ? "TRUE" : "FALSE") << endl; + settings << "TOPTION_ALT_START_AIM = " << (gGameSettings.fOptions[TOPTION_ALT_START_AIM] ? "TRUE" : "FALSE") << endl; + settings << "TOPTION_ALT_PATHFINDING = " << (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING] ? "TRUE" : "FALSE") << endl; settings << "TOPTION_CHEAT_MODE_OPTIONS_HEADER = " << (gGameSettings.fOptions[TOPTION_CHEAT_MODE_OPTIONS_HEADER] ? "TRUE" : "FALSE" ) << endl; settings << "TOPTION_FORCE_BOBBY_RAY_SHIPMENTS = " << (gGameSettings.fOptions[TOPTION_FORCE_BOBBY_RAY_SHIPMENTS] ? "TRUE" : "FALSE" ) << endl; @@ -843,6 +845,7 @@ void InitGameSettings() gGameSettings.fOptions[ TOPTION_MERCENARY_FORMATIONS ] = FALSE; // Flugente: mercenary formations gGameSettings.fOptions[TOPTION_SHOW_ENEMY_LOCATION] = FALSE; // sevenfm: show locations of known enemies gGameSettings.fOptions[TOPTION_ALT_START_AIM] = FALSE; + gGameSettings.fOptions[TOPTION_ALT_PATHFINDING] = FALSE; // arynn: Cheat/Debug Menu gGameSettings.fOptions[ TOPTION_CHEAT_MODE_OPTIONS_HEADER ] = FALSE; diff --git a/GameSettings.h b/GameSettings.h index 4c63af49b..8be6f7ca5 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -104,6 +104,7 @@ enum // sevenfm: new settings TOPTION_SHOW_ENEMY_LOCATION, TOPTION_ALT_START_AIM, + TOPTION_ALT_PATHFINDING, // arynn: Debug/Cheat TOPTION_CHEAT_MODE_OPTIONS_HEADER, diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index e62fec0e2..86a50e133 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -88,12 +88,10 @@ #include "Map Screen Interface.h" // added by Flugente for SquadNames #include "Keys.h" // added by silversurfer for door handling from the side -#ifdef USE_ASTAR_PATHS #include "AIInternals.h" extern BOOLEAN gubWorldTileInLight[MAX_ALLOWED_WORLD_MAX]; extern BOOLEAN gubIsCorpseThere[MAX_ALLOWED_WORLD_MAX]; extern INT32 gubMerkCanSeeThisTile[MAX_ALLOWED_WORLD_MAX]; -#endif ////////////////////////////////////////////////////////////////////////////// // SANDRO - In this file, all APBPConstants[AP_CROUCH] and APBPConstants[AP_PRONE] were changed to GetAPsCrouch() and GetAPsProne() @@ -1372,74 +1370,74 @@ UINT32 UIHandleEndTurn( UI_EVENT *pUIEvent ) SaveGame(SAVE__END_TURN_NUM, zString ); } - // Flugente: this stuff is only ever used in AStar pathing and is a unnecessary waste of resources otherwise, so I'm putting an end to this -#ifdef USE_ASTAR_PATHS ////ddd enemy turn optimization - if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT ) ) + if (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING]) { - memset( gubWorldTileInLight, FALSE, sizeof( gubWorldTileInLight ) ); - memset( gubIsCorpseThere, FALSE, sizeof( gubIsCorpseThere ) ); - memset( gubMerkCanSeeThisTile, FALSE, sizeof( gubMerkCanSeeThisTile ) ); - - //sevenfm translated: unwinding of loop. When changing WORLD_MAX to another value will need some cleaning! dangerous code! ;) - - // WANNE: We had a custom user map (Tixa, J9), where the following loop caused an unhandled exception. - // The crash occurd at ~index 16000 when calling the method IsCorpseAtGridNo() ... - // I don't know what causes it ... - // Just try/catch (ugly, but works). - __try - { - for(UINT32 i=0; i<(UINT32)WORLD_MAX; i+=4) - { - gubWorldTileInLight[i] = InLightAtNight(i, gpWorldLevelData[ i ].sHeight); - gubIsCorpseThere[i] = IsCorpseAtGridNo( i, gpWorldLevelData[ i ].sHeight ); - gubWorldTileInLight[i+1] = InLightAtNight(i+1, gpWorldLevelData[ i+1 ].sHeight); - gubIsCorpseThere[i+1] = IsCorpseAtGridNo( i+1, gpWorldLevelData[ i+1 ].sHeight ); - gubWorldTileInLight[i+2] = InLightAtNight(i+2, gpWorldLevelData[ i+2 ].sHeight); - gubIsCorpseThere[i+2] = IsCorpseAtGridNo( i+2, gpWorldLevelData[ i+2 ].sHeight ); - gubWorldTileInLight[i+3] = InLightAtNight(i+3, gpWorldLevelData[ i+3 ].sHeight); - gubIsCorpseThere[i+3] = IsCorpseAtGridNo( i+3, gpWorldLevelData[ i+3 ].sHeight ); - } - } - __except( EXCEPTION_EXECUTE_HANDLER ) + if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT ) ) { - // WANNE: Ignore, so the game can continue ... - } + memset( gubWorldTileInLight, FALSE, sizeof( gubWorldTileInLight ) ); + memset( gubIsCorpseThere, FALSE, sizeof( gubIsCorpseThere ) ); + memset( gubMerkCanSeeThisTile, FALSE, sizeof( gubMerkCanSeeThisTile ) ); + + //sevenfm translated: unwinding of loop. When changing WORLD_MAX to another value will need some cleaning! dangerous code! ;) + + // WANNE: We had a custom user map (Tixa, J9), where the following loop caused an unhandled exception. + // The crash occurd at ~index 16000 when calling the method IsCorpseAtGridNo() ... + // I don't know what causes it ... + // Just try/catch (ugly, but works). + __try + { + for(UINT32 i=0; i<(UINT32)WORLD_MAX; i+=4) + { + gubWorldTileInLight[i] = InLightAtNight(i, gpWorldLevelData[ i ].sHeight); + gubIsCorpseThere[i] = IsCorpseAtGridNo( i, gpWorldLevelData[ i ].sHeight ); + gubWorldTileInLight[i+1] = InLightAtNight(i+1, gpWorldLevelData[ i+1 ].sHeight); + gubIsCorpseThere[i+1] = IsCorpseAtGridNo( i+1, gpWorldLevelData[ i+1 ].sHeight ); + gubWorldTileInLight[i+2] = InLightAtNight(i+2, gpWorldLevelData[ i+2 ].sHeight); + gubIsCorpseThere[i+2] = IsCorpseAtGridNo( i+2, gpWorldLevelData[ i+2 ].sHeight ); + gubWorldTileInLight[i+3] = InLightAtNight(i+3, gpWorldLevelData[ i+3 ].sHeight); + gubIsCorpseThere[i+3] = IsCorpseAtGridNo( i+3, gpWorldLevelData[ i+3 ].sHeight ); + } + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + // WANNE: Ignore, so the game can continue ... + } - INT32 tcnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; - SOLDIERTYPE *tS; + INT32 tcnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; + SOLDIERTYPE *tS; - INT16 sXOffset, sYOffset; - INT32 sGridNo; - UINT16 usSightLimit=0; + INT16 sXOffset, sYOffset; + INT32 sGridNo; + UINT16 usSightLimit=0; - for ( tS = MercPtrs[ tcnt ]; tcnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++tcnt, tS++ ) - { - if ( tS->stats.bLife >= OKLIFE && tS->sGridNo != NOWHERE && tS->bInSector ) + for ( tS = MercPtrs[ tcnt ]; tcnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++tcnt, tS++ ) { - //loop through all the gridnos that we are interested in - for (sYOffset = -30; sYOffset <= 30; ++sYOffset) + if ( tS->stats.bLife >= OKLIFE && tS->sGridNo != NOWHERE && tS->bInSector ) { - for (sXOffset = -30; sXOffset <= 30; ++sXOffset) + //loop through all the gridnos that we are interested in + for (sYOffset = -30; sYOffset <= 30; ++sYOffset) { - sGridNo = tS->sGridNo + sXOffset + (MAXCOL * sYOffset); + for (sXOffset = -30; sXOffset <= 30; ++sXOffset) + { + sGridNo = tS->sGridNo + sXOffset + (MAXCOL * sYOffset); - if ( sGridNo <= 0 || sGridNo >= WORLD_MAX ) - continue; + if ( sGridNo <= 0 || sGridNo >= WORLD_MAX ) + continue; - //usSightLimit = tS->GetMaxDistanceVisible(sGridNo, FALSE, CALC_FROM_WANTED_DIR); - if(gubMerkCanSeeThisTile[sGridNo]==0) - { - gubMerkCanSeeThisTile[sGridNo]=//SoldierToVirtualSoldierLineOfSightTest( tS, sGridNo, FALSE, ANIM_STAND, TRUE, usSightLimit ); - SoldierToVirtualSoldierLineOfSightTest( tS, sGridNo, tS->pathing.bLevel, ANIM_STAND, TRUE, CALC_FROM_WANTED_DIR); - } - }//fo - } - }//if + //usSightLimit = tS->GetMaxDistanceVisible(sGridNo, FALSE, CALC_FROM_WANTED_DIR); + if(gubMerkCanSeeThisTile[sGridNo]==0) + { + gubMerkCanSeeThisTile[sGridNo]=//SoldierToVirtualSoldierLineOfSightTest( tS, sGridNo, FALSE, ANIM_STAND, TRUE, usSightLimit ); + SoldierToVirtualSoldierLineOfSightTest( tS, sGridNo, tS->pathing.bLevel, ANIM_STAND, TRUE, CALC_FROM_WANTED_DIR); + } + }//fo + } + }//if + } } } //ddd enemy turn optimization** -#endif // End our turn! if (is_server || !is_client) diff --git a/Tactical/PATHAI.H b/Tactical/PATHAI.H index 8f43e2d68..c48a06a35 100644 --- a/Tactical/PATHAI.H +++ b/Tactical/PATHAI.H @@ -10,12 +10,6 @@ #define _PATHAI_H #include "isometric utils.h" -// WANNE: Please do not use ASTAR pathing, -// because it is a HUGH PERFORMANCE KILLER on big maps!! -#define USE_ASTAR_PATHS - -#ifdef USE_ASTAR_PATHS - namespace ASTAR { #include "BinaryHeap.hpp" #include @@ -188,8 +182,6 @@ private: };//end namespace ASTAR -#endif// USE_ASTAR_PATHS - BOOLEAN InitPathAI( void ); void ShutDownPathAI( void ); diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index bf5b7a939..fa31d2599 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -49,14 +49,12 @@ class OBJECTTYPE; class SOLDIERTYPE; -#ifdef USE_ASTAR_PATHS #include "BinaryHeap.hpp" #include "opplist.h" #include "weapons.h" extern BOOLEAN gubWorldTileInLight[MAX_ALLOWED_WORLD_MAX]; extern BOOLEAN gubIsCorpseThere[MAX_ALLOWED_WORLD_MAX]; extern INT32 gubMerkCanSeeThisTile[MAX_ALLOWED_WORLD_MAX]; -#endif //#include "dnlprocesstalk.h"//dnl??? extern UINT16 gubAnimSurfaceIndex[ TOTALBODYTYPES ][ NUMANIMATIONSTATES ]; @@ -456,9 +454,6 @@ UINT32 guiFailedPathChecks = 0; UINT32 guiUnsuccessfulPathChecks = 0; #endif -#ifdef USE_ASTAR_PATHS - - //ADB the extra cover feature is supposed to pick a path of the same distance as one calculated with the feature off, //but a safer path, usually farther away from an enemy or following behind some cover. //however it has not been tested and it may need some work, I haven't touched it in a while @@ -2359,7 +2354,6 @@ bool AStarPathfinder::IsSomeoneInTheWay() return false; } -#endif//end ifdef USE_ASTAR_PATHS INT8 RandomSkipListLevel( void ) { @@ -2432,27 +2426,29 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT32 sDestination, INT8 bLevel, INT16 usMov { s->sPlotSrcGrid = s->sGridNo; -#ifdef USE_ASTAR_PATHS -//ddd -CHAR8 errorBuf[511]; UINT32 b,e; -b=GetJA2Clock();//return s->sGridNo+6; + if (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING]) + { + CHAR8 errorBuf[511]; UINT32 b,e; + b=GetJA2Clock();//return s->sGridNo+6; - int retVal = ASTAR::AStarPathfinder::GetInstance().GetPath(s, sDestination, bLevel, usMovementMode, bCopy, fFlags); + int retVal = ASTAR::AStarPathfinder::GetInstance().GetPath(s, sDestination, bLevel, usMovementMode, bCopy, fFlags); - e=GetJA2Clock();sprintf(errorBuf, "timefind bestpath= %d",e-b );LiveMessage(errorBuf); + e=GetJA2Clock();sprintf(errorBuf, "timefind bestpath= %d",e-b );LiveMessage(errorBuf); - if (retVal || TileIsOutOfBounds(sDestination)) { - return retVal; - } - else { - DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR path failed!" ) ); - } + if (retVal || TileIsOutOfBounds(sDestination)) { + return retVal; + } + else { + DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR path failed!" ) ); + } - // if (TileIsOutOfBounds(sDestination)) - { - return 0; + // if (TileIsOutOfBounds(sDestination)) + { + return 0; + } } -#else + else + { //__try //{ INT32 iDestination = sDestination, iOrigination; @@ -4212,7 +4208,7 @@ if(!GridNoOnVisibleWorldTile(iDestination)) //{ // return (0); //} -#endif + } } void GlobalReachableTest( INT32 sStartGridNo ) diff --git a/TileEngine/Buildings.cpp b/TileEngine/Buildings.cpp index ef40eee3b..dfcaa7c8e 100644 --- a/TileEngine/Buildings.cpp +++ b/TileEngine/Buildings.cpp @@ -9,9 +9,9 @@ #define ROOF_LOCATION_CHANCE 8 -UINT8* gubBuildingInfo = NULL; -BUILDING gBuildings[ MAX_BUILDINGS ]; -UINT8 gubNumberOfBuildings; +UINT8* gubBuildingInfo = NULL; +BUILDING gBuildings[ MAX_BUILDINGS ]; +UINT8 gubNumberOfBuildings; #ifdef ROOF_DEBUG extern INT16 gsCoverValue[WORLD_MAX]; @@ -19,17 +19,21 @@ UINT8 gubNumberOfBuildings; #include "renderworld.h" #endif -// WANNE: Overhauls new building climbing only works with A* enabled -// ------------------------- -// A* building climbing - BEGIN -// ------------------------- -#ifdef USE_ASTAR_PATHS - BUILDING * CreateNewBuilding( UINT8 * pubBuilding ) { - if (gubNumberOfBuildings >= MAX_BUILDINGS) + if (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING]) { - return( NULL ); + if (gubNumberOfBuildings >= MAX_BUILDINGS) + { + return( NULL ); + } + } + else + { + if (gubNumberOfBuildings >= MAX_BUILDINGS - 1) + { + return(NULL); + } } // increment # of buildings @@ -83,11 +87,14 @@ BUILDING * FindBuilding( INT32 sGridNo ) BOOLEAN InBuilding( INT32 sGridNo ) { - if ( FindBuilding( sGridNo ) == NULL ) + // sevenfm: FindBuilding() only checks for buildings with flat roofs (those you can climb) + // since InBuilding() is used to determine flashbang effect, check also if tile is in a room and has any roof above + //if ( FindBuilding( sGridNo ) == NULL ) + if (FindBuilding(sGridNo) || InARoom(sGridNo, NULL) && FindStructure(sGridNo, STRUCTURE_ROOF)) { - return( FALSE ); + return(FALSE); } - return( TRUE ); + return(TRUE); } BOOLEAN SameBuilding( INT32 sGridNo1, INT32 sGridNo2 ) @@ -105,515 +112,319 @@ BOOLEAN SameBuilding( INT32 sGridNo1, INT32 sGridNo2 ) BUILDING * GenerateBuilding( INT32 sDesiredSpot ) { - BUILDING * pBuilding; - UINT8 ubBuildingID = 0; - - pBuilding = CreateNewBuilding( &ubBuildingID ); + UINT8 ubBuildingID = 0; + BUILDING* pBuilding = CreateNewBuilding(&ubBuildingID); if (!pBuilding) { - return( NULL ); - } - - // Set reachable - RoofReachableTest( sDesiredSpot, ubBuildingID ); - - // 0verhaul: The RoofReachableTest now finds ALL of the climb points for each climbable building, instead of a max of - // 21 climb points (and a min of 0) for each building. It claims an extended map flag to mark a tile as a climb point. - // So the array of up-climbs and down-climbs is now obsolete. FindClosestClimbPoint is now updated to search the map - // for these flags. - return( pBuilding ); -} - -void GenerateBuildings( void ) -{ - INT32 uiLoop; - - // init building structures and variables - memset( gubBuildingInfo, 0, WORLD_MAX * sizeof( UINT8 ) ); - memset( &gBuildings, 0, MAX_BUILDINGS * sizeof( BUILDING ) ); - gubNumberOfBuildings = 0; - - if ( (gbWorldSectorZ > 0) || gfEditMode) - { - return; - } - -#ifdef ROOF_DEBUG - memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX ); -#endif - - // reset ALL reachable flags - // do once before we start building generation for - // whole map - for ( uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ ) - { - gpWorldLevelData[ uiLoop ].uiFlags &= ~(MAPELEMENT_REACHABLE); - gpWorldLevelData[ uiLoop ].ubExtFlags[0] &= ~(MAPELEMENT_EXT_ROOFCODE_VISITED); - } - - // search through world - // for each location in a room try to find building info - for ( uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ ) - { - if ( (gusWorldRoomInfo[ uiLoop ] != NO_ROOM) && (gubBuildingInfo[ uiLoop ] == NO_BUILDING) && (FindStructure( uiLoop, STRUCTURE_NORMAL_ROOF ) != NULL) ) + return(NULL); + } + + // RoofReachableTest() is called in the original codepath so this if conditional is commented out for now. + // TODO: Convert original pathfinding code to use the same climbing spot flag as A* so we can get rid of the whole else clause + //if (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING]) + //{ + + // // Set reachable + // RoofReachableTest(sDesiredSpot, ubBuildingID); + + // // 0verhaul: The RoofReachableTest now finds ALL of the climb points for each climbable building, instead of a max of + // // 21 climb points (and a min of 0) for each building. It claims an extended map flag to mark a tile as a climb point. + // // So the array of up-climbs and down-climbs is now obsolete. FindClosestClimbPoint is now updated to search the map + // // for these flags. + // return(pBuilding); + //} + //else + { + INT32 uiLoop; + INT32 uiLoop2; + INT32 sTempGridNo, sNextTempGridNo, sVeryTemporaryGridNo; + INT32 sStartGridNo, sCurrGridNo, sPrevGridNo = NOWHERE, sRightGridNo; + UINT8 ubDirection, ubTempDirection; + BOOLEAN fFoundDir, fFoundWall; + UINT32 uiChanceIn = ROOF_LOCATION_CHANCE; // chance of a location being considered + INT32 sWallGridNo; + INT8 bDesiredOrientation; + INT8 bSkipSpots = 0; + SOLDIERTYPE FakeSoldier; + INT32 iLoopCount = 0; + + FakeSoldier.sGridNo = sDesiredSpot; + FakeSoldier.pathing.bLevel = 1; + FakeSoldier.bTeam = 1; + + // Set reachable + RoofReachableTest(sDesiredSpot, ubBuildingID); + + // From sGridNo, search until we find a spot that isn't part of the building + ubDirection = NORTHWEST; + sTempGridNo = sDesiredSpot; + // using diagonal directions to hopefully prevent picking a + // spot that + while ((gpWorldLevelData[sTempGridNo].uiFlags & MAPELEMENT_REACHABLE)) { - GenerateBuilding( uiLoop ); - } - } -} - -INT32 FindClosestClimbPoint( SOLDIERTYPE *pSoldier, INT32 sStartGridNo, INT32 sDesiredGridNo, BOOLEAN fClimbUp ) -{ - BUILDING * pBuilding; - INT32 sGridNo; - INT32 sTestGridNo; - UINT8 ubTestDir; - INT32 sDistance, sClosestDistance = 1000, sClosestSpot= NOWHERE; - - pBuilding = FindBuilding( sDesiredGridNo ); - if (!pBuilding) - { - return( NOWHERE ); - } - - for (sGridNo = 0; sGridNo < WORLD_MAX; sGridNo++) - { - if (gubBuildingInfo[ sGridNo ] == gubBuildingInfo[ sDesiredGridNo ] && - gpWorldLevelData[ sGridNo ].ubExtFlags[1] & MAPELEMENT_EXT_CLIMBPOINT) - { - // Found a climb point for this building - if (fClimbUp) + sNextTempGridNo = NewGridNo(sTempGridNo, DirectionInc(ubDirection)); + if (sTempGridNo == sNextTempGridNo) { - for (ubTestDir = 0; ubTestDir < NUM_WORLD_DIRECTIONS; ubTestDir += 2) - { - sTestGridNo = NewGridNo( sGridNo, DirectionInc( ubTestDir)); - if (gpWorldLevelData[ sTestGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT) - { - // Found a matching climb point - if ( (WhoIsThere2( sTestGridNo, 0 ) == NOBODY || sTestGridNo == pSoldier->sGridNo) - && (WhoIsThere2( sGridNo, 1 ) == NOBODY) && - (!pSoldier || !InGas( pSoldier, sTestGridNo ) ) ) - { - // And it's open - sDistance = PythSpacesAway( sStartGridNo, sTestGridNo ); - if (sDistance < sClosestDistance ) - { - sClosestDistance = sDistance; - sClosestSpot = sTestGridNo; - } - } - } - } + // hit edge of map!??! + return(NULL); } else { - for (ubTestDir = 0; ubTestDir < NUM_WORLD_DIRECTIONS; ubTestDir += 2) - { - sTestGridNo = NewGridNo( sGridNo, DirectionInc( ubTestDir)); - if (gpWorldLevelData[ sTestGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT) - { - // Found a matching climb point - if ( (WhoIsThere2( sTestGridNo, 0 ) == NOBODY) && - (WhoIsThere2( sGridNo, 1 ) == NOBODY || sGridNo == pSoldier->sGridNo) && - (!pSoldier || !InGas( pSoldier, sTestGridNo ) ) ) - { - // And it's open - sDistance = PythSpacesAway( sStartGridNo, sGridNo ); - if (sDistance < sClosestDistance ) - { - sClosestDistance = sDistance; - sClosestSpot = sGridNo; - } - } - } - } + sTempGridNo = sNextTempGridNo; } } - } - - return( sClosestSpot ); -} -// ------------------------- -// A* building climbing - END -// ------------------------- + // we've got our spot + sStartGridNo = sTempGridNo; -// ------------------------- -// JA2 vanilla building climbing - BEGIN -// ------------------------- -#else - -BUILDING * CreateNewBuilding( UINT8 * pubBuilding ) -{ - if (gubNumberOfBuildings >= MAX_BUILDINGS-1) - { - return( NULL ); - } - // increment # of buildings - gubNumberOfBuildings++; - - // clear entry - gBuildings[ gubNumberOfBuildings ].ubNumClimbSpots = 0; - *pubBuilding = gubNumberOfBuildings; - - // return pointer (have to subtract 1 since we just added 1 - return( &(gBuildings[ gubNumberOfBuildings ]) ); -} - -BUILDING * GenerateBuilding( INT32 sDesiredSpot ) -{ - INT32 uiLoop; - INT32 uiLoop2; - INT32 sTempGridNo, sNextTempGridNo, sVeryTemporaryGridNo; - INT32 sStartGridNo, sCurrGridNo, sPrevGridNo = NOWHERE, sRightGridNo; - UINT8 ubDirection, ubTempDirection; - BOOLEAN fFoundDir, fFoundWall; - UINT32 uiChanceIn = ROOF_LOCATION_CHANCE; // chance of a location being considered - INT32 sWallGridNo; - INT8 bDesiredOrientation; - INT8 bSkipSpots = 0; - SOLDIERTYPE FakeSoldier; - BUILDING * pBuilding; - UINT8 ubBuildingID = 0; - INT32 iLoopCount = 0; - - pBuilding = CreateNewBuilding( &ubBuildingID ); - if (!pBuilding) - { - return( NULL ); - } - - FakeSoldier.sGridNo = sDesiredSpot; - FakeSoldier.pathing.bLevel = 1; - FakeSoldier.bTeam = 1; - - // Set reachable - RoofReachableTest( sDesiredSpot, ubBuildingID ); - - // From sGridNo, search until we find a spot that isn't part of the building - ubDirection = NORTHWEST; - sTempGridNo = sDesiredSpot; - // using diagonal directions to hopefully prevent picking a - // spot that - while( (gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE ) ) - { - sNextTempGridNo = NewGridNo( sTempGridNo, DirectionInc( ubDirection ) ); - if ( sTempGridNo == sNextTempGridNo ) + sCurrGridNo = sStartGridNo; + sVeryTemporaryGridNo = NewGridNo(sCurrGridNo, DirectionInc(EAST)); + if (gpWorldLevelData[sVeryTemporaryGridNo].uiFlags & MAPELEMENT_REACHABLE) { - // hit edge of map!??! - return( NULL ); + // go north first + ubDirection = NORTH; } else { - sTempGridNo = sNextTempGridNo; + // go that way (east) + ubDirection = EAST; } - } - - // we've got our spot - sStartGridNo = sTempGridNo; - - sCurrGridNo = sStartGridNo; - sVeryTemporaryGridNo = NewGridNo( sCurrGridNo, DirectionInc( EAST ) ); - if ( gpWorldLevelData[ sVeryTemporaryGridNo ].uiFlags & MAPELEMENT_REACHABLE ) - { - // go north first - ubDirection = NORTH; - } - else - { - // go that way (east) - ubDirection = EAST; - } - gpWorldLevelData[ sStartGridNo ].ubExtFlags[0] |= MAPELEMENT_EXT_ROOFCODE_VISITED; + gpWorldLevelData[sStartGridNo].ubExtFlags[0] |= MAPELEMENT_EXT_ROOFCODE_VISITED; - uiLoop2 = 0; - while(uiLoop2 < WORLD_MAX) - { - // if point to (2 clockwise) is not part of building and is not visited, - // or is starting point, turn! - sRightGridNo = NewGridNo( sCurrGridNo, DirectionInc( gTwoCDirection[ ubDirection ] ) ); - sTempGridNo = sRightGridNo; - if ( ( ( !(gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE) && !(gpWorldLevelData[ sTempGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED) ) || (sTempGridNo == sStartGridNo) ) && (sCurrGridNo != sStartGridNo) ) + uiLoop2 = 0; + while (uiLoop2 < WORLD_MAX) { - iLoopCount++; - - if ( iLoopCount >= 10 ) + // if point to (2 clockwise) is not part of building and is not visited, + // or is starting point, turn! + sRightGridNo = NewGridNo(sCurrGridNo, DirectionInc(gTwoCDirection[ubDirection])); + sTempGridNo = sRightGridNo; + if (((!(gpWorldLevelData[sTempGridNo].uiFlags & MAPELEMENT_REACHABLE) && !(gpWorldLevelData[sTempGridNo].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED)) || (sTempGridNo == sStartGridNo)) && (sCurrGridNo != sStartGridNo)) { - return( NULL ); - } - ubDirection = gTwoCDirection[ ubDirection ]; - // try in that direction - continue; - } - iLoopCount = 0; - - // if spot ahead is part of building, turn - sTempGridNo = NewGridNo( sCurrGridNo, DirectionInc( ubDirection ) ); - if ( gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE ) - { - // first search for a spot that is neither part of the building or visited + iLoopCount++; - // we KNOW that the spot in the original direction is blocked, so only loop 3 times - ubTempDirection = gTwoCDirection[ ubDirection ]; - fFoundDir = FALSE; - for ( uiLoop = 0; uiLoop < 3; uiLoop++ ) - { - sTempGridNo = NewGridNo( sCurrGridNo, DirectionInc( ubTempDirection ) ); - if ( !(gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE) && !(gpWorldLevelData[ sTempGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED) ) + if (iLoopCount >= 10) { - // this is the way to go! - fFoundDir = TRUE; - break; + return(NULL); } - ubTempDirection = gTwoCDirection[ ubTempDirection ]; + ubDirection = gTwoCDirection[ubDirection]; + // try in that direction + continue; } - if (!fFoundDir) + iLoopCount = 0; + + // if spot ahead is part of building, turn + sTempGridNo = NewGridNo(sCurrGridNo, DirectionInc(ubDirection)); + if (gpWorldLevelData[sTempGridNo].uiFlags & MAPELEMENT_REACHABLE) { - // now search for a spot that is just not part of the building - ubTempDirection = gTwoCDirection[ ubDirection ]; + // first search for a spot that is neither part of the building or visited + + // we KNOW that the spot in the original direction is blocked, so only loop 3 times + ubTempDirection = gTwoCDirection[ubDirection]; fFoundDir = FALSE; - for ( uiLoop = 0; uiLoop < 3; uiLoop++ ) + for (uiLoop = 0; uiLoop < 3; uiLoop++) { - sTempGridNo = NewGridNo( sCurrGridNo, DirectionInc( ubTempDirection ) ); - if ( !(gpWorldLevelData[ sTempGridNo ].uiFlags & MAPELEMENT_REACHABLE) ) + sTempGridNo = NewGridNo(sCurrGridNo, DirectionInc(ubTempDirection)); + if (!(gpWorldLevelData[sTempGridNo].uiFlags & MAPELEMENT_REACHABLE) && !(gpWorldLevelData[sTempGridNo].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED)) { // this is the way to go! fFoundDir = TRUE; break; } - ubTempDirection = gTwoCDirection[ ubTempDirection ]; + ubTempDirection = gTwoCDirection[ubTempDirection]; } if (!fFoundDir) { - // WTF is going on? - return( NULL ); + // now search for a spot that is just not part of the building + ubTempDirection = gTwoCDirection[ubDirection]; + fFoundDir = FALSE; + for (uiLoop = 0; uiLoop < 3; uiLoop++) + { + sTempGridNo = NewGridNo(sCurrGridNo, DirectionInc(ubTempDirection)); + if (!(gpWorldLevelData[sTempGridNo].uiFlags & MAPELEMENT_REACHABLE)) + { + // this is the way to go! + fFoundDir = TRUE; + break; + } + ubTempDirection = gTwoCDirection[ubTempDirection]; + } + if (!fFoundDir) + { + // WTF is going on? + return(NULL); + } } + ubDirection = ubTempDirection; + // try in that direction + continue; } - ubDirection = ubTempDirection; - // try in that direction - continue; - } - // move ahead - sPrevGridNo = sCurrGridNo; - sCurrGridNo = sTempGridNo; - sRightGridNo = NewGridNo( sCurrGridNo, DirectionInc( gTwoCDirection[ ubDirection ] ) ); + // move ahead + sPrevGridNo = sCurrGridNo; + sCurrGridNo = sTempGridNo; + sRightGridNo = NewGridNo(sCurrGridNo, DirectionInc(gTwoCDirection[ubDirection])); #ifdef ROOF_DEBUG - if (gsCoverValue[sCurrGridNo] == 0x7F7F) - { - gsCoverValue[sCurrGridNo] = 1; - } - else if (gsCoverValue[sCurrGridNo] >= 0) - { - gsCoverValue[sCurrGridNo]++; - } + if (gsCoverValue[sCurrGridNo] == 0x7F7F) + { + gsCoverValue[sCurrGridNo] = 1; + } + else if (gsCoverValue[sCurrGridNo] >= 0) + { + gsCoverValue[sCurrGridNo]++; + } - DebugAI( String( "Roof code visits %d", sCurrGridNo ) ); + DebugAI(String("Roof code visits %d", sCurrGridNo)); #endif - if (sCurrGridNo == sStartGridNo) - { - // done - break; - } + if (sCurrGridNo == sStartGridNo) + { + // done + break; + } - if ( !(gpWorldLevelData[ sCurrGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED) ) - { - gpWorldLevelData[ sCurrGridNo ].ubExtFlags[0] |= MAPELEMENT_EXT_ROOFCODE_VISITED; + if (!(gpWorldLevelData[sCurrGridNo].ubExtFlags[0] & MAPELEMENT_EXT_ROOFCODE_VISITED)) + { + gpWorldLevelData[sCurrGridNo].ubExtFlags[0] |= MAPELEMENT_EXT_ROOFCODE_VISITED; - if( InARoom(sCurrGridNo, NULL) ) - gubBuildingInfo[ sCurrGridNo ] = ubBuildingID; + if (InARoom(sCurrGridNo, NULL)) + gubBuildingInfo[sCurrGridNo] = ubBuildingID; - // consider this location as possible climb gridno - // there must be a regular wall adjacent to this for us to consider it a - // climb gridno + // consider this location as possible climb gridno + // there must be a regular wall adjacent to this for us to consider it a + // climb gridno - // if the direction is east or north, the wall would be in our gridno; - // if south or west, the wall would be in the gridno two clockwise - fFoundWall = FALSE; + // if the direction is east or north, the wall would be in our gridno; + // if south or west, the wall would be in the gridno two clockwise + fFoundWall = FALSE; - // There must not be roof here either. There are places where a pitched roof butts up against a flat roof. - // Don't mark such a border as a climb point. Otherwise AI units will get stuck. - if (FindStructure( sCurrGridNo, STRUCTURE_ROOF ) == NULL && - NewOKDestination( &FakeSoldier, sCurrGridNo, FALSE, 0 ) ) - { - switch( ubDirection ) + // There must not be roof here either. There are places where a pitched roof butts up against a flat roof. + // Don't mark such a border as a climb point. Otherwise AI units will get stuck. + if (FindStructure(sCurrGridNo, STRUCTURE_ROOF) == NULL && + NewOKDestination(&FakeSoldier, sCurrGridNo, FALSE, 0)) { - case NORTH: - sWallGridNo = sCurrGridNo; - bDesiredOrientation = OUTSIDE_TOP_RIGHT; - break; - case EAST: - sWallGridNo = sCurrGridNo; - bDesiredOrientation = OUTSIDE_TOP_LEFT; - break; - case SOUTH: - sWallGridNo = ( sCurrGridNo + DirectionInc( gTwoCDirection[ ubDirection ] ) ); - bDesiredOrientation = OUTSIDE_TOP_RIGHT; - break; - case WEST: - sWallGridNo = ( sCurrGridNo + DirectionInc( gTwoCDirection[ ubDirection ] ) ); - bDesiredOrientation = OUTSIDE_TOP_LEFT; - break; - default: - // what the heck? - return( NULL ); - } + switch (ubDirection) + { + case NORTH: + sWallGridNo = sCurrGridNo; + bDesiredOrientation = OUTSIDE_TOP_RIGHT; + break; + case EAST: + sWallGridNo = sCurrGridNo; + bDesiredOrientation = OUTSIDE_TOP_LEFT; + break; + case SOUTH: + sWallGridNo = (sCurrGridNo + DirectionInc(gTwoCDirection[ubDirection])); + bDesiredOrientation = OUTSIDE_TOP_RIGHT; + break; + case WEST: + sWallGridNo = (sCurrGridNo + DirectionInc(gTwoCDirection[ubDirection])); + bDesiredOrientation = OUTSIDE_TOP_LEFT; + break; + default: + // what the heck? + return(NULL); + } - if (bDesiredOrientation == OUTSIDE_TOP_LEFT) - { - if (WallExistsOfTopLeftOrientation( sWallGridNo )) + if (bDesiredOrientation == OUTSIDE_TOP_LEFT) { - fFoundWall = TRUE; + if (WallExistsOfTopLeftOrientation(sWallGridNo)) + { + fFoundWall = TRUE; + } } - } - else - { - if (WallExistsOfTopRightOrientation( sWallGridNo )) + else { - fFoundWall = TRUE; + if (WallExistsOfTopRightOrientation(sWallGridNo)) + { + fFoundWall = TRUE; + } } } - } - if (fFoundWall) - { + if (fFoundWall) + { #ifdef ROOF_DEBUG - gsCoverValue[sCurrGridNo] = 50; + gsCoverValue[sCurrGridNo] = 50; #endif - if (bSkipSpots > 0) - { - bSkipSpots--; - } - else if ( Random( uiChanceIn ) == 0 ) - { - pBuilding->sUpClimbSpots[ pBuilding->ubNumClimbSpots ] = sCurrGridNo; - pBuilding->sDownClimbSpots[ pBuilding->ubNumClimbSpots ] = sRightGridNo; - pBuilding->ubNumClimbSpots++; - - if ( pBuilding->ubNumClimbSpots == MAX_CLIMBSPOTS_PER_BUILDING) + if (bSkipSpots > 0) { - // gotta stop! - return( pBuilding ); + bSkipSpots--; } + else if (Random(uiChanceIn) == 0) + { + pBuilding->sUpClimbSpots[pBuilding->ubNumClimbSpots] = sCurrGridNo; + pBuilding->sDownClimbSpots[pBuilding->ubNumClimbSpots] = sRightGridNo; + pBuilding->ubNumClimbSpots++; - // if location is added as a spot, reset uiChanceIn - uiChanceIn = ROOF_LOCATION_CHANCE; + if (pBuilding->ubNumClimbSpots == MAX_CLIMBSPOTS_PER_BUILDING) + { + // gotta stop! + return(pBuilding); + } + + // if location is added as a spot, reset uiChanceIn + uiChanceIn = ROOF_LOCATION_CHANCE; #ifdef ROOF_DEBUG - gsCoverValue[sCurrGridNo] = 99; + gsCoverValue[sCurrGridNo] = 99; #endif - // skip the next spot - bSkipSpots = 1; - } - else - { - // didn't pick this location, so increase chance that next location - // will be considered - if (uiChanceIn > 2) + // skip the next spot + bSkipSpots = 1; + } + else { - uiChanceIn--; + // didn't pick this location, so increase chance that next location + // will be considered + if (uiChanceIn > 2) + { + uiChanceIn--; + } } } - } - else - { - // can't select this spot - if ( ( !TileIsOutOfBounds(sPrevGridNo)) && (pBuilding->ubNumClimbSpots > 0) ) + else { - if ( pBuilding->sDownClimbSpots[ pBuilding->ubNumClimbSpots - 1 ] == sCurrGridNo ) + // can't select this spot + if ((!TileIsOutOfBounds(sPrevGridNo)) && (pBuilding->ubNumClimbSpots > 0)) { - // unselect previous spot - pBuilding->ubNumClimbSpots--; - // overwrote a selected spot so go into automatic selection for later - uiChanceIn = 1; + if (pBuilding->sDownClimbSpots[pBuilding->ubNumClimbSpots - 1] == sCurrGridNo) + { + // unselect previous spot + pBuilding->ubNumClimbSpots--; + // overwrote a selected spot so go into automatic selection for later + uiChanceIn = 1; #ifdef ROOF_DEBUG - // reset marker - gsCoverValue[sPrevGridNo] = 1; + // reset marker + gsCoverValue[sPrevGridNo] = 1; #endif + } } - } - // skip the next gridno - bSkipSpots = 1; + // skip the next gridno + bSkipSpots = 1; + } } + uiLoop2++; } - uiLoop2++; - } - // If we've run out of loop before we've run out of building, then there is - // something that has gone pear shaped - if(uiLoop2 >= WORLD_MAX) - { - INT32 x = 0; - INT32 y = 0; - while((sDesiredSpot - ((y + 1) * WORLD_COLS)) >= 0) - { - ++y; - } - x = sDesiredSpot - (y * WORLD_COLS); - DebugMsg (TOPIC_JA2,DBG_LEVEL_2,String( "113/UC Warning! Building Walk Algorithm has covered the entire map! Building %d located at [%d,%d] must be bogus.", ubBuildingID, x, y )); - } - - // at end could prune # of locations if there are too many - - return( pBuilding ); -} - -BUILDING * FindBuilding( INT32 sGridNo ) -{ - UINT8 ubBuildingID; - - if ( TileIsOutOfBounds( sGridNo ) ) - { - return( NULL ); - } - - // id 0 indicates no building - ubBuildingID = gubBuildingInfo[ sGridNo ]; - - if ( ubBuildingID == NO_BUILDING ) - { - return( NULL ); - - /* - // need extra checks to see if is valid spot... - // must have valid room information and be a flat-roofed - // building - if ( InARoom( sGridNo, &ubRoomNo ) && (FindStructure( sGridNo, STRUCTURE_NORMAL_ROOF ) != NULL) ) - { - return( GenerateBuilding( sGridNo ) ); - } - else + // If we've run out of loop before we've run out of building, then there is + // something that has gone pear shaped + if (uiLoop2 >= WORLD_MAX) { - return( NULL ); + INT32 x = 0; + INT32 y = 0; + while ((sDesiredSpot - ((y + 1) * WORLD_COLS)) >= 0) + { + ++y; + } + x = sDesiredSpot - (y * WORLD_COLS); + DebugMsg(TOPIC_JA2, DBG_LEVEL_2, String("113/UC Warning! Building Walk Algorithm has covered the entire map! Building %d located at [%d,%d] must be bogus.", ubBuildingID, x, y)); } - */ - } - else if ( ubBuildingID > gubNumberOfBuildings ) // huh? - { - return( NULL ); + // at end could prune # of locations if there are too many + return(pBuilding); } - - return( &(gBuildings[ ubBuildingID ]) ); } -BOOLEAN InBuilding( INT32 sGridNo ) -{ - // sevenfm: FindBuilding() only checks for buildings with flat roofs (those you can climb) - // since InBuilding() is used to determine flashbang effect, check also if tile is in a room and has any roof above - //if ( FindBuilding( sGridNo ) == NULL ) - if (FindBuilding(sGridNo) || InARoom(sGridNo, NULL) && FindStructure(sGridNo, STRUCTURE_ROOF)) - { - return( FALSE ); - } - return( TRUE ); -} - - void GenerateBuildings( void ) { - INT32 uiLoop; - // init building structures and variables memset( gubBuildingInfo, 0, WORLD_MAX * sizeof( UINT8 ) ); memset( &gBuildings, 0, MAX_BUILDINGS * sizeof( BUILDING ) ); @@ -631,7 +442,7 @@ void GenerateBuildings( void ) // reset ALL reachable flags // do once before we start building generation for // whole map - for ( uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ ) + for (INT32 uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ ) { gpWorldLevelData[ uiLoop ].uiFlags &= ~(MAPELEMENT_REACHABLE); gpWorldLevelData[ uiLoop ].ubExtFlags[0] &= ~(MAPELEMENT_EXT_ROOFCODE_VISITED); @@ -639,7 +450,7 @@ void GenerateBuildings( void ) // search through world // for each location in a room try to find building info - for ( uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ ) + for (INT32 uiLoop = 0; uiLoop < WORLD_MAX; uiLoop++ ) { if ( (gusWorldRoomInfo[ uiLoop ] != NO_ROOM) && (gubBuildingInfo[ uiLoop ] == NO_BUILDING) && (FindStructure( uiLoop, STRUCTURE_NORMAL_ROOF ) != NULL) ) { @@ -650,66 +461,103 @@ void GenerateBuildings( void ) INT32 FindClosestClimbPoint( SOLDIERTYPE *pSoldier, INT32 sStartGridNo, INT32 sDesiredGridNo, BOOLEAN fClimbUp ) { - BUILDING * pBuilding; - INT32 sDistance, sClosestDistance = 1000, sClosestSpot= NOWHERE; - - pBuilding = FindBuilding( sDesiredGridNo ); + BUILDING* pBuilding = FindBuilding( sDesiredGridNo ); if (!pBuilding) { return( NOWHERE ); } - - // WANNE: Reworked climbing code from sevenfm - UINT8 ubNumClimbSpots; - INT32 * psClimbSpots; - UINT8 ubLoop; - - ubNumClimbSpots = pBuilding->ubNumClimbSpots; - if (fClimbUp) + INT32 sDistance, sClosestDistance = 1000, sClosestSpot = NOWHERE; + if (gGameSettings.fOptions[TOPTION_ALT_PATHFINDING]) { - psClimbSpots = pBuilding->sUpClimbSpots; + for (INT32 sGridNo = 0; sGridNo < WORLD_MAX; sGridNo++) + { + if (gubBuildingInfo[sGridNo] == gubBuildingInfo[sDesiredGridNo] && + gpWorldLevelData[sGridNo].ubExtFlags[1] & MAPELEMENT_EXT_CLIMBPOINT) + { + // Found a climb point for this building + if (fClimbUp) + { + for (UINT8 ubTestDir = 0; ubTestDir < NUM_WORLD_DIRECTIONS; ubTestDir += 2) + { + INT32 sTestGridNo = NewGridNo(sGridNo, DirectionInc(ubTestDir)); + if (gpWorldLevelData[sTestGridNo].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT) + { + // Found a matching climb point + if ((WhoIsThere2(sTestGridNo, 0) == NOBODY || sTestGridNo == pSoldier->sGridNo) + && (WhoIsThere2(sGridNo, 1) == NOBODY) && + (!pSoldier || !InGas(pSoldier, sTestGridNo))) + { + // And it's open + sDistance = PythSpacesAway(sStartGridNo, sTestGridNo); + if (sDistance < sClosestDistance) + { + sClosestDistance = sDistance; + sClosestSpot = sTestGridNo; + } + } + } + } + } + else + { + for (UINT8 ubTestDir = 0; ubTestDir < NUM_WORLD_DIRECTIONS; ubTestDir += 2) + { + INT32 sTestGridNo = NewGridNo(sGridNo, DirectionInc(ubTestDir)); + if (gpWorldLevelData[sTestGridNo].ubExtFlags[0] & MAPELEMENT_EXT_CLIMBPOINT) + { + // Found a matching climb point + if ((WhoIsThere2(sTestGridNo, 0) == NOBODY) && + (WhoIsThere2(sGridNo, 1) == NOBODY || sGridNo == pSoldier->sGridNo) && + (!pSoldier || !InGas(pSoldier, sTestGridNo))) + { + // And it's open + sDistance = PythSpacesAway(sStartGridNo, sGridNo); + if (sDistance < sClosestDistance) + { + sClosestDistance = sDistance; + sClosestSpot = sGridNo; + } + } + } + } + } + } + } } else { - psClimbSpots = pBuilding->sDownClimbSpots; - } + // WANNE: Reworked climbing code from sevenfm + INT32* psClimbSpots; + UINT8 ubNumClimbSpots = pBuilding->ubNumClimbSpots; - for ( ubLoop = 0; ubLoop < ubNumClimbSpots; ubLoop++ ) - { - if( (WhoIsThere2( pBuilding->sUpClimbSpots[ ubLoop ], 0 ) == NOBODY || - WhoIsThere2( pBuilding->sUpClimbSpots[ ubLoop ], 0 ) == pSoldier->ubID ) && - (WhoIsThere2( pBuilding->sDownClimbSpots[ ubLoop ], 1 ) == NOBODY || - WhoIsThere2( pBuilding->sDownClimbSpots[ ubLoop ], 1 ) == pSoldier->ubID) && - !InGas( pSoldier, psClimbSpots[ ubLoop] ) && - !Water( psClimbSpots[ ubLoop], pSoldier->pathing.bLevel) ) + if (fClimbUp) + { + psClimbSpots = pBuilding->sUpClimbSpots; + } + else { - sDistance = PythSpacesAway( sStartGridNo, psClimbSpots[ ubLoop ] ); - if (sDistance < sClosestDistance ) + psClimbSpots = pBuilding->sDownClimbSpots; + } + + for (UINT8 ubLoop = 0; ubLoop < ubNumClimbSpots; ubLoop++) + { + if ((WhoIsThere2(pBuilding->sUpClimbSpots[ubLoop], 0) == NOBODY || + WhoIsThere2(pBuilding->sUpClimbSpots[ubLoop], 0) == pSoldier->ubID) && + (WhoIsThere2(pBuilding->sDownClimbSpots[ubLoop], 1) == NOBODY || + WhoIsThere2(pBuilding->sDownClimbSpots[ubLoop], 1) == pSoldier->ubID) && + !InGas(pSoldier, psClimbSpots[ubLoop]) && + !Water(psClimbSpots[ubLoop], pSoldier->pathing.bLevel)) { - sClosestDistance = sDistance; - sClosestSpot = psClimbSpots[ ubLoop ]; + sDistance = PythSpacesAway(sStartGridNo, psClimbSpots[ubLoop]); + if (sDistance < sClosestDistance) + { + sClosestDistance = sDistance; + sClosestSpot = psClimbSpots[ubLoop]; + } } } } - - return( sClosestSpot ); -} -BOOLEAN SameBuilding( INT32 sGridNo1, INT32 sGridNo2 ) -{ - if ( gubBuildingInfo[ sGridNo1 ] == NO_BUILDING ) - { - return( FALSE ); - } - if ( gubBuildingInfo[ sGridNo2 ] == NO_BUILDING ) - { - return( FALSE ); - } - return( (BOOLEAN) (gubBuildingInfo[ sGridNo1] == gubBuildingInfo[ sGridNo2 ]) ); + return( sClosestSpot ); } - -#endif -// ------------------------- -// JA2 vanilla building climbing - END -// ------------------------- diff --git a/TileEngine/worlddef.cpp b/TileEngine/worlddef.cpp index c141200c2..bf616f584 100644 --- a/TileEngine/worlddef.cpp +++ b/TileEngine/worlddef.cpp @@ -145,14 +145,11 @@ UINT32 gSurfaceMemUsage; //UINT8 gubWorldMovementCosts[ WORLD_MAX ][MAXDIR][2]; UINT8 (*gubWorldMovementCosts)[MAXDIR][2] = NULL;//dnl ch43 260909 -// Flugente: this stuff is only ever used in AStar pathing and is a unnecessary waste of resources otherwise, so I'm putting an end to this -#ifdef USE_ASTAR_PATHS //ddd to speed up search of illuminated tiles in PATH AI BOOLEAN gubWorldTileInLight[ MAX_ALLOWED_WORLD_MAX ]; BOOLEAN gubIsCorpseThere[ MAX_ALLOWED_WORLD_MAX ]; INT32 gubMerkCanSeeThisTile[ MAX_ALLOWED_WORLD_MAX ]; //ddd -#endif // set to nonzero (locs of base gridno of structure are good) to have it defined by structure code INT16 gsRecompileAreaTop = 0; diff --git a/TileEngine/worlddef.h b/TileEngine/worlddef.h index 0d60208e8..847d902f5 100644 --- a/TileEngine/worlddef.h +++ b/TileEngine/worlddef.h @@ -281,18 +281,6 @@ typedef struct // World Data extern MAP_ELEMENT *gpWorldLevelData; - -// Flugente: this stuff is only ever used in AStar pathing and is a unnecessary waste of resources otherwise, so I'm putting an end to this -#ifdef USE_ASTAR_PATHS -// World Movement Costs -//UINT8 gubWorldMovementCosts[ WORLD_MAX ][MAXDIR][2]; -//ddd Tables to track tactical value of grid tiles -extern BOOLEAN gubWorldTileInLight[ MAX_ALLOWED_WORLD_MAX ]; -extern BOOLEAN gubIsCorpseThere[ MAX_ALLOWED_WORLD_MAX ]; //Tracks position of corpses for AI avoidance -extern INT32 gubMerkCanSeeThisTile[ MAX_ALLOWED_WORLD_MAX ]; //Tracks visibility my mercs -//ddd -#endif - extern UINT8 (*gubWorldMovementCosts)[MAXDIR][2];//dnl ch43 260909 //dnl ch44 290909 Translation routine diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 2290f4db0..695fc5929 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -6575,6 +6575,7 @@ STR16 zOptionsToggleText[] = L"保持佣兵间距", // when multiple mercs are selected, they will try to keep their relative distances L"显示已知敌人位置", //L"Show enemy location", show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--作弊模式选项--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"强制 Bobby Ray 送货", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6695,6 +6696,7 @@ STR16 zOptionsScreenHelpText[] = L"打开时,当选择多个佣兵,在前进时会保持彼此的间距。|C|t|r|l+|A|l|t+|G \n(按|S|h|i|f|t+点击人物头像可以加入或移出队伍)", //L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", TODO.Translate L"打开时,会显示已知敌人最后移动的位置。", //L"When ON, shows last known enemy location.", L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"强制 Bobby Ray 出货", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index ecdcc0d8a..77e4cba5b 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -6577,6 +6577,7 @@ STR16 zOptionsToggleText[] = L"Formation Movement", // when multiple mercs are selected, they will try to keep their relative distances // TODO.Translate L"Show enemy location", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Force Bobby Ray shipments", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6697,6 +6698,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", //TODO.Translate L"When ON, shows last known enemy location.", //TODO.Translate L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Force all pending Bobby Ray shipments", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index f4d8413fd..bc75fc678 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -6575,6 +6575,7 @@ STR16 zOptionsToggleText[] = L"Formation Movement", // when multiple mercs are selected, they will try to keep their relative distances L"Show enemy location", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Force Bobby Ray Shipments", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6695,6 +6696,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", L"When ON, shows last known enemy location.", L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Force all pending Bobby Ray shipments", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index c5f69a294..dc183be94 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -6582,6 +6582,7 @@ STR16 zOptionsToggleText[] = L"Déplacement tactique", // when multiple mercs are selected, they will try to keep their relative distances L"Show enemy location", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Options mode triche--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Forcer envois Bobby Ray", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6701,6 +6702,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", //TODO.Translate L"When ON, shows last known enemy location.", //TODO.Translate L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Forcer tous les envois en attente de Bobby Ray", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index a3b0ff0f0..d2d6009a3 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -6448,6 +6448,7 @@ STR16 zOptionsToggleText[] = L"Bewegung in Formation", // when multiple mercs are selected, they will try to keep their relative distances L"Show enemy location", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Erzwinge BR Lieferung", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6568,6 +6569,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", //TODO.Translate L"When ON, shows last known enemy location.", //TODO.Translate L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Force all pending Bobby Ray shipments", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 79a9bdb61..9a3463944 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -6560,6 +6560,7 @@ STR16 zOptionsToggleText[] = L"Formation Movement", // when multiple mercs are selected, they will try to keep their relative distances // TODO.Translate L"Show enemy location", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Force Bobby Ray shipments", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6680,6 +6681,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", //TODO.Translate L"When ON, shows last known enemy location.", //TODO.Translate L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Force all pending Bobby Ray shipments", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index c5fc6a3da..187348df0 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -6579,6 +6579,7 @@ STR16 zOptionsToggleText[] = L"Formation Movement", // when multiple mercs are selected, they will try to keep their relative distances // TODO.Translate L"Show enemy location", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Cheat Mode Options--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Force Bobby Ray shipments", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6699,6 +6700,7 @@ STR16 zOptionsScreenHelpText[] = L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", //TODO.Translate L"When ON, shows last known enemy location.", //TODO.Translate L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Wymuś wszystkie oczekiwane dostawy od Bobby Ray's.", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 125f6effd..c1d100e13 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -6571,6 +6571,7 @@ STR16 zOptionsToggleText[] = L"Боевой порядок", // when multiple mercs are selected, they will try to keep their relative distances L"Показывать расположение", // show locator on last known enemy location L"Start at maximum aim", + L"Alternative pathfinding", L"--Читерские настройки--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"Ускорить доставку Бобби Рэя", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6691,6 +6692,7 @@ STR16 zOptionsScreenHelpText[] = L"Если выбрано несколько наёмников,\nони будут пытаться сохранять взаимное расположение\nи дистанцию при движении\n(нажмите |C|t|r|l+|A|l|t+|G для переключения или |S|h|i|f|t + клик для движения).", L"Если включено, показывает известное расположение противника\n(нажмите |S|h|i|f|t, чтобы показать источник шума).", L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"Выберите этот пункт,\nчтобы груз Бобби Рэя прибыл немедленно.", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END",