From 11c4d9648eea4f3335679ab4f13e8b88b33d492e Mon Sep 17 00:00:00 2001 From: Overhaul Date: Fri, 9 Nov 2007 09:10:40 +0000 Subject: [PATCH] Added memory leak detection to "new" operator Prevent merc in slot 0 from disappearing due to errant schedule. Still need to find and fix schedule though Prevent attempts to turn or climb roof when soldier collapsed Fix problems when mortar or RPG randomly assigned to a soldier but cannot be added for whatever reason Pathing favors beginning path on orthogonal and using as few turns as possible (mostly old behavior). Prevent interruptions when soldier climbs up, which can cause fall into building just below the target roof position Fix pauses due to soldiers hurt in previous turn Fix soldier path not reaching destination when thrown by explosion damage or heavy gunfire Restore pathing to cave and underground exits Fix too many AI ending up on the roofs git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1588 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- Editor/EditorMercs.cpp | 4 +-- GameSettings.cpp | 32 ++++++++--------- Standard Gaming Platform/MemMan.h | 5 +++ Strategic/Auto Resolve.cpp | 2 +- Strategic/Scheduling.cpp | 10 ++++++ Tactical/Enemy Soldier Save.cpp | 8 ++--- Tactical/Handle UI.cpp | 15 +++++++- Tactical/Inventory Choosing.cpp | 57 ++++++++++++++++++++----------- Tactical/PATHAI.cpp | 14 ++++++-- Tactical/Soldier Ani.cpp | 17 ++++++--- Tactical/Soldier Control.cpp | 18 ++++++++-- Tactical/Soldier Create.cpp | 6 ++-- Tactical/Soldier Tile.cpp | 34 +++++++----------- Tactical/Turn Based Input.cpp | 13 ++++++- TacticalAI/AIMain.cpp | 6 ++++ TacticalAI/AIUtils.cpp | 4 ++- TacticalAI/DecideAction.cpp | 48 +++++++++++++++----------- TileEngine/Exit Grids.cpp | 4 +-- gamescreen.cpp | 2 +- 19 files changed, 197 insertions(+), 102 deletions(-) diff --git a/Editor/EditorMercs.cpp b/Editor/EditorMercs.cpp index a7f8fb65..11ae2a34 100644 --- a/Editor/EditorMercs.cpp +++ b/Editor/EditorMercs.cpp @@ -1966,7 +1966,7 @@ void InitDetailedPlacementForMerc() Assert( !gpSelected->pDetailedPlacement ); // WDS - Clean up inventory handling - gpSelected->pDetailedPlacement = new (MemAlloc(SIZEOF_SOLDIERCREATE_STRUCT)) SOLDIERCREATE_STRUCT; + gpSelected->pDetailedPlacement = new SOLDIERCREATE_STRUCT; Assert( gpSelected->pDetailedPlacement ); gpSelected->pBasicPlacement->fDetailedPlacement = TRUE; @@ -3710,7 +3710,7 @@ void PasteMercPlacement( INT32 iMapIndex ) gTempBasicPlacement.fDetailedPlacement = TRUE; gTempBasicPlacement.fPriorityExistance = gSaveBufferBasicPlacement.fPriorityExistance; // WDS - Clean up inventory handling - pNode->pDetailedPlacement = new (MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT )) SOLDIERCREATE_STRUCT; + pNode->pDetailedPlacement = new SOLDIERCREATE_STRUCT; if( !pNode->pDetailedPlacement ) { AssertMsg( 0, "Failed to allocate memory for new detailed placement in PasteMercPlacement." ); diff --git a/GameSettings.cpp b/GameSettings.cpp index 4c49d441..f3270ae5 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -389,8 +389,8 @@ void LoadGameExternalOptions() gGameExternalOptions.iStartAttribute = iniReader.ReadInteger("JA2 Laptop Settings","START_ATTRIBUTE",55); //gGameExternalOptions.fPers_att = iniReader.ReadBoolean("JA2 Laptop Settings","USE_RANDOM_PERSONALITY",FALSE); - gGameExternalOptions.iCustomPersonality = iniReader.ReadInteger("JA2 Laptop Settings","CUSTOM_PERSONALITY",0); - gGameExternalOptions.iCustomAttitude = iniReader.ReadInteger("JA2 Laptop Settings","CUSTOM_ATTITUDE",0); + gGameExternalOptions.iCustomPersonality = (INT8) iniReader.ReadInteger("JA2 Laptop Settings","CUSTOM_PERSONALITY",0); + gGameExternalOptions.iCustomAttitude = (INT8) iniReader.ReadInteger("JA2 Laptop Settings","CUSTOM_ATTITUDE",0); //Merc settings gGameExternalOptions.fMercDayOne = iniReader.ReadBoolean("JA2 Laptop Settings","MERC_AVAILABLE_DAY_ONE",FALSE); @@ -404,7 +404,7 @@ void LoadGameExternalOptions() gGameExternalOptions.giInsaneMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","INSANE_MERC_DEATHS",4); //################# System Settings ################# - gGameExternalOptions.gubDeadLockDelay = iniReader.ReadInteger("JA2 System Settings","DEAD_LOCK_DELAY",15); + gGameExternalOptions.gubDeadLockDelay = (UINT8) iniReader.ReadInteger("JA2 System Settings","DEAD_LOCK_DELAY",15); gGameExternalOptions.gfEnableEmergencyButton_SkipStrategicEvents = iniReader.ReadBoolean("JA2 System Settings","ENABLE_EMERGENCY_BUTTON_NUMLOCK_TO_SKIP_STRATEGIC_EVENTS",0); @@ -433,11 +433,11 @@ void LoadGameExternalOptions() gGameExternalOptions.fAllowTacticalMilitiaCommand = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_TACTICAL_MILITIA_COMMAND",0); // Enemy AP settings - gGameExternalOptions.iEasyAPBonus = iniReader.ReadInteger("JA2 Tactical Settings","NOVICE_AP_BONUS",0); - gGameExternalOptions.iExperiencedAPBonus = iniReader.ReadInteger("JA2 Tactical Settings","EXPERIENCED_AP_BONUS",0); - gGameExternalOptions.iExpertAPBonus = iniReader.ReadInteger("JA2 Tactical Settings","EXPERT_AP_BONUS",0); - gGameExternalOptions.iInsaneAPBonus = iniReader.ReadInteger("JA2 Tactical Settings","INSANE_AP_BONUS",0); - gGameExternalOptions.iPlayerAPBonus = iniReader.ReadInteger("JA2 Tactical Settings","PLAYER_AP_BONUS",0); + gGameExternalOptions.iEasyAPBonus = (INT8) iniReader.ReadInteger("JA2 Tactical Settings","NOVICE_AP_BONUS",0); + gGameExternalOptions.iExperiencedAPBonus = (INT8) iniReader.ReadInteger("JA2 Tactical Settings","EXPERIENCED_AP_BONUS",0); + gGameExternalOptions.iExpertAPBonus = (INT8) iniReader.ReadInteger("JA2 Tactical Settings","EXPERT_AP_BONUS",0); + gGameExternalOptions.iInsaneAPBonus = (INT8) iniReader.ReadInteger("JA2 Tactical Settings","INSANE_AP_BONUS",0); + gGameExternalOptions.iPlayerAPBonus = (INT8) iniReader.ReadInteger("JA2 Tactical Settings","PLAYER_AP_BONUS",0); // Sight range gGameExternalOptions.ubStraightSightRange = iniReader.ReadInteger("JA2 Tactical Settings","BASE_SIGHT_RANGE",13); @@ -451,7 +451,7 @@ void LoadGameExternalOptions() gGameExternalOptions.gfAllowSoldierToolTips = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_SOLDIER_TOOL_TIPS",0); // ShadoWarrior: Tooltip changes (start) - gGameExternalOptions.ubSoldierTooltipDetailLevel = iniReader.ReadInteger("JA2 Tactical Settings", "SOLDIER_TOOLTIP_DETAIL_LEVEL", 1); + gGameExternalOptions.ubSoldierTooltipDetailLevel = (UINT8) iniReader.ReadInteger("JA2 Tactical Settings", "SOLDIER_TOOLTIP_DETAIL_LEVEL", 1); gGameExternalOptions.fEnableDynamicSoldierTooltips = iniReader.ReadBoolean("JA2 Tactical Settings", "DYNAMIC_SOLDIER_TOOLTIPS", TRUE); gGameExternalOptions.fEnableSoldierTooltipLocation = iniReader.ReadBoolean("JA2 Tactical Settings", "SOLDIER_TOOLTIP_DISPLAY_LOCATION", TRUE); @@ -531,7 +531,7 @@ void LoadGameExternalOptions() gGameExternalOptions.fSellAll = iniReader.ReadBoolean("JA2 Gameplay Settings","CAN_SELL_ALT_LMB",FALSE); - gGameExternalOptions.iPriceModifier = iniReader.ReadInteger("JA2 Gameplay Settings","PRICE_MODIFIER",10); + gGameExternalOptions.iPriceModifier = (INT16) iniReader.ReadInteger("JA2 Gameplay Settings","PRICE_MODIFIER",10); // WDS - Option to turn off stealing @@ -582,10 +582,10 @@ void LoadGameExternalOptions() gGameExternalOptions.iPercentElitesBonusExpert = iniReader.ReadInteger("JA2 Gameplay Settings","EXPERT_ELITE_BONUS",25); gGameExternalOptions.iPercentElitesBonusInsane = iniReader.ReadInteger("JA2 Gameplay Settings","INSANE_ELITE_BONUS",50); - gGameExternalOptions.ubMinEnemyGroupSizeNovice = iniReader.ReadInteger("JA2 Gameplay Settings","NOVICE_MIN_ENEMY_GROUP_SIZE",3); - gGameExternalOptions.ubMinEnemyGroupSizeExperienced = iniReader.ReadInteger("JA2 Gameplay Settings","EXPERIENCED_MIN_ENEMY_GROUP_SIZE",4); - gGameExternalOptions.ubMinEnemyGroupSizeExpert = iniReader.ReadInteger("JA2 Gameplay Settings","EXPERT_MIN_ENEMY_GROUP_SIZE",6); - gGameExternalOptions.ubMinEnemyGroupSizeInsane = iniReader.ReadInteger("JA2 Gameplay Settings","INSANE_MIN_ENEMY_GROUP_SIZE",12); + gGameExternalOptions.ubMinEnemyGroupSizeNovice = (UINT8) iniReader.ReadInteger("JA2 Gameplay Settings","NOVICE_MIN_ENEMY_GROUP_SIZE",3); + gGameExternalOptions.ubMinEnemyGroupSizeExperienced = (UINT8) iniReader.ReadInteger("JA2 Gameplay Settings","EXPERIENCED_MIN_ENEMY_GROUP_SIZE",4); + gGameExternalOptions.ubMinEnemyGroupSizeExpert = (UINT8) iniReader.ReadInteger("JA2 Gameplay Settings","EXPERT_MIN_ENEMY_GROUP_SIZE",6); + gGameExternalOptions.ubMinEnemyGroupSizeInsane = (UINT8) iniReader.ReadInteger("JA2 Gameplay Settings","INSANE_MIN_ENEMY_GROUP_SIZE",12); gGameExternalOptions.ubEasyEnemyStartingAlertLevel = iniReader.ReadInteger("JA2 Gameplay Settings","NOVICE_ENEMY_STARTING_ALERT_LEVEL", 5); gGameExternalOptions.ubNormalEnemyStartingAlertLevel = iniReader.ReadInteger("JA2 Gameplay Settings","NORMAL_ENEMY_STARTING_ALERT_LEVEL", 20); @@ -717,7 +717,7 @@ BOOLEAN GetCDLocation( ) { UINT32 uiStrngLength = 0; CHAR8 zCdLocation[ SGPFILENAME_LEN ]; - UINT32 uiDriveType=0; + //UINT32 uiDriveType=0; UINT32 uiRetVal=0; //Do a crude check to make sure the Ja2.ini file is the right on @@ -889,7 +889,7 @@ BOOLEAN CheckIfGameCdromIsInCDromDrive() CHAR8 zCdFile[ SGPFILENAME_LEN ]; CHAR8 zCdromRootDrive[512]; - BOOLEAN fFailed = FALSE; + //BOOLEAN fFailed = FALSE; UINT32 uiVolumeReturnValue; UINT32 uiLastError = ERROR_SUCCESS; diff --git a/Standard Gaming Platform/MemMan.h b/Standard Gaming Platform/MemMan.h index de18d627..b9b57282 100644 --- a/Standard Gaming Platform/MemMan.h +++ b/Standard Gaming Platform/MemMan.h @@ -68,6 +68,7 @@ extern void ShutdownMemoryManager( void ); extern PTR MemReallocXDebug( PTR ptr, UINT32 size, const STR8 szCodeString, INT32 iLineNum, void *pSpecial ); #else #ifdef _DEBUG + #include //This is another debug feature. Not as sophistocated, but definately not the pig the extreme system is. //This system reports all memory allocations/deallocations in the debug output. #define MemAlloc( size ) MemAllocReal( (size), __FILE__, __LINE__ ) @@ -76,6 +77,10 @@ extern void ShutdownMemoryManager( void ); extern PTR MemAllocReal( UINT32 size, const STR8 , INT32 ); extern void MemFreeReal( PTR ptr, const STR8 , INT32 ); extern PTR MemReallocReal( PTR ptr, UINT32 size, const STR8 , INT32 ); + //void* ::operator new( size_t sz, const char* file, int line); + //void* ::operator new[]( size_t sz, const char *file, int line); + #define NEW new + #define new NEW(_NORMAL_BLOCK, __FILE__, __LINE__) #else //Release build verison #include diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 77f64fb7..c9b7328e 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -1958,7 +1958,7 @@ static void ARCreateMilitia( UINT8 mclass, INT32 i, INT16 sX, INT16 sY) ResetMortarsOnTeamCount(); if( !gpBattleGroup ) { - AssertMsg(0, "No battle group set while creating militia"); + //AssertMsg(0, "No battle group set while creating militia"); } if( mclass == SOLDIER_CLASS_ELITE_MILITIA ) diff --git a/Strategic/Scheduling.cpp b/Strategic/Scheduling.cpp index 0b695ace..5520bebb 100644 --- a/Strategic/Scheduling.cpp +++ b/Strategic/Scheduling.cpp @@ -182,6 +182,11 @@ void ProcessTacticalSchedule( UINT8 ubScheduleID ) #endif return; } + if (pSchedule->ubSoldierID == 0) + { + ScreenMsg( FONT_RED, MSG_INTERFACE, L"Schedule callback: Illegal soldier ID of %d.", pSchedule->ubSoldierID ); + return; + } //Validate the existance of the soldier. pSoldier = MercPtrs[ pSchedule->ubSoldierID ]; @@ -661,6 +666,11 @@ void AutoProcessSchedule( SCHEDULENODE *pSchedule, INT32 index ) pSoldier = MercPtrs[ pSchedule->ubSoldierID ]; + if (pSoldier->ubID == 0) + { + ScreenMsg( FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Soldier %s moved to away slot by schedule ID %d!", pSoldier->name, pSchedule->ubScheduleID ); + } + #ifdef JA2EDITOR if ( pSoldier->ubProfile != NO_PROFILE ) { diff --git a/Tactical/Enemy Soldier Save.cpp b/Tactical/Enemy Soldier Save.cpp index b0a6abe7..5d2b4d4c 100644 --- a/Tactical/Enemy Soldier Save.cpp +++ b/Tactical/Enemy Soldier Save.cpp @@ -282,7 +282,7 @@ BOOLEAN LoadEnemySoldiersFromTempFile() curr->pBasicPlacement->fPriorityExistance = TRUE; if( !curr->pDetailedPlacement ) { //need to upgrade the placement to detailed placement - curr->pDetailedPlacement = new (MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT )) SOLDIERCREATE_STRUCT; //(SOLDIERCREATE_STRUCT*)MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT ); + curr->pDetailedPlacement = new SOLDIERCREATE_STRUCT; //(SOLDIERCREATE_STRUCT*)MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT ); } //now replace the map pristine placement info with the temp map file version.. //memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, SIZEOF_SOLDIERCREATE_STRUCT ); @@ -475,7 +475,7 @@ BOOLEAN SaveEnemySoldiersToTempFile( INT16 sSectorX, INT16 sSectorY, INT8 bSecto { //need to upgrade the placement to detailed placement curr->pBasicPlacement->fDetailedPlacement = TRUE; // WDS - Clean up inventory handling - curr->pDetailedPlacement = new (MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT )) SOLDIERCREATE_STRUCT; //(SOLDIERCREATE_STRUCT*)MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT ); + curr->pDetailedPlacement = new SOLDIERCREATE_STRUCT; //(SOLDIERCREATE_STRUCT*)MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT ); //memset( curr->pDetailedPlacement, 0, SIZEOF_SOLDIERCREATE_STRUCT ); curr->pDetailedPlacement->initialize(); } @@ -1009,7 +1009,7 @@ BOOLEAN NewWayOfLoadingEnemySoldiersFromTempFile() curr->pBasicPlacement->fPriorityExistance = TRUE; if( !curr->pDetailedPlacement ) { //need to upgrade the placement to detailed placement - curr->pDetailedPlacement = new (MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT )) SOLDIERCREATE_STRUCT; //(SOLDIERCREATE_STRUCT*)MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT ); + curr->pDetailedPlacement = new SOLDIERCREATE_STRUCT; //(SOLDIERCREATE_STRUCT*)MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT ); } //now replace the map pristine placement info with the temp map file version.. //memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, SIZEOF_SOLDIERCREATE_STRUCT ); @@ -1582,7 +1582,7 @@ BOOLEAN NewWayOfSavingEnemyAndCivliansToTempFile( INT16 sSectorX, INT16 sSectorY { //need to upgrade the placement to detailed placement curr->pBasicPlacement->fDetailedPlacement = TRUE; // WDS - Clean up inventory handling - curr->pDetailedPlacement = new (MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT )) SOLDIERCREATE_STRUCT; + curr->pDetailedPlacement = new SOLDIERCREATE_STRUCT; curr->pDetailedPlacement->initialize(); //memset( curr->pDetailedPlacement, 0, SIZEOF_SOLDIERCREATE_STRUCT ); } diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index 59aec155..dda283bd 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -4626,6 +4626,18 @@ BOOLEAN MakeSoldierTurn( SOLDIERTYPE *pSoldier, INT16 sXPos, INT16 sYPos ) INT16 sFacingDir, sAPCost, sAPCostToReady; UINT16 usAnimState; + // Make sure the merc is not collapsed! + if (!IsValidStance(pSoldier, ANIM_CROUCH) ) + { + if ( pSoldier->bCollapsed && pSoldier->bBreath < OKBREATH ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, gzLateLocalizedString[ 4 ], pSoldier->name ); + } + + return FALSE; + } + + // Get direction from mouse pos sFacingDir = GetDirectionFromXY( sXPos, sYPos, pSoldier ); @@ -4730,7 +4742,8 @@ UINT32 UIHandleLCLook( UI_EVENT *pUIEvent ) if ( MakeSoldierTurn( pSoldier, sXPos, sYPos ) ) { - SetUIBusy( pSoldier->ubID ); + // 0verhaul: Why do we set UI busy for a single soldier but not for a group of selected soldiers? + //SetUIBusy( pSoldier->ubID ); } } return( GAME_SCREEN ); diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index 03ca11eb..125b4cd6 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -978,33 +978,50 @@ void ChooseGrenadesForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bGrena return; // special mortar shell handling - if (bGrenadeClass == MORTAR_GRENADE_CLASS && itemMortar > 0 ) + if (bGrenadeClass == MORTAR_GRENADE_CLASS) { - usItem = PickARandomLaunchable ( itemMortar ); - if ( usItem > 0 ) + // 0verhaul: itemMortar can be 0 if the previous function + // 1) failed to find a Mortar that the soldier would want to use (due to XML prefs) + // 2) randomly chose not to supply a Mortar (always possible, even if choices exist). + // Since we should not go beyond this point in the case where Mortar Rounds are desired, + // return here in any case + if (itemMortar > 0 ) { - CreateItems( usItem, (INT8) (80 + Random(21)), bGrenades, &Object ); - Object.fFlags |= OBJECT_UNDROPPABLE; - PlaceObjectInSoldierCreateStruct( pp, &Object ); - - return; - } - } - - // special rpg rocket handling - if (bGrenadeClass == RPG_GRENADE_CLASS && itemRPG > 0 ) - { - usItem = PickARandomLaunchable ( itemRPG ); - if ( usItem > 0 ) - { - for ( int i = 0; i < bGrenades; i++ ) + usItem = PickARandomLaunchable ( itemMortar ); + if ( usItem > 0 ) { - CreateItem( usItem, (INT8) (70 + Random(31)), &Object ); + CreateItems( usItem, (INT8) (80 + Random(21)), bGrenades, &Object ); Object.fFlags |= OBJECT_UNDROPPABLE; PlaceObjectInSoldierCreateStruct( pp, &Object ); } - return; } + + return; + } + + // special rpg rocket handling + if (bGrenadeClass == RPG_GRENADE_CLASS) + { + // 0verhaul: itemRPG can be 0 if the previous function + // 1) failed to find an RPG that the soldier would want to use (due to XML prefs) + // 2) randomly chose not to supply an RPG (always possible, even if choices exist). + // Since we should not go beyond this point in the case where RPG grenades are desired, + // return here in any case + if (itemRPG > 0 ) + { + usItem = PickARandomLaunchable ( itemRPG ); + if ( usItem > 0 ) + { + for ( int i = 0; i < bGrenades; i++ ) + { + CreateItem( usItem, (INT8) (70 + Random(31)), &Object ); + Object.fFlags |= OBJECT_UNDROPPABLE; + PlaceObjectInSoldierCreateStruct( pp, &Object ); + } + + } + } + return; } Assert( bGrenadeClass <= 11 ); diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index d3c68ca3..f5bdb469 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -1096,7 +1096,16 @@ void AStarPathfinder::ExecuteAStarLogic() //movementG = (INT16) CalcG( &prevCost); //movementG = CalcAP( movementG, direction); - movementG = terrainCost * 100; + //movementG = terrainCost * 100; + movementG = terrainCost; + // Favor continuing in the same direction by increasing the cost for changing direction + int ParentParentNode = GetAStarParent( ParentNode); + if ((ParentParentNode != -1 && + ParentNode - ParentParentNode != CurrentNode - ParentNode) || + (ParentParentNode == -1 && (direction & 1) ) ) + { + movementG++; + } INT16 AStarG = baseGCost + movementG; //if the node is more costly in this path than in another open path, continue @@ -1629,7 +1638,7 @@ int AStarPathfinder::CalcH() int x = abs(n1->x - n2->x); int y = abs(n1->y - n2->y); -#if 0 +#if 1 if (x >= y) { return this->travelcostDiag * y + this->travelcostOrth * (x-y); @@ -4703,3 +4712,4 @@ UINT8 DoorTravelCost( SOLDIERTYPE * pSoldier, INT32 iGridNo, UINT8 ubMovementCos return( InternalDoorTravelCost( pSoldier, iGridNo, ubMovementCost, fReturnPerceivedValue, piDoorGridNo, FALSE ) ); } + diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 476ef846..7c2e0480 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -252,8 +252,11 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) // CODE: SPECIAL MOVE CLIMB UP ROOF EVENT - // re-enable sight - gTacticalStatus.uiFlags &= (~DISALLOW_SIGHT); + // Moved here because this represents "already on the roof", so breath collapses and interrupts should + // keep the soldier on the roof where he belongs + // Move merc up specific height + SetSoldierHeight( pSoldier, (FLOAT)50 ); + { INT16 sXPos, sYPos; @@ -261,6 +264,10 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) ConvertMapPosToWorldTileCenter( pSoldier->sTempNewGridNo, &sXPos, &sYPos ); EVENT_SetSoldierPosition( pSoldier, (FLOAT)sXPos, (FLOAT)sYPos ); } + + // re-enable sight + gTacticalStatus.uiFlags &= (~DISALLOW_SIGHT); + // Move two CC directions EVENT_SetSoldierDirection( pSoldier, gTwoCCDirection[ pSoldier->ubDirection ] ); @@ -273,9 +280,6 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) usUIMovementMode = GetMoveStateBasedOnStance( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ); pSoldier->usUIMovementMode = usUIMovementMode; - // Move merc up specific height - SetSoldierHeight( pSoldier, (FLOAT)50 ); - // ATE: Change interface level..... // CJC: only if we are a player merc if (pSoldier->bTeam == gbPlayerNum) @@ -326,6 +330,9 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) // Move two C directions EVENT_SetSoldierDirection( pSoldier, gTwoCDirection[ pSoldier->ubDirection ] ); + // Remove the roof marker + HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, FALSE, TRUE ); + EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubDirection ); // Adjust height SetSoldierHeight( pSoldier, (FLOAT)gClimbDownRoofStartDist[ pSoldier->ubBodyType ] ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 51589a0a..9fe0ecc0 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -3000,7 +3000,10 @@ void InternalSetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight, BOOLEAN return; } - if ( pSoldier->sHeightAdjustment > 0 ) + // 0verhaul: Changed this to half the wall height. During a climb up, a soldier's height increases to about 8, then falls + // to near 0 before being set to 50 at the end. The animation offsets should probably be changed to make this unnecessary + // but this is good enough to keep him from bouncing between level 1 and level 0 (and also triggering weird sight bugs). + if ( pSoldier->sHeightAdjustment > 25 ) { pSoldier->bLevel = SECOND_LEVEL; @@ -6084,6 +6087,7 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) else { pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT ); + pSoldier->fGettingHit = FALSE; } } else if ( pSoldier->fGettingHit == 2 ) @@ -6100,7 +6104,7 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) //ReduceAttackBusyCount( ); //FREEUP GETTING HIT FLAG - // pSoldier->fGettingHit = FALSE; + pSoldier->fGettingHit = FALSE; } } @@ -11598,6 +11602,11 @@ void ChangeToFlybackAnimation( SOLDIERTYPE *pSoldier, UINT8 ubDirection ) // Remove any previous actions pSoldier->ubPendingAction = NO_PENDING_ACTION; + // Since we're manually setting our path, we have to reset these @#$@# flags too. Otherwise we don't reach the + // destination a lot of the time + pSoldier->fPastXDest = 0; + pSoldier->fPastYDest = 0; + // Set path.... pSoldier->usPathDataSize = 0; pSoldier->usPathIndex = 0; @@ -11638,6 +11647,11 @@ void ChangeToFallbackAnimation( SOLDIERTYPE *pSoldier, UINT8 ubDirection ) // Remove any previous actions pSoldier->ubPendingAction = NO_PENDING_ACTION; + // Since we're manually setting our path, we have to reset these @#$@# flags too. Otherwise we don't reach the + // destination a lot of the time + pSoldier->fPastXDest = 0; + pSoldier->fPastYDest = 0; + // Set path.... pSoldier->usPathDataSize = 0; pSoldier->usPathIndex = 0; diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index ba1de8bf..08a62456 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -751,7 +751,7 @@ SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 * UINT8 ubSectorID; ubSectorID = GetAutoResolveSectorID(); // WDS - Clean up inventory handling - pSoldier = new (MemAlloc( SIZEOF_SOLDIERTYPE )) SOLDIERTYPE; //(SOLDIERTYPE*)MemAlloc( SIZEOF_SOLDIERTYPE ); + pSoldier = new SOLDIERTYPE; //(SOLDIERTYPE*)MemAlloc( SIZEOF_SOLDIERTYPE ); if( !pSoldier ) return NULL; //memcpy( pSoldier, &Soldier, SIZEOF_SOLDIERTYPE ); @@ -2205,7 +2205,7 @@ SOLDIERTYPE* ReserveTacticalSoldierForAutoresolve( UINT8 ubSoldierClass ) // WDS - Clean up inventory handling //Allocate and copy the soldier - pSoldier = new (MemAlloc( SIZEOF_SOLDIERTYPE )) SOLDIERTYPE; //(SOLDIERTYPE*)MemAlloc( SIZEOF_SOLDIERTYPE ); + pSoldier = new SOLDIERTYPE; //(SOLDIERTYPE*)MemAlloc( SIZEOF_SOLDIERTYPE ); if( !pSoldier ) return NULL; // memcpy( pSoldier, MercPtrs[ i ], SIZEOF_SOLDIERTYPE ); @@ -2360,7 +2360,7 @@ SOLDIERTYPE* ReserveTacticalMilitiaSoldierForAutoresolve( UINT8 ubSoldierClass ) // WDS - Clean up inventory handling //Allocate and copy the soldier - pSoldier = new (MemAlloc( SIZEOF_SOLDIERTYPE )) SOLDIERTYPE; //(SOLDIERTYPE*)MemAlloc( SIZEOF_SOLDIERTYPE ); + pSoldier = new SOLDIERTYPE; //(SOLDIERTYPE*)MemAlloc( SIZEOF_SOLDIERTYPE ); if( !pSoldier ) return NULL; // memcpy( pSoldier, MercPtrs[ i ], SIZEOF_SOLDIERTYPE ); diff --git a/Tactical/Soldier Tile.cpp b/Tactical/Soldier Tile.cpp index 6fbf6577..542b8de3 100644 --- a/Tactical/Soldier Tile.cpp +++ b/Tactical/Soldier Tile.cpp @@ -516,6 +516,12 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) { RESETTIMECOUNTER( pSoldier->NextTileCounter, NEXT_TILE_CHECK_DELAY ); + // ATE: Allow path to exit grid! + if ( pSoldier->ubWaitActionToDo == 1 && gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO ) + { + gfPlotPathToExitGrid = TRUE; + } + // Get direction from gridno... bCauseDirection = (INT8)GetDirectionToGridNoFromGridNo( pSoldier->sGridNo, pSoldier->sDelayedMovementCauseGridNo ); @@ -534,6 +540,7 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) SetFinalTile( pSoldier, pSoldier->sGridNo, TRUE ); pSoldier->fDelayedMovement = FALSE; } + gfPlotPathToExitGrid = FALSE; return( TRUE ); } @@ -595,15 +602,7 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) sCheckGridNo = pSoldier->sFinalDestination; } - // Try another path to destination - // ATE: Allow path to exit grid! - if ( pSoldier->ubWaitActionToDo == 1 && gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO ) - { - gfPlotPathToExitGrid = TRUE; - } - sCost = (INT16) FindBestPath( pSoldier, sCheckGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, fFlags ); - gfPlotPathToExitGrid = FALSE; // Can we get there if ( sCost > 0 ) @@ -617,15 +616,8 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) { // Try to path around everyone except dest person - if ( pSoldier->ubWaitActionToDo == 1 && gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO ) - { - gfPlotPathToExitGrid = TRUE; - } - sCost = (INT16) FindBestPath( pSoldier, sCheckGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, PATH_IGNORE_PERSON_AT_DEST ); - gfPlotPathToExitGrid = FALSE; - // Is the next tile in this new path blocked too? sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( (UINT8)guiPathingData[ 0 ] ) ); @@ -633,6 +625,7 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) // now working with a path which does not go through people pSoldier->ubDelayedMovementFlags &= (~DELAYED_MOVEMENT_FLAG_PATH_THROUGH_PEOPLE); + } else { @@ -646,12 +639,6 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) // Are we clear? if ( bPathBlocked == MOVE_TILE_CLEAR ) { - // Go for it path! - if ( pSoldier->ubWaitActionToDo == 1 && gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO ) - { - gfPlotPathToExitGrid = TRUE; - } - //pSoldier->fDelayedMovement = FALSE; // ATE: THis will get set in EENT_GetNewSoldierPath.... pSoldier->usActionData = sCheckGridNo; @@ -659,8 +646,8 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) pSoldier->bPathStored = FALSE; EVENT_GetNewSoldierPath( pSoldier, sCheckGridNo, pSoldier->usUIMovementMode ); - gfPlotPathToExitGrid = FALSE; + gfPlotPathToExitGrid = FALSE; return( TRUE ); } } @@ -707,6 +694,7 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) NPCReachedDestination( pSoldier, FALSE ); pSoldier->bNextAction = AI_ACTION_WAIT; pSoldier->usNextActionData = 500; + gfPlotPathToExitGrid = FALSE; return( TRUE ); } } @@ -743,6 +731,8 @@ BOOLEAN HandleNextTileWaiting( SOLDIERTYPE *pSoldier ) } } } + + gfPlotPathToExitGrid = FALSE; return( TRUE ); } diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 7b1b6f32..e568dd92 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -250,7 +250,7 @@ void QueryTBLeftButton( UINT32 *puiNewEvent ) SOLDIERTYPE *pSoldier; UINT16 usMapPos; static BOOLEAN fClickHoldIntercepted = FALSE; - BOOLEAN fOnInterTile = FALSE; + //BOOLEAN fOnInterTile = FALSE; static BOOLEAN fCanCheckForSpeechAdvance = FALSE; static INT16 sMoveClickGridNo = 0; @@ -2347,6 +2347,17 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) BOOLEAN fNearLowerLevel; INT8 bDirection; + // Make sure the merc is not collapsed! + if (!IsValidStance(pjSoldier, ANIM_CROUCH) ) + { + if ( pjSoldier->bCollapsed && pjSoldier->bBreath < OKBREATH ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, gzLateLocalizedString[ 4 ], pjSoldier->name ); + } + + break; + } + GetMercClimbDirection( pjSoldier->ubID, &fNearLowerLevel, &fNearHeigherLevel ); if ( fNearLowerLevel ) diff --git a/TacticalAI/AIMain.cpp b/TacticalAI/AIMain.cpp index c13d0819..52aac376 100644 --- a/TacticalAI/AIMain.cpp +++ b/TacticalAI/AIMain.cpp @@ -51,6 +51,7 @@ #include "Quests.h" #include "Campaign Types.h" #include "Queen Command.h" +#include "cheats.h" #endif extern void PauseAITemporarily( void ); @@ -1922,6 +1923,11 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier) // to get here, we MUST have an action selected, but not in progress... + if ( CHEATER_CHEAT_LEVEL( ) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Soldier %d decides action %d with data %d", pSoldier->ubID, pSoldier->bAction, pSoldier->usActionData ); + } + // see if we can afford to do this action if (IsActionAffordable(pSoldier)) { diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 24b9a476..3c894437 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -2283,7 +2283,8 @@ INT16 RoamingRange(SOLDIERTYPE *pSoldier, INT16 * pusFromGridNo) else { return( 5 ); - } case ONGUARD: return( 5 ); + } + case ONGUARD: return( 5 ); case CLOSEPATROL: return( 15 ); case RNDPTPATROL: case POINTPATROL: return(10 ); // from nextPatrolGrid, not whereIWas @@ -2305,6 +2306,7 @@ INT16 RoamingRange(SOLDIERTYPE *pSoldier, INT16 * pusFromGridNo) } case SEEKENEMY: *pusFromGridNo = pSoldier->sGridNo; // from current position! return(MAX_ROAMING_RANGE); + case SNIPER: return ( 5 ); default: #ifdef BETAVERSION sprintf(tempstr,"%s has invalid orders = %d",pSoldier->name,pSoldier->bOrders); diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 35cead57..652fabde 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -924,7 +924,7 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) // CLIMB A BUILDING //////////////////////////////////////////////////////////////////////////// - if (!fCivilian) + if (!fCivilian && pSoldier->bLastAction != AI_ACTION_CLIMB_ROOF) { iChance = 10 + pSoldier->bBypassToGreen; @@ -965,30 +965,40 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier) // reduce chance if breath is down //iChance -= (100 - pSoldier->bBreath); // don't care + // This is the chance that we want to be on the roof. If already there, invert the chance to see if we want back + // down + if (pSoldier->bLevel > 0) + { + iChance = 100 - iChance; + } + if ((INT16) PreRandom(100) < iChance) { + BOOLEAN fUp = FALSE; if ( pSoldier->bLevel == 0 ) { - BOOLEAN fUp = TRUE; - if (pSoldier->bLevel > 0 ) - fUp = FALSE; + fUp = TRUE; + } + else if (pSoldier->bLevel > 0 ) + { + fUp = FALSE; + } - if ( CanClimbFromHere ( pSoldier, fUp ) ) + if ( CanClimbFromHere ( pSoldier, fUp ) ) + { + DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing roof",pSoldier->ubID) ); + return( AI_ACTION_CLIMB_ROOF ); + } + else + { + pSoldier->usActionData = FindClosestClimbPoint(pSoldier, fUp ); + // Added the check here because sniper militia who are locked inside of a building without keys + // will still have a >100% chance to want to climb, which means an infinite loop. In fact, any + // time a move is desired, there probably also will be a need to check for a path. + if ( pSoldier->usActionData != NOWHERE && + LegalNPCDestination(pSoldier,pSoldier->usActionData,ENSURE_PATH,WATEROK, 0 )) { - DebugMsg ( TOPIC_JA2AI , DBG_LEVEL_3 , String("Soldier %d is climbing roof",pSoldier->ubID) ); - return( AI_ACTION_CLIMB_ROOF ); - } - else - { - pSoldier->usActionData = FindClosestClimbPoint(pSoldier, fUp ); - // Added the check here because sniper militia who are locked inside of a building without keys - // will still have a >100% chance to want to climb, which means an infinite loop. In fact, any - // time a move is desired, there probably also will be a need to check for a path. - if ( pSoldier->usActionData != NOWHERE && - LegalNPCDestination(pSoldier,pSoldier->usActionData,ENSURE_PATH,WATEROK, 0 )) - { - return( AI_ACTION_MOVE_TO_CLIMB ); - } + return( AI_ACTION_MOVE_TO_CLIMB ); } } } diff --git a/TileEngine/Exit Grids.cpp b/TileEngine/Exit Grids.cpp index 92e4d2d9..736807f9 100644 --- a/TileEngine/Exit Grids.cpp +++ b/TileEngine/Exit Grids.cpp @@ -299,8 +299,6 @@ UINT16 FindGridNoFromSweetSpotCloseToExitGrid( SOLDIERTYPE *pSoldier, INT16 sSwe //(use the fake soldier and the pathing settings) FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, PATH_THROUGH_PEOPLE ); - gfPlotPathToExitGrid = FALSE; - uiLowestRange = 999999; for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ ) @@ -337,6 +335,8 @@ UINT16 FindGridNoFromSweetSpotCloseToExitGrid( SOLDIERTYPE *pSoldier, INT16 sSwe } } } + + gfPlotPathToExitGrid = FALSE; gubNPCAPBudget = ubSaveNPCAPBudget; gubNPCDistLimit = ubSaveNPCDistLimit; diff --git a/gamescreen.cpp b/gamescreen.cpp index 20feea77..6567fd36 100644 --- a/gamescreen.cpp +++ b/gamescreen.cpp @@ -583,7 +583,7 @@ UINT32 MainGameScreenHandle(void) // Check timer that could have been set to do anything CheckCustomizableTimer(); - // HAndle physics engine + // Handle physics engine SimulateWorld( ); // Handle strategic engine