From dd0d6ea66da9384e635245b3e00de5d2926da17a Mon Sep 17 00:00:00 2001 From: Overhaul Date: Sat, 20 Oct 2007 08:09:03 +0000 Subject: [PATCH] Fixed appearance of save game corruption when a game with different inventory set is loaded. Note that such games can still cause odd behavior and CTD's Fixed old-style behavior when creating a soldier struct (caused memory corruption and leaks) Fixed soldiers unable to fire if an error happened while firing the off-hand weapon Fix animation system problems with soldiers interrupted while changing stance, such as twitching and not being able to navigate around obstacles Prevent splitting money while an item is in the hand, which would result in losing the original item Fix reverse of X,Y when checking visibility through a roof, which could make soldiers falsely visible/invisible Prevent militia from simply waiting on the border when enemies are known to be in the sector Fix stack overflow for soldier inventory debug display Optimized A* pathing and cleaned up VS2K5 specific code AI can now use all climb points for climbable buildings, not just a random few Fix to not allow flat roof butted against slanted roof to be identified as a climb point Prevent soldiers who cause an interrupt from a special action from continuing on their path after being interrupted Prevent soldiers who enter or leave deep water from moving a space too far and turning back Prevent soldiers from trying to crouch while in shallow water Make soldier turn in the closest direction when he must get up from prone and turn then fall back to prone Allow a soldier to swat for a tile before returning to prone if unable to return to prone immediately after a turn Fix civilians that should show up in a sector, missing because of others that aren't Fix a memset on a C++ class Place Skyrider in a sector near his helecopter once available and the helicopter is present Fix soldier corruption if too many shots burst-spread into too few tiles Include both first and last tile in spread when some locations must be skipped Fix to shoot at last location in burst Fix to remove other previous spread locations when calculating a new spread Fix to reload or rechamber off-hand when necessary, such as having a pistol shotgun in that hand git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1532 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- Tactical/Animation Control.cpp | 12 + Tactical/Animation Data.cpp | 6 +- Tactical/Animation Data.h | 4 +- Tactical/Arms Dealer Init.cpp | 8 +- Tactical/Auto Bandage.cpp | 4 +- Tactical/BinaryHeap.hpp | 30 +- Tactical/Enemy Soldier Save.cpp | 4 +- Tactical/Handle Items.cpp | 9 +- Tactical/Handle UI Plan.cpp | 4 +- Tactical/Handle UI.cpp | 7 +- Tactical/Interface Dialogue.cpp | 6 +- Tactical/Interface Items.cpp | 14 +- Tactical/Items.cpp | 37 +- Tactical/Items.h | 2 + Tactical/LOS.cpp | 4 +- Tactical/Militia Control.cpp | 5 +- Tactical/Overhead.cpp | 23 +- Tactical/PATHAI.H | 132 ++-- Tactical/PATHAI.cpp | 1055 ++++++++++++++++++++----------- Tactical/Points.cpp | 33 +- Tactical/Rain.h | 1 + Tactical/Rotting Corpses.cpp | 4 +- Tactical/Soldier Ani.cpp | 18 +- Tactical/Soldier Control.cpp | 138 ++-- Tactical/Soldier Control.h | 9 +- Tactical/Soldier Init List.cpp | 550 ++++++++-------- Tactical/Soldier Profile.cpp | 7 +- Tactical/Spread Burst.cpp | 11 +- Tactical/Tactical Save.cpp | 2 +- Tactical/TeamTurns.cpp | 8 +- Tactical/Turn Based Input.cpp | 4 +- Tactical/UI Cursors.cpp | 5 +- Tactical/Weapons.cpp | 18 +- Tactical/fov.cpp | 7 +- Tactical/opplist.cpp | 4 +- 35 files changed, 1322 insertions(+), 863 deletions(-) diff --git a/Tactical/Animation Control.cpp b/Tactical/Animation Control.cpp index 85cbf6f2..ee527d8f 100644 --- a/Tactical/Animation Control.cpp +++ b/Tactical/Animation Control.cpp @@ -2663,6 +2663,8 @@ void InitAnimationSurfacesPerBodytype( ) gubAnimSurfaceIndex[ FATCIV ][ GENERIC_HIT_DEATHTWITCHNB ] = FATMANDIE; gubAnimSurfaceIndex[ FATCIV ][ GENERIC_HIT_DEATHTWITCHB ] = FATMANDIE; gubAnimSurfaceIndex[ FATCIV ][ FALLFORWARD_FROMHIT_STAND ] = FATMANDIE; + gubAnimSurfaceIndex[ FATCIV ][ FALLFORWARD_FROMHIT_CROUCH ] = FATMANDIE; + gubAnimSurfaceIndex[ FATCIV ][ ENDFALLFORWARD_FROMHIT_CROUCH ] = FATMANDIE; gubAnimSurfaceIndex[ FATCIV ][ FALLFORWARD_HITDEATH_STOP ] = FATMANDIE; gubAnimSurfaceIndex[ FATCIV ][ STAND_FALLFORWARD_STOP ] = FATMANDIE; gubAnimSurfaceIndex[ FATCIV ][ GENERIC_HIT_DEATH ] = FATMANDIE; @@ -2699,6 +2701,8 @@ void InitAnimationSurfacesPerBodytype( ) gubAnimSurfaceIndex[ MANCIV ][ GENERIC_HIT_DEATHTWITCHNB ] = MANCIVDIE; gubAnimSurfaceIndex[ MANCIV ][ GENERIC_HIT_DEATHTWITCHB ] = MANCIVDIE; gubAnimSurfaceIndex[ MANCIV ][ FALLFORWARD_FROMHIT_STAND ] = MANCIVDIE; + gubAnimSurfaceIndex[ MANCIV ][ FALLFORWARD_FROMHIT_CROUCH ] = MANCIVDIE; + gubAnimSurfaceIndex[ MANCIV ][ ENDFALLFORWARD_FROMHIT_CROUCH ] = MANCIVDIE; gubAnimSurfaceIndex[ MANCIV ][ FALLFORWARD_HITDEATH_STOP ] = MANCIVDIE; gubAnimSurfaceIndex[ MANCIV ][ STAND_FALLFORWARD_STOP ] = MANCIVDIE; gubAnimSurfaceIndex[ MANCIV ][ GENERIC_HIT_DEATH ] = MANCIVDIE; @@ -2734,6 +2738,8 @@ void InitAnimationSurfacesPerBodytype( ) gubAnimSurfaceIndex[ MINICIV ][ GENERIC_HIT_DEATHTWITCHNB ] = MINICIVDIE; gubAnimSurfaceIndex[ MINICIV ][ GENERIC_HIT_DEATHTWITCHB ] = MINICIVDIE; gubAnimSurfaceIndex[ MINICIV ][ FALLFORWARD_FROMHIT_STAND ] = MINICIVDIE; + gubAnimSurfaceIndex[ MINICIV ][ FALLFORWARD_FROMHIT_CROUCH ] = MINICIVDIE; + gubAnimSurfaceIndex[ MINICIV ][ ENDFALLFORWARD_FROMHIT_CROUCH ] = MINICIVDIE; gubAnimSurfaceIndex[ MINICIV ][ FALLFORWARD_HITDEATH_STOP ] = MINICIVDIE; gubAnimSurfaceIndex[ MINICIV ][ STAND_FALLFORWARD_STOP ] = MINICIVDIE; gubAnimSurfaceIndex[ MINICIV ][ GENERIC_HIT_DEATH ] = MINICIVDIE; @@ -2771,6 +2777,8 @@ void InitAnimationSurfacesPerBodytype( ) gubAnimSurfaceIndex[ DRESSCIV ][ GENERIC_HIT_DEATHTWITCHNB ] = DRESSCIVDIE; gubAnimSurfaceIndex[ DRESSCIV ][ GENERIC_HIT_DEATHTWITCHB ] = DRESSCIVDIE; gubAnimSurfaceIndex[ DRESSCIV ][ FALLFORWARD_FROMHIT_STAND ] = DRESSCIVDIE; + gubAnimSurfaceIndex[ DRESSCIV ][ FALLFORWARD_FROMHIT_CROUCH ] = DRESSCIVDIE; + gubAnimSurfaceIndex[ DRESSCIV ][ ENDFALLFORWARD_FROMHIT_CROUCH ] = DRESSCIVDIE; gubAnimSurfaceIndex[ DRESSCIV ][ FALLFORWARD_HITDEATH_STOP ] = DRESSCIVDIE; gubAnimSurfaceIndex[ DRESSCIV ][ STAND_FALLFORWARD_STOP ] = DRESSCIVDIE; gubAnimSurfaceIndex[ DRESSCIV ][ GENERIC_HIT_DEATH ] = DRESSCIVDIE; @@ -2798,6 +2806,8 @@ void InitAnimationSurfacesPerBodytype( ) gubAnimSurfaceIndex[ HATKIDCIV ][ GENERIC_HIT_DEATHTWITCHNB ] = HATKIDCIVDIE; gubAnimSurfaceIndex[ HATKIDCIV ][ GENERIC_HIT_DEATHTWITCHB ] = HATKIDCIVDIE; gubAnimSurfaceIndex[ HATKIDCIV ][ FALLFORWARD_FROMHIT_STAND ] = HATKIDCIVDIE; + gubAnimSurfaceIndex[ HATKIDCIV ][ FALLFORWARD_FROMHIT_CROUCH ] = HATKIDCIVDIE; + gubAnimSurfaceIndex[ HATKIDCIV ][ ENDFALLFORWARD_FROMHIT_CROUCH ] = HATKIDCIVDIE; gubAnimSurfaceIndex[ HATKIDCIV ][ FALLFORWARD_HITDEATH_STOP ] = HATKIDCIVDIE; gubAnimSurfaceIndex[ HATKIDCIV ][ STAND_FALLFORWARD_STOP ] = HATKIDCIVDIE; gubAnimSurfaceIndex[ HATKIDCIV ][ GENERIC_HIT_DEATH ] = HATKIDCIVDIE; @@ -2839,6 +2849,8 @@ void InitAnimationSurfacesPerBodytype( ) gubAnimSurfaceIndex[ KIDCIV ][ GENERIC_HIT_DEATHTWITCHNB ] = KIDCIVDIE; gubAnimSurfaceIndex[ KIDCIV ][ GENERIC_HIT_DEATHTWITCHB ] = KIDCIVDIE; gubAnimSurfaceIndex[ KIDCIV ][ FALLFORWARD_FROMHIT_STAND ] = KIDCIVDIE; + gubAnimSurfaceIndex[ KIDCIV ][ FALLFORWARD_FROMHIT_CROUCH ] = KIDCIVDIE; + gubAnimSurfaceIndex[ KIDCIV ][ ENDFALLFORWARD_FROMHIT_CROUCH ] = KIDCIVDIE; gubAnimSurfaceIndex[ KIDCIV ][ FALLFORWARD_HITDEATH_STOP ] = KIDCIVDIE; gubAnimSurfaceIndex[ KIDCIV ][ STAND_FALLFORWARD_STOP ] = KIDCIVDIE; gubAnimSurfaceIndex[ KIDCIV ][ GENERIC_HIT_DEATH ] = KIDCIVDIE; diff --git a/Tactical/Animation Data.cpp b/Tactical/Animation Data.cpp index 5e384697..d152cf50 100644 --- a/Tactical/Animation Data.cpp +++ b/Tactical/Animation Data.cpp @@ -778,7 +778,7 @@ BOOLEAN DeInitAnimationSystem( ) -STRUCTURE_FILE_REF *InternalGetAnimationStructureRef( UINT16 usSoldierID, UINT16 usSurfaceIndex, UINT16 usAnimState, BOOLEAN fUseAbsolute ) +STRUCTURE_FILE_REF *InternalGetAnimationStructureRef( UINT8 usSoldierID, UINT16 usSurfaceIndex, UINT16 usAnimState, BOOLEAN fUseAbsolute ) { INT8 bStructDataType; @@ -807,13 +807,13 @@ STRUCTURE_FILE_REF *InternalGetAnimationStructureRef( UINT16 usSoldierID, UINT16 } -STRUCTURE_FILE_REF *GetAnimationStructureRef( UINT16 usSoldierID, UINT16 usSurfaceIndex, UINT16 usAnimState ) +STRUCTURE_FILE_REF *GetAnimationStructureRef( UINT8 usSoldierID, UINT16 usSurfaceIndex, UINT16 usAnimState ) { return( InternalGetAnimationStructureRef( usSoldierID, usSurfaceIndex, usAnimState, FALSE ) ); } -STRUCTURE_FILE_REF *GetDefaultStructureRef( UINT16 usSoldierID ) +STRUCTURE_FILE_REF *GetDefaultStructureRef( UINT8 usSoldierID ) { return( gAnimStructureDatabase[ MercPtrs[ usSoldierID ]->ubBodyType ][ DEFAULT_STRUCT ].pStructureFileRef ); } diff --git a/Tactical/Animation Data.h b/Tactical/Animation Data.h index e9f031e6..cfddf809 100644 --- a/Tactical/Animation Data.h +++ b/Tactical/Animation Data.h @@ -565,8 +565,8 @@ void ClearAnimationSurfacesUsageHistory( UINT16 usSoldierID ); void DeleteAnimationProfiles( ); BOOLEAN LoadAnimationProfiles( ); -STRUCTURE_FILE_REF *GetAnimationStructureRef( UINT16 usSoldierID, UINT16 usSurfaceIndex, UINT16 usAnimState ); -STRUCTURE_FILE_REF *GetDefaultStructureRef( UINT16 usSoldierID ); +STRUCTURE_FILE_REF *GetAnimationStructureRef( UINT8 usSoldierID, UINT16 usSurfaceIndex, UINT16 usAnimState ); +STRUCTURE_FILE_REF *GetDefaultStructureRef( UINT8 usSoldierID ); // Profile data extern ANIM_PROF *gpAnimProfiles; diff --git a/Tactical/Arms Dealer Init.cpp b/Tactical/Arms Dealer Init.cpp index 9e5b8540..40078f19 100644 --- a/Tactical/Arms Dealer Init.cpp +++ b/Tactical/Arms Dealer Init.cpp @@ -198,8 +198,8 @@ void ShutDownArmsDealers() //loop through all the item types for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ ) { - if ( Item[usItemIndex].usItemClass == 0 ) - break; + //if ( Item[usItemIndex].usItemClass == 0 ) + // break; if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced > 0 ) { FreeSpecialItemArray( &gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ] ); @@ -294,8 +294,8 @@ BOOLEAN LoadArmsDealerInventoryFromSavedGameFile( HWFILE hFile, BOOLEAN fInclude //loop through this dealer's individual items for(usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ ) { - if ( Item[usItemIndex].usItemClass == 0 ) - break; + //if ( Item[usItemIndex].usItemClass == 0 ) + // break; //if there are any elements allocated for this item, load them if( gArmsDealersInventory[ubArmsDealer][usItemIndex].ubElementsAlloced > 0 ) { diff --git a/Tactical/Auto Bandage.cpp b/Tactical/Auto Bandage.cpp index ffb7454c..3f22f2f3 100644 --- a/Tactical/Auto Bandage.cpp +++ b/Tactical/Auto Bandage.cpp @@ -463,10 +463,10 @@ void AutoBandage( BOOLEAN fStart ) if ( pSoldier->bSlotItemTakenFrom != NO_SLOT ) { // swap our old hand item back to the main hand - SwapObjs( &( pSoldier->inv[ HANDPOS ] ), &( pSoldier->inv[ pSoldier->bSlotItemTakenFrom ] ) ); + SwapObjs( pSoldier, HANDPOS, pSoldier->bSlotItemTakenFrom ); } - // ATE: Mkae everyone stand up! + // ATE: Make everyone stand up! if ( pSoldier->bLife >= OKLIFE && !pSoldier->bCollapsed ) { if ( gAnimControl[ pSoldier->usAnimState ].ubHeight != ANIM_STAND ) diff --git a/Tactical/BinaryHeap.hpp b/Tactical/BinaryHeap.hpp index c5562431..8d1697fe 100644 --- a/Tactical/BinaryHeap.hpp +++ b/Tactical/BinaryHeap.hpp @@ -14,8 +14,7 @@ public: HEAP(T data, int key) {HEAP::data = data; HEAP::key = key; return;}; bool operator==(HEAP &compare) { - if (this->key == compare.key && this->data == compare.data) return true; - else return false; + return (this->key == compare.key && this->data == compare.data); } }; @@ -54,13 +53,15 @@ private: public: + typedef HEAP CBinaryHeap_t; + void clear () { heapCount = 1; return; } - CBinaryHeap(int size) + CBinaryHeap(int size=WORLD_MAX) { if (size <= 0) { size = WORLD_MAX; @@ -72,21 +73,12 @@ public: return; } - CBinaryHeap() - { - //size must be maxSize + 1, because 1 element is unused. - BinaryHeap = new HEAP[WORLD_MAX+1]; - maxSize = WORLD_MAX; - heapCount = 1; - return; - } - ~CBinaryHeap() { delete BinaryHeap; } - HEAP removeElement(const T data) + CBinaryHeap_t removeElement(const T data) { HEAP returnHeap; int index = findData(data); @@ -98,7 +90,7 @@ public: return (returnHeap); } - HEAP removeElement(const T data, const KEY key) + CBinaryHeap_t removeElement(const T data, const KEY key) { HEAP returnHeap; int index = findData(data, key); @@ -250,7 +242,7 @@ insert: }*/ } - HEAP popTopHeap(int& returnSize) + CBinaryHeap_t popTopHeap(int& returnSize) { returnSize = heapCount-1; if (heapCount != 1) { @@ -259,7 +251,7 @@ insert: return (BinaryHeap[0]); } - HEAP popTopHeap() + CBinaryHeap_t popTopHeap() { HEAP returnHeap; if (heapCount != 1) { @@ -295,12 +287,12 @@ insert: return (returnHeap); } - HEAP peekTopHeap() const + CBinaryHeap_t peekTopHeap() const { return (BinaryHeap[(heapCount != 1)]); } - HEAP peekElement(int index) const + CBinaryHeap_t peekElement(int index) const { if (index < heapCount) { return (BinaryHeap[index]); @@ -352,7 +344,7 @@ insert: private: int heapCount; int maxSize; - HEAP* BinaryHeap; + CBinaryHeap_t* BinaryHeap; }; #endif diff --git a/Tactical/Enemy Soldier Save.cpp b/Tactical/Enemy Soldier Save.cpp index 94a52f10..b0a6abe7 100644 --- a/Tactical/Enemy Soldier Save.cpp +++ b/Tactical/Enemy Soldier Save.cpp @@ -1369,7 +1369,7 @@ BOOLEAN NewWayOfLoadingCiviliansFromTempFile() if( !curr->pDetailedPlacement ) { //need to upgrade the placement to detailed placement - curr->pDetailedPlacement = new (MemAlloc( SIZEOF_SOLDIERCREATE_STRUCT )) SOLDIERCREATE_STRUCT; + curr->pDetailedPlacement = new SOLDIERCREATE_STRUCT; } //now replace the map pristine placement info with the temp map file version.. //memcpy( curr->pDetailedPlacement, &tempDetailedPlacement, SIZEOF_SOLDIERCREATE_STRUCT ); @@ -1558,7 +1558,7 @@ BOOLEAN NewWayOfSavingEnemyAndCivliansToTempFile( INT16 sSectorX, INT16 sSectorY //STEP ONE: Prep the soldiers for saving... - //modify the map's soldier init list to reflect the changes to the member's still alive... + //modify the map's soldier init list to reflect the changes to the members still alive... for( i = gTacticalStatus.Team[ ubStartID ].bFirstID; i <= gTacticalStatus.Team[ ubEndID ].bLastID; i++ ) { pSoldier = MercPtrs[ i ]; diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 1290dee6..b457f5bd 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -284,6 +284,7 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bLevel, UINT16 usHa // Check HAND ITEM if ( Item[ usHandItem ].usItemClass == IC_GUN || Item[ usHandItem ].usItemClass == IC_THROWING_KNIFE ) { + // WEAPONS DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("HandleItem: checking for fingerprintID, item id = %d,id required = %d, imprint id = %d, soldier id = %d",usHandItem,Item[usHandItem].fingerprintid,pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID,pSoldier->ubProfile)); if ( Item[usHandItem].fingerprintid ) @@ -545,6 +546,8 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bLevel, UINT16 usHa if ( Item[ usHandItem ].usItemClass != IC_THROWING_KNIFE ) { + pSoldier->ubAttackingHand = HANDPOS; + pSoldier->usAttackingWeapon = usHandItem; // If doing spread, set down the first gridno..... if ( pSoldier->fDoSpread ) { @@ -2144,7 +2147,11 @@ OBJECTTYPE* InternalAddItemToPool( INT16 *psGridNo, OBJECTTYPE *pObject, INT8 bV (*psGridNo) = sNewGridNo = gMapInformation.sCenterGridNo; - //return( NULL ); + // If no center grid number exists, shrug it off + if (sNewGridNo == -1) + { + return( NULL ); + } } // CHECK IF THIS ITEM IS IN DEEP WATER.... diff --git a/Tactical/Handle UI Plan.cpp b/Tactical/Handle UI Plan.cpp index 24ac2c53..5f49e424 100644 --- a/Tactical/Handle UI Plan.cpp +++ b/Tactical/Handle UI Plan.cpp @@ -102,7 +102,7 @@ BOOLEAN AddUIPlan( UINT16 sGridNo, UINT8 ubPlanID ) ConvertGridNoToCenterCellXY( sGridNo, &sXPos, &sYPos ); EVENT_SetSoldierPosition( pPlanSoldier, sXPos, sYPos ); - EVENT_SetSoldierDestination( pPlanSoldier, sGridNo ); + EVENT_SetSoldierDestination( pPlanSoldier, (UINT8) sGridNo ); // Hopefully this code is never used anymore because the second param is now direction, not grid pPlanSoldier->bVisible = 1; pPlanSoldier->usUIMovementMode = gpUIPlannedSoldier->usUIMovementMode; @@ -181,7 +181,7 @@ BOOLEAN AddUIPlan( UINT16 sGridNo, UINT8 ubPlanID ) } EVENT_SetSoldierPosition( pPlanSoldier, gpUIPlannedSoldier->dXPos, gpUIPlannedSoldier->dYPos ); - EVENT_SetSoldierDestination( pPlanSoldier, gpUIPlannedSoldier->sGridNo ); + EVENT_SetSoldierDestination( pPlanSoldier, (UINT8) gpUIPlannedSoldier->sGridNo ); pPlanSoldier->bVisible = 1; pPlanSoldier->usUIMovementMode = gpUIPlannedSoldier->usUIMovementMode; diff --git a/Tactical/Handle UI.cpp b/Tactical/Handle UI.cpp index f3e77f34..59aec155 100644 --- a/Tactical/Handle UI.cpp +++ b/Tactical/Handle UI.cpp @@ -3386,7 +3386,7 @@ void UIHandleSoldierStanceChange( UINT8 ubSoldierID, INT8 bNewStance ) // IF turn-based - adjust stance now! if ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) { - pSoldier->fTurningFromPronePosition = FALSE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; // Check if we have enough APS if ( SoldierCanAffordNewStance( pSoldier, bNewStance ) ) @@ -3420,13 +3420,16 @@ void UIHandleSoldierStanceChange( UINT8 ubSoldierID, INT8 bNewStance ) // LOCK VARIBLE FOR NO UPDATE INDEX... pSoldier->usUIMovementMode = GetMoveStateBasedOnStance( pSoldier, bNewStance ); + pSoldier->ubDesiredHeight = NO_DESIRED_HEIGHT; +#if 0 if ( pSoldier->usUIMovementMode == CRAWLING && gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE ) { pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = LOCKED_NO_NEWGRIDNO; pSoldier->bPathStored = FALSE; } else +#endif { pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1; } @@ -6319,7 +6322,7 @@ BOOLEAN ValidQuickExchangePosition( ) BOOLEAN IsValidJumpLocation( SOLDIERTYPE *pSoldier, INT16 sGridNo, BOOLEAN fCheckForPath ) { INT16 sFourGrids[4], sDistance=0, sSpot, sIntSpot; - INT8 sDirs[4] = { NORTH, EAST, SOUTH, WEST }; + static const UINT8 sDirs[4] = { NORTH, EAST, SOUTH, WEST }; //INT32 cnt; UINT8 ubGuyThere; UINT8 ubMovementCost; diff --git a/Tactical/Interface Dialogue.cpp b/Tactical/Interface Dialogue.cpp index 26b800ff..1b1b0545 100644 --- a/Tactical/Interface Dialogue.cpp +++ b/Tactical/Interface Dialogue.cpp @@ -1166,7 +1166,7 @@ BOOLEAN HandleTalkingMenuEscape( BOOLEAN fCanDelete , BOOLEAN fFromEscKey ) { ShutupaYoFace( gTalkPanel.iFaceIndex ); } - // Else if our queue is empty, delete emnu + // Else if our queue is empty, delete menu else { if ( DialogueQueueIsEmpty( ) && fCanDelete ) @@ -2225,7 +2225,7 @@ void HandleNPCDoAction( UINT8 ubTargetNPC, UINT16 usActionCode, UINT8 ubQuoteNum bItemIn = FindAIUsableObjClass( pSoldier, IC_GUN ); if (bItemIn != NO_SLOT && bItemIn != HANDPOS) { - SwapObjs( &(pSoldier->inv[HANDPOS]), &(pSoldier->inv[bItemIn]) ); + SwapObjs( pSoldier, HANDPOS, bItemIn ); sGridNo = pSoldier->sGridNo + DirectionInc( pSoldier->ubDirection ); SoldierReadyWeapon( pSoldier, (INT16) (sGridNo % WORLD_COLS), (INT16) (sGridNo / WORLD_COLS), FALSE ); } @@ -3114,7 +3114,7 @@ void HandleNPCDoAction( UINT8 ubTargetNPC, UINT16 usActionCode, UINT8 ubQuoteNum if (bMoneySlot < bEmptySlot) { // move main stash to later in inventory! - SwapObjs( &(pSoldier->inv[ bEmptySlot ] ), &(pSoldier->inv[ bMoneySlot ] ) ); + SwapObjs( pSoldier, bEmptySlot, bMoneySlot ); SoldierGiveItem( pSoldier, pSoldier2, &(pSoldier->inv[ bMoneySlot ] ), bMoneySlot ); } else diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index bbcc3616..ca063d79 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -610,7 +610,7 @@ void GenerateProsString( STR16 zItemPros, OBJECTTYPE * pObject, UINT32 uiPixLimi ubWeight = Item[ usItem ].ubWeight; if (Item[ usItem ].usItemClass == IC_GUN) { - ubWeight += Item[ pObject->ItemData.Gun.usGunAmmoItem ].ubWeight; + ubWeight = ubWeight + Item[ pObject->ItemData.Gun.usGunAmmoItem ].ubWeight; } if (Weapon[usItem].bAccuracy >= EXCEPTIONAL_ACCURACY ) @@ -753,7 +753,7 @@ void GenerateConsString( STR16 zItemCons, OBJECTTYPE * pObject, UINT32 uiPixLimi ubWeight = Item[ usItem ].ubWeight; if (Item[ usItem ].usItemClass == IC_GUN) { - ubWeight += Item[ pObject->ItemData.Gun.usGunAmmoItem ].ubWeight; + ubWeight = ubWeight + Item[ pObject->ItemData.Gun.usGunAmmoItem ].ubWeight; } if (ubWeight >= BAD_WEIGHT) @@ -2390,6 +2390,14 @@ BOOLEAN InternalInitItemDescriptionBox( OBJECTTYPE *pObject, INT16 sX, INT16 sY, INT16 sForeColour; INT16 sProsConsIndent; + // ADB: Make sure the current object isn't money if there's something in hand + if (Item[ pObject->usItem].usItemClass & IC_MONEY && gpItemPointer != NULL && gpItemPointer->usItem != 0) { + //ADB oops, money splits and puts a new item on the cursor, which would replace what's already on the cursor! + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Unable to split money due to having an item on your cursor." ); + return FALSE; + } + + //Set the current screen guiCurrentItemDescriptionScreen = guiCurrentScreen; @@ -5539,7 +5547,7 @@ BOOLEAN InitKeyRingPopup( SOLDIERTYPE *pSoldier, INT16 sInvX, INT16 sInvY, INT16 HVOBJECT hVObject; INT32 cnt; UINT16 usPopupWidth, usPopupHeight; - UINT8 ubSlotSimilarToKeySlot = 10; + //UINT8 ubSlotSimilarToKeySlot = 10; INT16 sKeyRingItemWidth = 0; INT16 sOffSetY = 0, sOffSetX = 0; diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 95331fe2..9bb899cb 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -2378,6 +2378,26 @@ void SwapObjs( OBJECTTYPE * pObj1, OBJECTTYPE * pObj2 ) */ } +//ADB these 2 functions were created because the code calls SwapObjs all over the place +//but never handles the effects of that swap! +void SwapObjs(SOLDIERTYPE* pSoldier, int leftSlot, int rightSlot) +{ + PERFORMANCE_MARKER + SwapObjs(&pSoldier->inv[ leftSlot ], &pSoldier->inv[ rightSlot ]); + + //old usItem for the left slot is now stored in the right slot, and vice versa + HandleTacticalEffectsOfEquipmentChange(pSoldier, leftSlot, pSoldier->inv[ rightSlot ].usItem, pSoldier->inv[ leftSlot ].usItem); + HandleTacticalEffectsOfEquipmentChange(pSoldier, rightSlot, pSoldier->inv[ leftSlot ].usItem, pSoldier->inv[ rightSlot ].usItem); +} + +void SwapObjs(SOLDIERTYPE* pSoldier, int slot, OBJECTTYPE* pObject) +{ + PERFORMANCE_MARKER + SwapObjs(&pSoldier->inv[ slot ], pObject); + + HandleTacticalEffectsOfEquipmentChange(pSoldier, slot, pObject->usItem, pSoldier->inv[ slot ].usItem); +} + void RemoveObjFrom( OBJECTTYPE * pObj, UINT8 ubRemoveIndex ) { // remove 1 object from an OBJECTTYPE, starting at index bRemoveIndex @@ -3922,7 +3942,7 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) if (pSoldier->inv[SECONDHANDPOS].usItem != 0) { // swap what WAS in the second hand into the cursor - SwapObjs( pObj, &(pSoldier->inv[SECONDHANDPOS])); + SwapObjs( pSoldier, SECONDHANDPOS, pObj); } } } @@ -3932,7 +3952,8 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) { // replacement/reloading/merging/stacking // keys have an additional check for key ID being the same - if ( (pObj->usItem == pInSlot->usItem) && ( Item[ pObj->usItem ].usItemClass != IC_KEY || pObj->ItemData.Key.ubKeyID == pInSlot->ItemData.Key.ubKeyID ) ) + if ( (pObj->usItem == pInSlot->usItem) && + ( Item[ pObj->usItem ].usItemClass != IC_KEY || pObj->ItemData.Key.ubKeyID == pInSlot->ItemData.Key.ubKeyID ) ) { if (Item[ pObj->usItem ].usItemClass == IC_MONEY) { @@ -3967,7 +3988,7 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) if (pObj->ubNumberOfObjects <= 1) { // swapping - SwapObjs( pObj, pInSlot ); + SwapObjs( pSoldier, bPos, pObj ); } else { @@ -4031,13 +4052,13 @@ BOOLEAN PlaceObject( SOLDIERTYPE * pSoldier, INT8 bPos, OBJECTTYPE * pObj ) } else { - SwapObjs( pObj, pInSlot ); + SwapObjs( pSoldier, bPos, pObj ); } } else if (pObj->ubNumberOfObjects <= __max( ubSlotLimit, 1 ) ) { // swapping - SwapObjs( pObj, pInSlot ); + SwapObjs( pSoldier, bPos, pObj ); } else { @@ -5647,7 +5668,7 @@ void SwapHandItems( SOLDIERTYPE * pSoldier ) if (pSoldier->inv[HANDPOS].usItem == NOTHING || pSoldier->inv[SECONDHANDPOS].usItem == NOTHING) { // whatever is in the second hand can be swapped to the main hand! - SwapObjs( &(pSoldier->inv[HANDPOS]), &(pSoldier->inv[SECONDHANDPOS]) ); + SwapObjs( pSoldier, HANDPOS, SECONDHANDPOS ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } else @@ -5662,7 +5683,7 @@ void SwapHandItems( SOLDIERTYPE * pSoldier ) } // the main hand is now empty so a swap is going to work... } - SwapObjs( &(pSoldier->inv[HANDPOS]), &(pSoldier->inv[SECONDHANDPOS]) ); + SwapObjs( pSoldier, HANDPOS, SECONDHANDPOS ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); } } @@ -5679,7 +5700,7 @@ void SwapOutHandItem( SOLDIERTYPE * pSoldier ) if (pSoldier->inv[SECONDHANDPOS].usItem == NOTHING) { // just swap the hand item to the second hand - SwapObjs( &(pSoldier->inv[HANDPOS]), &(pSoldier->inv[SECONDHANDPOS]) ); + SwapObjs( pSoldier, HANDPOS, SECONDHANDPOS ); DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 ); return; } diff --git a/Tactical/Items.h b/Tactical/Items.h index 7c7f7f2b..b8b51296 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -35,6 +35,8 @@ extern INT8 FindUsableObj( SOLDIERTYPE * pSoldier, UINT16 usItem ); extern void DeleteObj(OBJECTTYPE * pObj ); extern void CopyObj( OBJECTTYPE * pSourceObj, OBJECTTYPE * pTargetObj ); extern void SwapObjs( OBJECTTYPE * pObj1, OBJECTTYPE * pObj2 ); +extern void SwapObjs(SOLDIERTYPE* pSoldier, int leftSlot, int rightSlot); +extern void SwapObjs(SOLDIERTYPE* pSoldier, int slot, OBJECTTYPE* pObject); extern void SwapWithinObj( OBJECTTYPE * pObj, UINT8 ubIndex1, UINT8 ubIndex2 ); extern void RemoveObjFrom( OBJECTTYPE * pObj, UINT8 ubRemoveIndex ); diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp index 97cab813..5c264d19 100644 --- a/Tactical/LOS.cpp +++ b/Tactical/LOS.cpp @@ -799,7 +799,7 @@ INT32 LineOfSightTest( FLOAT dStartX, FLOAT dStartY, FLOAT dStartZ, FLOAT dEndX, fCheckForRoof = FALSE; // figure out starting and ending cubes - iGridNo = GETWORLDINDEXFROMWORLDCOORDS( (INT32)dStartX, (INT32)dStartY ); + iGridNo = GETWORLDINDEXFROMWORLDCOORDS( (INT32)dStartY, (INT32)dStartX ); qCurrZ = FloatToFixed( dStartZ ); qLandHeight = INT32_TO_FIXEDPT( CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ iGridNo ].sHeight ) ); iCurrAboveLevelZ = FIXEDPT_TO_INT32( qCurrZ - qLandHeight ); @@ -835,7 +835,7 @@ INT32 LineOfSightTest( FLOAT dStartX, FLOAT dStartY, FLOAT dStartZ, FLOAT dEndX, } } - iGridNo = GETWORLDINDEXFROMWORLDCOORDS( (INT32)dEndX, (INT32)dEndY ); + iGridNo = GETWORLDINDEXFROMWORLDCOORDS( (INT32)dEndY, (INT32)dEndX ); qCurrZ = FloatToFixed( dEndZ ); qLandHeight = INT32_TO_FIXEDPT( CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ iGridNo ].sHeight ) ); iCurrAboveLevelZ = FIXEDPT_TO_INT32( qCurrZ - qLandHeight ); diff --git a/Tactical/Militia Control.cpp b/Tactical/Militia Control.cpp index 19183f42..4c650085 100644 --- a/Tactical/Militia Control.cpp +++ b/Tactical/Militia Control.cpp @@ -231,7 +231,10 @@ void PrepareMilitiaForTactical( BOOLEAN fPrepareAll) ubGreen = pSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; ubRegs = pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; ubElites = pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; - + + // Prevent militia from just waiting on the border + gTacticalStatus.Team[MILITIA_TEAM].bAwareOfOpposition = (pSector->uiFlags & SF_PLAYER_KNOWS_ENEMIES_ARE_HERE) != 0; + if(guiDirNumber) { if (fPrepareAll) diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index ef30c798..104d98d5 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -110,6 +110,7 @@ #include "Strategic Status.h" #include "PreBattle Interface.h" #include "Militia Control.h" +#include "Lua Interpreter.h" #endif extern void HandleBestSightingPositionInRealtime(); @@ -707,6 +708,8 @@ BOOLEAN InitOverhead( ) ZeroAnimSurfaceCounts( ); + InitializeLua(); + return( TRUE ); } @@ -714,6 +717,8 @@ BOOLEAN ShutdownOverhead( ) { UINT32 cnt; + ShutdownLua( ); + // Delete any soldiers which have been created! for( cnt = 0; cnt < TOTAL_SOLDIERS; cnt++ ) { @@ -1575,7 +1580,7 @@ BOOLEAN ExecuteOverhead( ) { // Change desired direction // Just change direction - EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState ); + EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState ); } if ( gTacticalStatus.bBoxingState != NOT_BOXING && (gTacticalStatus.bBoxingState == BOXING_WAITING_FOR_PLAYER || gTacticalStatus.bBoxingState == PRE_BOXING || gTacticalStatus.bBoxingState == BOXING) ) @@ -2273,7 +2278,7 @@ BOOLEAN HandleGotoNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving, BOOLE if ( !fDontContinue ) { // Don't apply the first deduction in points... - if ( usAnimState == CRAWLING && pSoldier->fTurningFromPronePosition > 1 ) + if ( usAnimState == CRAWLING && pSoldier->bTurningFromPronePosition > TURNING_FROM_PRONE_ON ) { } else @@ -2325,7 +2330,7 @@ BOOLEAN HandleGotoNewGridNo( SOLDIERTYPE *pSoldier, BOOLEAN *pfKeepMoving, BOOLE } // Change desired direction - EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], fInitialMove, usAnimState ); + EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) pSoldier->usPathingData[ pSoldier->usPathIndex ], fInitialMove, usAnimState ); // CONTINUE // IT'S SAVE TO GO AGAIN, REFRESH flag @@ -4586,7 +4591,7 @@ INT16 FindAdjacentGridEx( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 *pubDirect // fDoor determines whether special door-handling code should be used (for interacting with doors) INT16 sFourGrids[4], sDistance=0; - INT8 sDirs[4] = { NORTH, EAST, SOUTH, WEST }; + static const UINT8 sDirs[4] = { NORTH, EAST, SOUTH, WEST }; //INT32 cnt; INT16 sClosest=NOWHERE, sSpot, sOkTest; INT16 sCloseGridNo=NOWHERE; @@ -4730,7 +4735,7 @@ INT16 FindAdjacentGridEx( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 *pubDirect // MOVE OUT TWO DIRECTIONS sFourGrids[cnt] = sSpot = NewGridNo( sGridNo, DirectionInc( sDirs[ cnt ] ) ); - ubTestDirection = (UINT8)sDirs[ cnt ]; + ubTestDirection = sDirs[ cnt ]; // For switches, ALLOW them to walk through walls to reach it.... if ( pDoor && pDoor->fFlags & STRUCTURE_SWITCH ) @@ -4874,7 +4879,7 @@ INT16 FindNextToAdjacentGridEx( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 *pub // fDoor determines whether special door-handling code should be used (for interacting with doors) INT16 sFourGrids[4], sDistance=0; - INT8 sDirs[4] = { NORTH, EAST, SOUTH, WEST }; + static const UINT8 sDirs[4] = { NORTH, EAST, SOUTH, WEST }; //INT32 cnt; INT16 sClosest=WORLD_MAX, sSpot, sSpot2, sOkTest; INT16 sCloseGridNo=NOWHERE; @@ -4957,7 +4962,7 @@ INT16 FindNextToAdjacentGridEx( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 *pub // MOVE OUT TWO DIRECTIONS sFourGrids[cnt] = sSpot = NewGridNo( sGridNo, DirectionInc( sDirs[ cnt ] ) ); - ubTestDirection = (UINT8)sDirs[ cnt ]; + ubTestDirection = sDirs[ cnt ]; if ( pDoor && pDoor->fFlags & STRUCTURE_SWITCH ) { @@ -5274,7 +5279,7 @@ void HandleTeamServices( UINT8 ubTeamNum ) } if ( bSlot != NO_SLOT ) { - SwapObjs( &(pTeamSoldier->inv[HANDPOS]), &(pTeamSoldier->inv[bSlot] ) ); + SwapObjs( pTeamSoldier, HANDPOS, bSlot ); } else { @@ -5361,7 +5366,7 @@ void HandlePlayerServices( SOLDIERTYPE *pTeamSoldier ) if ( bSlot != NO_SLOT ) { - SwapObjs( &(pTeamSoldier->inv[HANDPOS]), &(pTeamSoldier->inv[bSlot] ) ); + SwapObjs( pTeamSoldier, HANDPOS, bSlot ); } else { diff --git a/Tactical/PATHAI.H b/Tactical/PATHAI.H index 4641f712..11dbdea2 100644 --- a/Tactical/PATHAI.H +++ b/Tactical/PATHAI.H @@ -10,6 +10,7 @@ #define _PATHAI_H #include "isometric utils.h" + #define USE_ASTAR_PATHS #ifdef USE_ASTAR_PATHS @@ -30,30 +31,25 @@ class AStar_Data public: AStar_Data() { - cost = f = APCost = direction = prevCost = 0; -#ifdef ASTAR_USING_EXTRACOVER + cost = directionFromPrev = 0; extraGCoverCost = -1;//-1 means we have not stopped at this node -#endif wasBackwards = false; status = AStar_Init; - parent = GridNode(-1,-1); + parent = -1; }; - //no H - int cost;//G - int f;//F - int APCost;//the APs spent to get here -#ifdef ASTAR_USING_EXTRACOVER + // h is calculated on the fly + INT16 cost;//G + //int f;//F is also calculated on the fly + //int APCost;//the APs spent to get here // Isn't this the same as "cost"? int extraGCoverCost;//an extra cost that makes stopping in midpath at a node with little cover worse -#endif - GridNode parent; + INT16 parent; eAStar status; - int prevCost; + //int prevCost; bool wasBackwards; - int direction; + UINT8 directionFromPrev; + int numSteps; }; -typedef HEAP AStarHeap; - class AStarPathfinder { public: @@ -68,13 +64,19 @@ public: private: static AStarPathfinder* pThis; - std::vector ClosedList; - CBinaryHeap OpenHeap; + CBinaryHeap OpenHeap; - int direction;//current direction - int startDir; - int endDir; - int lastDir; + // The "closed list" is the AStarData array below which is also the path backtrace when a path is found. + + // For PythSpacesAway, we do not really need the distance, but rather something to compare against. + // So I will eliminate the sqrt function from the result for the sake of speed. This means that when comparing against + // input distances, we should square them. This is one of the variables that needs to hold the squared value. + int gubNPCDistLimitSq; + + UINT8 direction;//current direction + UINT8 startDir; + UINT8 endDir; + UINT8 lastDir; bool startingLoop; SOLDIERTYPE* pSoldier; @@ -84,7 +86,7 @@ private: bool fPathingForPlayer; bool fPathAroundPeople; bool fGoingThroughDoor; - int bOKToAddStructID; + INT16 bOKToAddStructID; int bLoopState; bool bWaterToWater; bool fVisitSpotsOnlyOnce; @@ -95,13 +97,16 @@ private: bool fConsiderPersonAtDestAsObstacle; bool fCopyReachable; bool fCopyPathCosts; + bool fFindClimbPoints; int maxAPBudget;//the gubNPCAPBudget int mercsMaxAPs;//the merc only has so many points to move with - int movementMode; - int sClosePathLimit; + UINT16 movementMode; + int sClosePathLimitSq; - int PATHAI_VISIBLE_DEBUG_Counter; + int travelcostDiag; + int travelcostOrth; + INT16 PATHAI_VISIBLE_DEBUG_Counter; //vehicle defined in cpp //#ifdef VEHICLE @@ -110,20 +115,18 @@ private: //#endif // member variables to prevent passing them around - GridNode StartNode; - GridNode DestNode; - GridNode CurrentNode; - GridNode ParentNode; - INT16 ParentNodeIndex; - INT16 CurrentNodeIndex; + INT16 StartNode; + INT16 DestNode; + INT16 CurrentNode; + INT16 ParentNode; - AStar_Data AStarData[WORLD_COLS][WORLD_ROWS]; + AStar_Data AStarData[WORLD_MAX]; - AStarHeap AStar (); + INT16 AStar (); void ExecuteAStarLogic(); @@ -131,49 +134,49 @@ private: int TerrainCostToAStarG(int const terrainCost); int CalcG (int* pPrevCost); - int CalcAP (int const terrainCost); + INT16 CalcAP (int const terrainCost, UINT8 const direction); int CalcH (); int CalcGCover (int const NodeIndex, int const APCost); - int CalcStartingAP (); + INT16 CalcStartingAP (); //including THREATTYPE was a pain, so pass by value int CalcCoverValue (INT16 sMyGridNo, INT32 iMyThreat, INT32 iMyAPsLeft, INT32 myThreatsiOrigRange, INT16 myThreatssGridNo, SOLDIERTYPE* myThreatspOpponent, INT32 myThreatsiValue, INT32 myThreatsiAPs, INT32 myThreatsiCertainty); - eAStar GetAStarStatus (const GridNode node) const {return AStarData[node.x][node.y].status;}; - GridNode GetAStarParent (const GridNode node) const {return AStarData[node.x][node.y].parent;}; - int GetAStarG (const GridNode node) const {return AStarData[node.x][node.y].cost;}; - int GetAStarF (const GridNode node) const {return AStarData[node.x][node.y].f;}; - int GetActionPoints (const GridNode node) const {return AStarData[node.x][node.y].APCost;}; - bool GetLoopState (const GridNode node) const {return AStarData[node.x][node.y].wasBackwards;}; - int GetPrevCost (const GridNode node) const {return AStarData[node.x][node.y].prevCost;}; - int GetDirection (const GridNode node) const {return AStarData[node.x][node.y].direction;}; -#ifdef ASTAR_USING_EXTRACOVER - int GetExtraGCover (const GridNode node) const {return AStarData[node.x][node.y].extraGCoverCost;}; + eAStar GetAStarStatus (const INT16 node) const {return AStarData[node].status;}; + INT16 GetAStarParent (const INT16 node) const {return AStarData[node].parent;}; + INT16 GetAStarG (INT16 node) const {return AStarData[node].cost;}; + int GetExtraGCover (const INT16 node) const {return AStarData[node].extraGCoverCost;}; + //int GetAStarF (const INT16 node) const {return AStarData[node].f;}; + //int GetActionPoints (const INT16 node) const {return AStarData[node].APCost;}; + bool GetLoopState (const INT16 node) const {return AStarData[node].wasBackwards;}; + //int GetPrevCost (const INT16 node) const {return AStarData[node].prevCost;}; + UINT8 GetDirection (const INT16 node) const {return AStarData[node].directionFromPrev;}; + int GetNumSteps (const INT16 node) const {return AStarData[node].numSteps;}; - void SetExtraGCover (const GridNode node, const int extraGCoverCost) {AStarData[node.x][node.y].extraGCoverCost = extraGCoverCost;}; -#endif - void SetAStarStatus (const GridNode node, const eAStar status) {AStarData[node.x][node.y].status = status;}; - void SetAStarParent (const GridNode node, const GridNode parent) {AStarData[node.x][node.y].parent = parent;}; - void SetAStarG (const GridNode node, const int cost) {AStarData[node.x][node.y].cost = cost;}; - void SetAStarF (const GridNode node, const int f) {AStarData[node.x][node.y].f = f;}; - void SetActionPoints (const GridNode node, const int APCost) {AStarData[node.x][node.y].APCost = APCost;}; - void SetLoopState (const GridNode node, const int loopState); - void SetPrevCost (const GridNode node, const int prevCost) {AStarData[node.x][node.y].prevCost = prevCost;}; - void SetDirection (const GridNode node, const int direction) {AStarData[node.x][node.y].direction = direction;}; + void SetAStarStatus (const INT16 node, const eAStar status) {AStarData[node].status = status;}; + void SetAStarParent (const INT16 node, const INT16 parent) {AStarData[node].parent = parent;}; + void SetAStarG (const INT16 node, const INT16 cost) {AStarData[node].cost = cost;}; + void SetExtraGCover (const INT16 node, const int extraGCoverCost) {AStarData[node].extraGCoverCost = extraGCoverCost;}; + //void SetAStarF (const INT16 node, const int f) {AStarData[node].f = f;}; + //void SetActionPoints (const INT16 node, const int APCost) {AStarData[node].APCost = APCost;}; + void SetLoopState (const INT16 node, const int loopState); + //void SetPrevCost (const INT16 node, const int prevCost) {AStarData[node].prevCost = prevCost;}; + void SetDirection (const INT16 node, const UINT8 direction) {AStarData[node].directionFromPrev = direction;}; + void SetNumSteps (const INT16 node, const int steps) {AStarData[node].numSteps = steps;}; - INT16 PythSpacesAway (GridNode const node1, - GridNode const node2); - INT16 SpacesAway (GridNode const node1, - GridNode const node2); - //bool IsDiagonal (GridNode const node1, - // GridNode const node2) {return (abs(node1.x - node2.x) && abs(node1.y - node2.y));}; + int PythSpacesAway (const INT16 node1, + const INT16 node2); + INT16 SpacesAway (const INT16 node1, + const INT16 node2); + //bool IsDiagonal (const INT16 node1, + // const INT16 node2) {return (abs(node1.x - node2.x) && abs(node1.y - node2.y));}; bool IsDiagonal (int const direction) {return (direction & 1);}; void InitVehicle (); int VehicleObstacleCheck(); - bool CanTraverse (); + bool WantToTraverse (); // Renamed this because it doesn't mean "can we traverse" but rather, if it is profitable to. bool IsSomeoneInTheWay(); void IncrementLoop (); void InitLoop (); @@ -182,7 +185,7 @@ private: };//end namespace ASTAR -#endif//end #ifdef USE_ASTAR_PATHS +#endif// USE_ASTAR_PATHS BOOLEAN InitPathAI( void ); @@ -289,12 +292,15 @@ extern UINT8 gubGlobalPathFlags; #define COPYROUTE 1 #define COPYREACHABLE 2 #define COPYREACHABLE_AND_APS 3 +#define FINDCLIMBPOINTS 4 #define PATH_THROUGH_PEOPLE 0x01 #define PATH_IGNORE_PERSON_AT_DEST 0x02 #define PATH_CLOSE_GOOD_ENOUGH 0x04 + #define PATH_CLOSE_RADIUS 5 +#define PATH_CLOSE_RADIUS_SQ 25 // ------------------------------------------ diff --git a/Tactical/PATHAI.cpp b/Tactical/PATHAI.cpp index 23f6fce0..d3c68ca3 100644 --- a/Tactical/PATHAI.cpp +++ b/Tactical/PATHAI.cpp @@ -45,7 +45,6 @@ Date : 1997-NOV #include "PathAIDebug.h" - #ifdef USE_ASTAR_PATHS #include "BinaryHeap.hpp" #include "AIInternals.h" @@ -69,7 +68,7 @@ extern UINT16 gubAnimSurfaceIndex[ TOTALBODYTYPES ][ NUMANIMATIONSTATES ]; extern INT16 gsCoverValue[WORLD_MAX]; BOOLEAN gfDisplayCoverValues = TRUE; -BOOLEAN gfDrawPathPoints = FALSE; +BOOLEAN gfDrawPathPoints = TRUE; #endif BOOLEAN gfPlotPathToExitGrid = FALSE; @@ -418,7 +417,7 @@ static INT32 giPathDataSize; static INT32 giPlotCnt; static UINT32 guiEndPlotGridNo; -static INT32 dirDelta[8]= +static INT16 dirDelta[8]= { -MAPWIDTH, //N 1-MAPWIDTH, //NE @@ -458,14 +457,17 @@ AStarPathfinder::AStarPathfinder() } //init the pointer to the AStarPathfinder singleton instance -AStarPathfinder* AStarPathfinder::pThis = NULL; +// And init a static class so that it will destroy itself on game exit +static AStarPathfinder AStarThis; +AStarPathfinder* AStarPathfinder::pThis = &AStarThis; AStarPathfinder& AStarPathfinder::GetInstance() { - if (pThis) { - return *pThis; - } - pThis = new AStarPathfinder; + //if (pThis) + //{ + // return *pThis; + //} + //pThis = new AStarPathfinder; return *pThis; } @@ -473,45 +475,77 @@ void AStarPathfinder::ResetAStarList() { //only the starting node's data needs to be init, //and all other data doesn't need to be at all - for each (GridNode node in ClosedList) { + //for ( GridNode_t::iterator node = ClosedList.begin(); + // node != ClosedList.end(); + // node++) + //{ + // SetAStarStatus(*node, AStar_Init); + // SetAStarG(*node, 0); + //} + //for (int size = OpenHeap.size(); size > 0; --size) + //{ + // GridNode node = OpenHeap.peekElement(size).data; + // SetAStarStatus(node, AStar_Init); + // SetAStarG(node, 0); + //} + for (INT16 node = 0; node < WORLD_MAX; node++) + { SetAStarStatus(node, AStar_Init); SetAStarG(node, 0); } - for (int size = OpenHeap.size(); size > 0; --size) { - GridNode node = OpenHeap.peekElement(size).data; - SetAStarStatus(node, AStar_Init); - SetAStarG(node, 0); - } - ClosedList.clear(); + + //ClosedList.clear(); OpenHeap.clear(); return; }//end ResetAStarList -void AStarPathfinder::SetLoopState(GridNode const node, +void AStarPathfinder::SetLoopState(const INT16 node, int const loopState) { - if ( loopState == LOOPING_REVERSE ) { - AStarData[node.x][node.y].wasBackwards = STEP_BACKWARDS; + if ( loopState == LOOPING_REVERSE ) + { + AStarData[node].wasBackwards = STEP_BACKWARDS; } - else { - AStarData[node.x][node.y].wasBackwards = false; + else + { + AStarData[node].wasBackwards = false; } } -INT16 AStarPathfinder::PythSpacesAway(GridNode const node1, - GridNode const node2) +int AStarPathfinder::PythSpacesAway(const INT16 node1, + const INT16 node2) { - //should be faster than it's counterpart - int x = abs(node1.x - node2.x); - int y = abs(node1.y - node2.y); - return ((INT16)sqrt((double)(x * x + y * y))); + int sRows,sCols,sResult; + GridNode *n1, *n2; + + n1 = GridNode::MapXY + node1; + n2 = GridNode::MapXY + node2; + + sRows = n1->x - n2->x; + sCols = n1->y - n2->y; + + // apply Pythagoras's theorem for right-handed triangle: + // Except skip the square root. No reason to care about actual distance since we only need it for comparison. + // And conversion to double, then sqrt, then conversion back to int, is expensive. + sResult = (sRows * sRows) + (sCols * sCols); + + return(sResult); } -INT16 AStarPathfinder::SpacesAway(GridNode const node1, - GridNode const node2) +INT16 AStarPathfinder::SpacesAway(const INT16 node1, + const INT16 node2) { - //should be faster than it's counterpart - return (__max(abs(node1.x - node2.x), abs(node1.y - node2.y))); + //should be faster than its counterpart + int sRows,sCols; + GridNode *n1, *n2; + + n1 = GridNode::MapXY + node1; + n2 = GridNode::MapXY + node2; + + sRows = abs(n1->x - n2->x); + sCols = abs(n1->y - n2->y); + + return (INT16)(__max(sRows, sCols)); } int AStarPathfinder::GetPath(SOLDIERTYPE *s , @@ -527,49 +561,47 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , this->onRooftop = ubLevel; this->movementMode = usMovementMode; this->maxAPBudget = gubNPCAPBudget; - int start = pSoldier->sGridNo; - StartNode.IntToGridNode(start); - DestNode.IntToGridNode(dest); - if ( gubGlobalPathFlags ) { - fFlags |= gubGlobalPathFlags; - } + // Set the distance for this calc + this->gubNPCDistLimitSq = (int) gubNPCDistLimit * (int) gubNPCDistLimit; - //check start node is legal, don't check dest node - if (start < 0 || start > WORLD_MAX) { + StartNode = pSoldier->sGridNo; + DestNode = dest; + fFlags |= gubGlobalPathFlags; + + if (StartNode < 0 || StartNode > WORLD_MAX) + { #ifdef JA2BETAVERSION - ScreenMsg( FONT_MCOLOR_RED, MSG_TESTVERSION, L"ERROR! Trying to calculate path from off-world gridno %d to %d", start, dest ); + ScreenMsg( FONT_MCOLOR_RED, MSG_TESTVERSION, L"ERROR! Trying to calculate path from off-world gridno %d to %d", StartNode, DestNode ); #endif return( 0 ); } - else if (!GridNoOnVisibleWorldTile( (INT16) start ) ) { + else if (!GridNoOnVisibleWorldTile( StartNode ) ) + { #ifdef JA2BETAVERSION - ScreenMsg( FONT_MCOLOR_RED, MSG_TESTVERSION, L"ERROR! Trying to calculate path from non-visible gridno %d to %d", start, dest ); + ScreenMsg( FONT_MCOLOR_RED, MSG_TESTVERSION, L"ERROR! Trying to calculate path from non-visible gridno %d to %d", StartNode, DestNode ); #endif return( 0 ); } - else if (pSoldier->bLevel != onRooftop) { + else if (pSoldier->bLevel != onRooftop) + { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, different level" ) ); // pathing to a different level... bzzzt! return( 0 ); } - //init the starting node's data that hasn't been reset - SetAStarParent(StartNode, GridNode(-1,-1)); - SetPrevCost(StartNode, 0); - #if defined( PATHAI_VISIBLE_DEBUG ) if (gfDisplayCoverValues && gfDrawPathPoints) { memset( gsCoverValue, 0x7F, sizeof( INT16 ) * WORLD_MAX ); } - gsCoverValue[ start ] = 0; + gsCoverValue[ StartNode ] = 0; PATHAI_VISIBLE_DEBUG_Counter = 1; #endif //init other private data, mostly flags - startDir = endDir = lastDir = direction = 0; + endDir = lastDir = direction = startDir = 0; pStructureFileRef = NULL; bLoopState = LOOPING_CLOCKWISE; fCheckedBehind = FALSE; @@ -583,29 +615,37 @@ 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 ) ) + { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, water" ) ); return( 0 ); } - if ( fCloseGoodEnough ) { - sClosePathLimit = __min( PythSpacesAway( StartNode, DestNode ) - 1, PATH_CLOSE_RADIUS ); - if ( sClosePathLimit <= 0 ) { - DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, path limit" ) ); + if ( fCloseGoodEnough ) + { + sClosePathLimitSq = __min( PythSpacesAway( StartNode, DestNode ) - 1, PATH_CLOSE_RADIUS_SQ ); + if ( sClosePathLimitSq <= 0 ) + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, path limit" ) ); return( 0 ); } } - if (bCopy >= COPYREACHABLE) { + if (bCopy >= COPYREACHABLE) + { fCopyReachable = TRUE; fCopyPathCosts = (bCopy == COPYREACHABLE_AND_APS); - fVisitSpotsOnlyOnce = (bCopy == COPYREACHABLE); + fVisitSpotsOnlyOnce = (bCopy == COPYREACHABLE || bCopy == FINDCLIMBPOINTS); + fFindClimbPoints = (bCopy == FINDCLIMBPOINTS); // make sure we aren't trying to copy path costs for an area greater than the AI array... - if (fCopyPathCosts && gubNPCDistLimit > AI_PATHCOST_RADIUS) { + if (fCopyPathCosts && gubNPCDistLimitSq > AI_PATHCOST_RADIUS*AI_PATHCOST_RADIUS) + { // oy!!!! dis no supposed to happen! + gubNPCDistLimitSq = AI_PATHCOST_RADIUS*AI_PATHCOST_RADIUS; gubNPCDistLimit = AI_PATHCOST_RADIUS; } } - else { + else + { fCopyReachable = FALSE; fCopyPathCosts = FALSE; fVisitSpotsOnlyOnce = FALSE; @@ -613,18 +653,20 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , gubNPCPathCount++; - // only allow nowhere destination if distance limit set - if (dest != NOWHERE) { - if (dest == pSoldier->sGridNo) { - DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, dest is start" ) ); + if (DestNode != NOWHERE) + { + if (DestNode == pSoldier->sGridNo) + { + DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, dest is start" ) ); return( 0 ); } // the very first thing to do is make sure the destination tile is reachable - if (!NewOKDestination( pSoldier, dest, fConsiderPersonAtDestAsObstacle, onRooftop )) { + if (!NewOKDestination( pSoldier, dest, fConsiderPersonAtDestAsObstacle, onRooftop )) + { maxAPBudget = 0; - gubNPCDistLimit = 0; - DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, not ok dest" ) ); + gubNPCDistLimitSq = 0; + DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, not ok dest" ) ); return( 0 ); } } @@ -637,17 +679,20 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , #ifdef VEHICLE fMultiTile = ((pSoldier->uiStatusFlags & SOLDIER_MULTITILE) != 0); - if ( fMultiTile == false) { + if ( fMultiTile == false) + { fContinuousTurnNeeded = FALSE; } - else { + else + { // Get animation surface... // Chris_C... change this to use parameter..... UINT16 usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, movementMode ); // Get structure ref... pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, movementMode ); - if ( pStructureFileRef ) { + if ( pStructureFileRef ) + { fContinuousTurnNeeded = ( ( pSoldier->uiStatusFlags & (SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE) ) != 0 ); /* @@ -661,17 +706,21 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , fTurnSlow = TRUE; } */ - if ( gfEstimatePath ) { + if ( gfEstimatePath ) + { bOKToAddStructID = IGNORE_PEOPLE_STRUCTURE_ID; } - else if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) { + else if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) + { bOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } - else { + else + { bOKToAddStructID = INVALID_STRUCTURE_ID; } } - else { + else + { // turn off multitile pathing fMultiTile = FALSE; fContinuousTurnNeeded = FALSE; @@ -679,20 +728,25 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , } #endif - if (fContinuousTurnNeeded == false) { + if (fContinuousTurnNeeded == false) + { bLoopState = LOOPING_CLOCKWISE; } // if origin and dest is water, then user wants to stay in water! // so, check and set waterToWater flag accordingly - if (dest == NOWHERE) { + if (dest == NOWHERE) + { bWaterToWater = false; } - else { - if (ISWATER(gubWorldMovementCosts[start][0][onRooftop]) && ISWATER(gubWorldMovementCosts[dest][0][onRooftop])) { + else + { + if (ISWATER(gubWorldMovementCosts[StartNode][0][onRooftop]) && ISWATER(gubWorldMovementCosts[DestNode][0][onRooftop])) + { bWaterToWater = true; } - else { + else + { bWaterToWater = false; } } @@ -701,71 +755,103 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , ResetAStarList(); //Start the main loop and get a path - AStarHeap bestPath = AStar(); + int bestPath = AStar(); gubNPCAPBudget = 0; - gubNPCDistLimit = 0; - if (bestPath.key == -222) { + gubNPCDistLimitSq = 0; + if (bestPath == -222) + { //error, path not found - DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, path not found, size closed list %d", ClosedList.size() ) ); + DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR: path failed, path not found" ) ); return 0; } #if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) { + if (gfDisplayCoverValues && gfDrawPathPoints) + { SetRenderFlags( RENDER_FLAG_FULL ); - if ( guiCurrentScreen == GAME_SCREEN ) { +// The RenderCoverDebugInfo call is now made by RenderWorld. So don't try to call it here +#if 0 + if ( guiCurrentScreen == GAME_SCREEN ) + { RenderWorld(); RenderCoverDebug( ); InvalidateScreen( ); EndFrameBufferRender(); RefreshScreen( NULL ); } +#endif } #endif - std::vector reversePath; - GridNode parent = bestPath.data; - unsigned int sizePath; - for (sizePath = 0; parent.isInWorld() && parent != StartNode; ++sizePath) { - reversePath.push_back(parent); - parent = GetAStarParent(parent); + // 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) + INT16 parent = DestNode; + INT16 current = parent; + unsigned int sizePath = 0; + while (current != -1) + { + sizePath += GetNumSteps( current); + while (sizePath > MAX_PATH_LIST_SIZE) + { + sizePath -= GetNumSteps( parent); + parent = GetAStarParent( parent); + } + current = GetAStarParent( current); } - std::vector path; - path.resize(reversePath.size()); - for (int x = path.size() - 1, sizePath = 0; x >= 0; ++sizePath, --x) { - path[sizePath] = reversePath[x]; - } - - // if this function was called because a solider is about to embark on an actual route // (as opposed to "test" path finding (used by cursor, etc), then grab all pertinent // data and copy into soldier's database - if (bCopy == COPYROUTE) { - for (sizePath = 0; sizePath < path.size() && sizePath < MAX_PATH_LIST_SIZE; ++sizePath) { - parent = path[sizePath]; - pSoldier->usPathingData[sizePath] = GetDirection(parent); - } - pSoldier->usPathIndex = 0; + if (bCopy == COPYROUTE) + { pSoldier->usPathDataSize = (UINT16) sizePath; - } - else if (bCopy == NO_COPYROUTE) { - for (sizePath = 0; sizePath < path.size(); ++sizePath) { - parent = path[sizePath]; - guiPathingData[ sizePath ] = GetDirection(parent); + pSoldier->usPathIndex = 0; + while (sizePath > 0) + { + int numSteps = GetNumSteps(parent); + while (numSteps > 0) + { + sizePath--; + numSteps--; + pSoldier->usPathingData[sizePath] = GetDirection(parent); + } + parent = GetAStarParent( parent); } + // Since the sizePath began as the total path size, it should be 0 when we finish copying it + Assert( sizePath == 0); + sizePath = pSoldier->usPathDataSize; + } + else if (bCopy == NO_COPYROUTE) + { giPathDataSize = (UINT16) sizePath; + while (sizePath > 0) + { + int numSteps = GetNumSteps(parent); + while (numSteps > 0) + { + sizePath--; + numSteps--; + guiPathingData[sizePath] = GetDirection(parent); + } + parent = GetAStarParent( parent); + } + // Since the sizePath began as the total path size, it should be 0 when we finish copying it + Assert( sizePath == 0); + sizePath = giPathDataSize; } #if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) { + if (gfDisplayCoverValues && gfDrawPathPoints) + { SetRenderFlags( RENDER_FLAG_FULL ); +#if 0 RenderWorld(); RenderCoverDebug( ); InvalidateScreen( ); EndFrameBufferRender(); RefreshScreen( NULL ); +#endif } #endif @@ -775,7 +861,8 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , //TEMP: This is returning zero when I am generating edgepoints, so I am force returning 1 until // this is fixed? - if( gfGeneratingMapEdgepoints ) { + if( gfGeneratingMapEdgepoints ) + { return 1; } @@ -785,23 +872,47 @@ int AStarPathfinder::GetPath(SOLDIERTYPE *s , return sizePath; }//end GetPath -AStarHeap AStarPathfinder::AStar() +INT16 AStarPathfinder::AStar() { - AStarHeap TopHeap(StartNode, 0); + HEAP TopHeap(StartNode, 0); ParentNode = StartNode; - for (;;) { - if (ParentNode == DestNode) { + + this->travelcostOrth = CalcAP( TRAVELCOST_FLAT, 0); + this->travelcostDiag = CalcAP( TRAVELCOST_FLAT, 1); + + // Since the startup cost is always charged for the first node, why not make it the source node's cost? + //init the starting node's data that hasn't been reset + SetAStarParent(StartNode, -1); + SetNumSteps(StartNode, 0); + SetAStarG( StartNode, CalcStartingAP()); + + for (;;) + { + if ( fCloseGoodEnough ) + { + if ( PythSpacesAway( CurrentNode, DestNode ) <= sClosePathLimitSq ) + { + // stop the path here! + DestNode = CurrentNode; + fCloseGoodEnough = FALSE; + } + } + + if (ParentNode == DestNode) + { //the best path to date leads to the dest //this node is neither in the open heap or the closed list, make sure it gets reset ok. - ClosedList.push_back(ParentNode); - return TopHeap; + //ClosedList.push_back(ParentNode); + //We just clear the entire array before starting, so no prob. + break; } //this adds to the heap nodes that might be on a path ExecuteAStarLogic(); - if (OpenHeap.size() == 0) { - return AStarHeap(StartNode, -222);//error code meaning no path at all! + if (OpenHeap.size() == 0) + { + return -222;//error code meaning no path at all! } //get the best point so far from the heap @@ -809,19 +920,22 @@ AStarHeap AStarPathfinder::AStar() ParentNode = TopHeap.data; //ASSERT(GetAStarStatus(ParentNode) == AStar_Open); } - return TopHeap; + return GetAStarG( TopHeap.data); }//end AStar void AStarPathfinder::IncrementLoop() { startingLoop = false; - if ( fContinuousTurnNeeded && direction == gOppositeDirection[ startDir ] ) { + if ( fContinuousTurnNeeded && direction == gOppositeDirection[ startDir ] ) + { fCheckedBehind = TRUE; } - if (bLoopState == LOOPING_CLOCKWISE) {// backwards + if (bLoopState == LOOPING_CLOCKWISE) + {// backwards direction = gOneCCDirection[ direction ]; } - else { + else + { direction = gOneCDirection[ direction ]; } return; @@ -836,7 +950,8 @@ void AStarPathfinder::InitLoop() bool AStarPathfinder::ContinueLoop() { - if (direction == endDir && startingLoop == false) { + if (direction == endDir && startingLoop == false) + { return false; } return true; @@ -847,38 +962,47 @@ void AStarPathfinder::ExecuteAStarLogic() //parent node set in AStar //this node is now closed SetAStarStatus(ParentNode, AStar_Closed); - ClosedList.push_back(ParentNode); - ParentNodeIndex = ParentNode.GridNodeToInt(); + //ClosedList.push_back(ParentNode); #if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) { - if (gsCoverValue[ ParentNodeIndex ] > 0) { - gsCoverValue[ ParentNodeIndex ] *= -1; + if (gfDisplayCoverValues && gfDrawPathPoints) + { + if (gsCoverValue[ ParentNode ] > 0) + { + gsCoverValue[ ParentNode ] *= -1; } } #endif - int prevCost; - int baseGCost = GetAStarG(ParentNode); - int baseAP = GetActionPoints(ParentNode); - if (fCopyReachable) { - if (GetPrevCost(ParentNode) != TRAVELCOST_FENCE) { - gpWorldLevelData[ParentNodeIndex].uiFlags |= MAPELEMENT_REACHABLE; - if (gubBuildingInfoToSet > 0) { - gubBuildingInfo[ ParentNodeIndex ] = gubBuildingInfoToSet; + // Shouldn't G and AP be the same thing? + INT16 baseGCost = GetAStarG(ParentNode); + //int baseAP = GetActionPoints(ParentNode); + + if (fCopyReachable) + { + //if (GetPrevCost(ParentNode) != TRAVELCOST_FENCE) + { + gpWorldLevelData[ParentNode].uiFlags |= MAPELEMENT_REACHABLE; + if (gubBuildingInfoToSet > 0) + { + gubBuildingInfo[ ParentNode ] = gubBuildingInfoToSet; } } } - if (fContinuousTurnNeeded) { - GridNode parent = GetAStarParent(ParentNode); - if (parent == GridNode(-1,-1)) { + if (fContinuousTurnNeeded) + { + INT16 parent = GetAStarParent(ParentNode); + if (parent == -1) + { lastDir = pSoldier->ubDirection; } - else if ( GetLoopState(parent) == false ) { + else if ( GetLoopState(parent) == false ) + { lastDir = GetDirection(parent); } - else { + else + { lastDir = gOppositeDirection[ GetDirection(parent) ]; } startDir = lastDir; @@ -886,35 +1010,77 @@ void AStarPathfinder::ExecuteAStarLogic() bLoopState = LOOPING_CLOCKWISE; fCheckedBehind = FALSE; } - else { + else + { //startDir and endDir are init to 0, and not reinit after a loop in the original code } //because startDir and endDir are set to the same thing, and to easily use //continue along with the loop increment at the end of the loop, use functions - for (InitLoop(); ContinueLoop(); IncrementLoop()) { - CurrentNodeIndex = ParentNodeIndex + dirDelta[direction]; - CurrentNode.IntToGridNode(CurrentNodeIndex); + for (InitLoop(); ContinueLoop(); IncrementLoop()) + { + int prevCost = 1; + + CurrentNode = ParentNode + dirDelta[direction]; //if the node is not in the world, or it has already been searched, continue - if (CurrentNode.isInWorld() == false || - GetAStarStatus(CurrentNode) == AStar_Closed) { + if (CurrentNode < 0 || + CurrentNode >= WORLD_MAX || + GetAStarStatus(CurrentNode) == AStar_Closed) + { continue; } #ifdef VEHICLE //has side effects, including setting loop counters int retVal = VehicleObstacleCheck(); - if (retVal == 1) { + if (retVal == 1) + { continue; } - else if (retVal == 2) { + else if (retVal == 2) + { return; } #endif - //if the node is not traversable, continue - if (CanTraverse() == false) { + //calc the cost to move from the current node to here + INT16 terrainCost = EstimateActionPointCost( pSoldier, CurrentNode, direction, movementMode, 0, 3 ); + + if (terrainCost == 100) { + if (fFindClimbPoints) + { + if ((direction & 1) == 0 && + NewOKDestination( pSoldier, CurrentNode, FALSE, 0) && + FindStructure( CurrentNode, STRUCTURE_ROOF ) == NULL) + { + gpWorldLevelData[ CurrentNode ].ubExtFlags[0] |= MAPELEMENT_EXT_CLIMBPOINT; + gpWorldLevelData[ ParentNode ].ubExtFlags[1] |= MAPELEMENT_EXT_CLIMBPOINT; +#ifdef ROOF_DEBUG + gsCoverValue[CurrentNode] = 1; +#endif + + } + } + + //an error code like diagonal door or obstruction was returned + continue; + } + + if (gubWorldMovementCosts[CurrentNode][direction][pSoldier->bLevel] == TRAVELCOST_FENCE) + { + SetAStarStatus(CurrentNode, AStar_Closed); + CurrentNode = CurrentNode + dirDelta[direction]; + if (!NewOKDestination( pSoldier, CurrentNode, fPathAroundPeople, pSoldier->bLevel)) + { + continue; + } + prevCost = 2; + } + + //if the node is not traversible, continue + if (WantToTraverse() == false) + { continue; } @@ -924,49 +1090,24 @@ void AStarPathfinder::ExecuteAStarLogic() continue; } - //calc the cost to move from the current node to here - int terrainCost = CalcG(&prevCost);//store the prevCost (it's actually a renamed terrainCost) - //early in the function because terrainCost may change later inside CalcG - if (terrainCost == -1) { - //an error code like diagonal door or obstruction was returned - continue; - } + //int movementG = TerrainCostToAStarG(terrainCost); + //INT16 movementG = CalcAP( terrainCost, direction); + INT16 movementG; + //movementG = (INT16) CalcG( &prevCost); + //movementG = CalcAP( movementG, direction); + + movementG = terrainCost * 100; - int movementG = TerrainCostToAStarG(terrainCost); - - int AStarG = baseGCost + movementG; + INT16 AStarG = baseGCost + movementG; //if the node is more costly in this path than in another open path, continue if (GetAStarStatus(CurrentNode) == AStar_Open - && AStarG >= GetAStarG(CurrentNode)) { + && AStarG >= GetAStarG(CurrentNode)) + { continue; } -#if defined( PATHAI_VISIBLE_DEBUG ) - if (gfDisplayCoverValues && gfDrawPathPoints) { - if (gsCoverValue[CurrentNodeIndex] == 0x7F7F) { - gsCoverValue[CurrentNodeIndex] = PATHAI_VISIBLE_DEBUG_Counter++; - } - /* - else if (gsCoverValue[CurrentNodeIndex] >= 0) { - gsCoverValue[CurrentNodeIndex]++; - } - else { - gsCoverValue[CurrentNodeIndex]--; - } - */ - } -#endif - int APCost = CalcAP(terrainCost);//cost to make this one move - if (APCost == -1) { - //error code like obstacle or crawling over fence - continue; - } - if (GetAStarParent(ParentNode) != GridNode(-1,-1)) { - APCost += baseAP;//cost to move from start to here - } - else { - APCost += CalcStartingAP();//cost to start running and to move here - } - if (maxAPBudget && APCost > maxAPBudget) { + + if (maxAPBudget && AStarG > maxAPBudget * 100) + { continue; } @@ -974,11 +1115,13 @@ void AStarPathfinder::ExecuteAStarLogic() #ifdef ASTAR_USING_EXTRACOVER //check if we will run out of AP while entering this node or before //if we run out, the merc will stop at the parent node for a turn and be vulnerable - if (mercsMaxAPs && APCost > mercsMaxAPs) { + if (mercsMaxAPs && APCost > mercsMaxAPs) + { extraGCoverCost = GetExtraGCover(ParentNode); //if the parent did not have the cost cached, then calc it - if (extraGCoverCost == -1) { + if (extraGCoverCost == -1) + { //use the stance and cover to see how much we really want to stop at the parent node //as opposed to an equal path with different cover //because other nodes further on the path will stop here too, add this value to the F cost @@ -988,7 +1131,8 @@ void AStarPathfinder::ExecuteAStarLogic() //cache the cost to stay at the parent node SetExtraGCover(ParentNode, extraGCoverCost); } - else { + else + { //the parent node is too long, all other nodes from start node //will be too long too, and have the extra cost already added } @@ -997,30 +1141,61 @@ void AStarPathfinder::ExecuteAStarLogic() int AStarH = CalcH(); int AStarF = (AStarG + extraGCoverCost) + AStarH; +#if defined( PATHAI_VISIBLE_DEBUG ) + if (gfDisplayCoverValues && gfDrawPathPoints) + { + if (gsCoverValue[CurrentNode] == 0x7F7F) + { + //gsCoverValue[CurrentNode] = PATHAI_VISIBLE_DEBUG_Counter++; + gsCoverValue[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) { + if (GetAStarStatus(CurrentNode) == AStar_Init) + { OpenHeap.insertElement(CurrentNode, AStarF); } - else { + else + { OpenHeap.editElement(CurrentNode, AStarF); } + SetAStarStatus(CurrentNode, AStar_Open); - SetAStarF(CurrentNode, AStarF); + //SetAStarF(CurrentNode, AStarF); SetAStarG(CurrentNode, AStarG); - SetActionPoints(CurrentNode, APCost); + //SetActionPoints(CurrentNode, APCost); SetAStarParent(CurrentNode, ParentNode); SetLoopState(CurrentNode, bLoopState); SetDirection(CurrentNode, direction); - SetPrevCost(CurrentNode, prevCost); -#ifdef ASTAR_USING_EXTRACOVER + //SetPrevCost(CurrentNode, prevCost); SetExtraGCover(CurrentNode, extraGCoverCost); -#endif - if (maxAPBudget) { - if (fCopyPathCosts) { - //dunno what this is yet - int iX = AI_PATHCOST_RADIUS + CurrentNode.x - StartNode.x; - int iY = AI_PATHCOST_RADIUS + CurrentNode.y - StartNode.y; - gubAIPathCosts[iX][iY] = APCost; + // Set the number of steps for this tile. It's 2 if we are hopping a fence + SetNumSteps( CurrentNode, prevCost); + + if (maxAPBudget) + { + if (fCopyPathCosts) + { + //This is used by FindBestNearbyCover to look for something that can be reached this turn + GridNode *n1, *n2; + n1 = GridNode::MapXY + CurrentNode; + n2 = GridNode::MapXY + StartNode; + int iX = AI_PATHCOST_RADIUS + n1->x - n2->x; + int iY = AI_PATHCOST_RADIUS + n1->y - n2->y; + Assert( iX >= 0 && iX <= AI_PATHCOST_RADIUS*2 && iY >= 0 && iY <= AI_PATHCOST_RADIUS*2); + gubAIPathCosts[iX][iY] = (INT8) AStarG; } } } @@ -1028,17 +1203,17 @@ void AStarPathfinder::ExecuteAStarLogic() return; }//end ExecuteAStarLogic -int AStarPathfinder::CalcStartingAP() +INT16 AStarPathfinder::CalcStartingAP() { // Add to points, those needed to start from different stance! - int startingAPCost = MinAPsToStartMovement( pSoldier, movementMode ); + INT16 startingAPCost = MinAPsToStartMovement( pSoldier, movementMode ); // We should reduce points for starting to run if first tile is a fence... - if ( gubWorldMovementCosts[ CurrentNodeIndex ][ direction ][ onRooftop ] == TRAVELCOST_FENCE ) + if ( gubWorldMovementCosts[ CurrentNode ][ direction ][ onRooftop ] == TRAVELCOST_FENCE ) { if ( movementMode == RUNNING && pSoldier->usAnimState != RUNNING ) { - startingAPCost -= AP_START_RUN_COST; + startingAPCost = startingAPCost - AP_START_RUN_COST; } } return startingAPCost; @@ -1046,36 +1221,31 @@ int AStarPathfinder::CalcStartingAP() int AStarPathfinder::TerrainCostToAStarG(int const terrainCost) { - //first, a small check that doesn't belong here but where else to put it? - if ( fCloseGoodEnough ) { - if ( PythSpacesAway( CurrentNode, DestNode ) <= sClosePathLimit ) { - // stop the path here! - DestNode = CurrentNode; - fCloseGoodEnough = FALSE; - } - } - int movementG; // NOTE: on September 24, 1997, Chris went back to a diagonal bias system - if (IsDiagonal(direction) == true) { + if (IsDiagonal(direction) == true) + { movementG = terrainCost * 14; } - else { + else + { movementG = terrainCost * 10; } - if ( bLoopState == LOOPING_REVERSE) { + if ( bLoopState == LOOPING_REVERSE) + { // penalize moving backwards to encourage turning sooner movementG += 500; } return movementG; } -int AStarPathfinder::CalcAP(int const terrainCost) +INT16 AStarPathfinder::CalcAP(int const terrainCost, UINT8 const direction) { // NEW Apr 21 by Ian: abort if cost exceeds budget - int movementAPCost; - switch(terrainCost) { + INT16 movementAPCost; + switch(terrainCost) + { case TRAVELCOST_NONE: movementAPCost = 0; break; case TRAVELCOST_DIRTROAD: @@ -1109,23 +1279,26 @@ int AStarPathfinder::CalcAP(int const terrainCost) // don't make the mistake of adding directly to // ubCurAPCost, that must be preserved for remaining dirs! - if (IsDiagonal(direction)) { + if (IsDiagonal(direction)) + { movementAPCost = (movementAPCost * 14) / 10; } int movementModeToUseForAPs = movementMode; // ATE: if water, force to be walking always! - if ( terrainCost == TRAVELCOST_SHORE || terrainCost == TRAVELCOST_KNEEDEEP || terrainCost == TRAVELCOST_DEEPWATER ) { + if ( terrainCost == TRAVELCOST_SHORE || terrainCost == TRAVELCOST_KNEEDEEP || terrainCost == TRAVELCOST_DEEPWATER ) + { movementModeToUseForAPs = WALKING; } // adjust AP cost for movement mode - switch( movementModeToUseForAPs ) { + switch( movementModeToUseForAPs ) + { case RUNNING: case ADULTMONSTER_WALKING: // save on casting - movementAPCost = movementAPCost * 100 / ( (UINT8) (RUNDIVISOR * 100)); + movementAPCost = (INT16) (movementAPCost * 100 / ( (UINT8) (RUNDIVISOR * 100))); //movementAPCost = (INT16)(DOUBLE)( (sTileCost / RUNDIVISOR) ); break; break; case WALKING: @@ -1140,8 +1313,10 @@ int AStarPathfinder::CalcAP(int const terrainCost) break; } - if (terrainCost == TRAVELCOST_FENCE) { - switch( movementModeToUseForAPs ) { + if (terrainCost == TRAVELCOST_FENCE) + { + switch( movementModeToUseForAPs ) + { case RUNNING: case WALKING : // Here pessimistically assume the path will continue after hopping the fence @@ -1158,8 +1333,10 @@ int AStarPathfinder::CalcAP(int const terrainCost) return -1; } } - else if (terrainCost == TRAVELCOST_NOT_STANDING) { - switch(movementModeToUseForAPs) { + else if (terrainCost == TRAVELCOST_NOT_STANDING) + { + switch(movementModeToUseForAPs) + { case RUNNING: case WALKING : // charge crouch APs for ducking head! @@ -1170,8 +1347,11 @@ int AStarPathfinder::CalcAP(int const terrainCost) break; } } - else if (fGoingThroughDoor) { - movementAPCost += AP_OPEN_DOOR; + else if (fGoingThroughDoor) + { + // Uh, there IS a cost to close the door too! + // Then if the door has to be unlocked to open there's the cost of locking and unlocking. + movementAPCost += AP_OPEN_DOOR + AP_OPEN_DOOR; fGoingThroughDoor = FALSE; } return movementAPCost; @@ -1180,190 +1360,225 @@ int AStarPathfinder::CalcAP(int const terrainCost) int AStarPathfinder::CalcG(int* pPrevCost) { //how much is admission to the next tile - if ( gfPathAroundObstacles == false) { + if ( gfPathAroundObstacles == false) + { return TRAVELCOST_FLAT; } - int nextCost = gubWorldMovementCosts[ CurrentNodeIndex ][ direction ][ onRooftop ]; + int nextCost = gubWorldMovementCosts[ CurrentNode ][ direction ][ onRooftop ]; *pPrevCost = nextCost; //if we are holding down shift and finding a direct path, count non obstacles as flat terrain - if (gfPlotDirectPath && nextCost < NOPASS && nextCost != 0) { + if (gfPlotDirectPath && nextCost < NOPASS && nextCost != 0) + { return TRAVELCOST_FLAT; } //performance: if nextCost is low then do not do many many if == checks - if ( nextCost >= TRAVELCOST_FENCE ) { + if ( nextCost >= TRAVELCOST_FENCE ) + { //ATE: Check for differences from reality // Is next cost an obstcale - if ( nextCost == TRAVELCOST_HIDDENOBSTACLE ) { - if ( fPathingForPlayer ) { - - // Is this obstcale a hidden tile that has not been revealed yet? + if ( nextCost == TRAVELCOST_HIDDENOBSTACLE ) + { + if ( fPathingForPlayer ) + { + // Is this obstacle a hidden tile that has not been revealed yet? BOOLEAN fHiddenStructVisible; - if( DoesGridnoContainHiddenStruct( CurrentNodeIndex, &fHiddenStructVisible ) ) { - + if( DoesGridnoContainHiddenStruct( CurrentNode, &fHiddenStructVisible ) ) + { // Are we not visible, if so use terrain costs! - if ( !fHiddenStructVisible ) { - + if ( !fHiddenStructVisible ) + { // Set cost of terrain! - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } } } } - else if ( nextCost == TRAVELCOST_NOT_STANDING ) { + else if ( nextCost == TRAVELCOST_NOT_STANDING ) + { // for path plotting purposes, use the terrain value - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } - else if ( nextCost == TRAVELCOST_EXITGRID ) { - if (gfPlotPathToExitGrid) { + else if ( nextCost == TRAVELCOST_EXITGRID ) + { + if (gfPlotPathToExitGrid) + { // replace with terrain cost so that we can plot path, otherwise is obstacle - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } } - else if ( nextCost == TRAVELCOST_FENCE && fNonFenceJumper ) { + else if ( nextCost == TRAVELCOST_FENCE && fNonFenceJumper ) + { return -1; } - else if ( IS_TRAVELCOST_DOOR( nextCost ) ) { + else if ( IS_TRAVELCOST_DOOR( nextCost ) ) + { // don't let anyone path diagonally through doors! - if (IsDiagonal(direction) == true) { + if (IsDiagonal(direction) == true) + { return -1; } - INT16 iDoorGridNo = CurrentNodeIndex; + INT16 iDoorGridNo = CurrentNode; bool fDoorIsObstacleIfClosed = FALSE; bool fDoorIsOpen = false; - switch( nextCost ) { + switch( nextCost ) + { case TRAVELCOST_DOOR_CLOSED_HERE: fDoorIsObstacleIfClosed = TRUE; - iDoorGridNo = CurrentNodeIndex; + iDoorGridNo = CurrentNode; break; case TRAVELCOST_DOOR_CLOSED_N: fDoorIsObstacleIfClosed = TRUE; - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTH ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTH ]; break; case TRAVELCOST_DOOR_CLOSED_W: fDoorIsObstacleIfClosed = TRUE; - iDoorGridNo = CurrentNodeIndex + dirDelta[ WEST ]; + iDoorGridNo = CurrentNode + dirDelta[ WEST ]; break; case TRAVELCOST_DOOR_OPEN_HERE: - iDoorGridNo = CurrentNodeIndex; + iDoorGridNo = CurrentNode; break; case TRAVELCOST_DOOR_OPEN_N: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTH ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTH ]; break; case TRAVELCOST_DOOR_OPEN_NE: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTHEAST ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTHEAST ]; break; case TRAVELCOST_DOOR_OPEN_E: - iDoorGridNo = CurrentNodeIndex + dirDelta[ EAST ]; + iDoorGridNo = CurrentNode + dirDelta[ EAST ]; break; case TRAVELCOST_DOOR_OPEN_SE: - iDoorGridNo = CurrentNodeIndex + dirDelta[ SOUTHEAST ]; + iDoorGridNo = CurrentNode + dirDelta[ SOUTHEAST ]; break; case TRAVELCOST_DOOR_OPEN_S: - iDoorGridNo = CurrentNodeIndex + dirDelta[ SOUTH ]; + iDoorGridNo = CurrentNode + dirDelta[ SOUTH ]; break; case TRAVELCOST_DOOR_OPEN_SW: - iDoorGridNo = CurrentNodeIndex + dirDelta[ SOUTHWEST ]; + iDoorGridNo = CurrentNode + dirDelta[ SOUTHWEST ]; break; case TRAVELCOST_DOOR_OPEN_W: - iDoorGridNo = CurrentNodeIndex + dirDelta[ WEST ]; + iDoorGridNo = CurrentNode + dirDelta[ WEST ]; break; case TRAVELCOST_DOOR_OPEN_NW: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTHWEST ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTHWEST ]; break; case TRAVELCOST_DOOR_OPEN_N_N: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTH ] + dirDelta[ NORTH ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTH ] + dirDelta[ NORTH ]; break; case TRAVELCOST_DOOR_OPEN_NW_N: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTHWEST ] + dirDelta[ NORTH ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTHWEST ] + dirDelta[ NORTH ]; break; case TRAVELCOST_DOOR_OPEN_NE_N: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTHEAST ] + dirDelta[ NORTH ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTHEAST ] + dirDelta[ NORTH ]; break; case TRAVELCOST_DOOR_OPEN_W_W: - iDoorGridNo = CurrentNodeIndex + dirDelta[ WEST ] + dirDelta[ WEST ]; + iDoorGridNo = CurrentNode + dirDelta[ WEST ] + dirDelta[ WEST ]; break; case TRAVELCOST_DOOR_OPEN_SW_W: - iDoorGridNo = CurrentNodeIndex + dirDelta[ SOUTHWEST ] + dirDelta[ WEST ]; + iDoorGridNo = CurrentNode + dirDelta[ SOUTHWEST ] + dirDelta[ WEST ]; break; case TRAVELCOST_DOOR_OPEN_NW_W: - iDoorGridNo = CurrentNodeIndex + dirDelta[ NORTHWEST ] + dirDelta[ WEST ]; + iDoorGridNo = CurrentNode + dirDelta[ NORTHWEST ] + dirDelta[ WEST ]; break; default: break; } - if ( fPathingForPlayer && gpWorldLevelData[ iDoorGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_DOOR_STATUS_PRESENT ) { + if ( fPathingForPlayer && gpWorldLevelData[ iDoorGridNo ].ubExtFlags[0] & MAPELEMENT_EXT_DOOR_STATUS_PRESENT ) + { // check door status DOOR_STATUS* pDoorStatus = GetDoorStatus( iDoorGridNo ); - if (pDoorStatus) { + if (pDoorStatus) + { fDoorIsOpen = (pDoorStatus->ubFlags & DOOR_PERCEIVED_OPEN) != 0; } - else { + else + { // door destroyed? - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } } - else { + else + { // check door structure STRUCTURE* pDoorStructure = FindStructure( iDoorGridNo, STRUCTURE_ANYDOOR ); - if (pDoorStructure) { + if (pDoorStructure) + { fDoorIsOpen = (pDoorStructure->fFlags & STRUCTURE_OPEN) != 0; } - else { + else + { // door destroyed? - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } } // now determine movement cost... if it hasn't been changed already - if ( IS_TRAVELCOST_DOOR( nextCost ) ) { - if (fDoorIsOpen) { - if (fDoorIsObstacleIfClosed) { - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + if ( IS_TRAVELCOST_DOOR( nextCost ) ) + { + if (fDoorIsOpen) + { + if (fDoorIsObstacleIfClosed) + { + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } - else { + else + { nextCost = TRAVELCOST_OBSTACLE; } } - else { - if (fDoorIsObstacleIfClosed) { + else + { + if (fDoorIsObstacleIfClosed) + { // door is closed and this should be an obstacle, EXCEPT if we are calculating // a path for an enemy or NPC with keys - if ( fPathingForPlayer || ( pSoldier && (pSoldier->uiStatusFlags & SOLDIER_MONSTER || pSoldier->uiStatusFlags & SOLDIER_ANIMAL) ) ) { + if ( fPathingForPlayer || ( pSoldier && (pSoldier->uiStatusFlags & (SOLDIER_MONSTER | SOLDIER_ANIMAL) ) ) ) + { nextCost = TRAVELCOST_OBSTACLE; } - else { + else + { // have to check if door is locked and NPC does not have keys! + // This function has an inaccurate name. It is actually checking if the door has LOCK info. DOOR* pDoor = FindDoorInfoAtGridNo( iDoorGridNo ); - if (pDoor) { - if (!pDoor->fLocked || pSoldier->bHasKeys) { + if (pDoor) + { + if (!pDoor->fLocked || pSoldier->bHasKeys) + { // add to AP cost - if (maxAPBudget) { + //if (maxAPBudget) + { fGoingThroughDoor = TRUE; } - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } - else { + else + { nextCost = TRAVELCOST_OBSTACLE; } } - else { - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + else + { + // The door is closed, so we still have to open it + fGoingThroughDoor = TRUE; + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } } } - else { - nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNodeIndex ].ubTerrainID ]; + else + { + nextCost = gTileTypeMovementCost[ gpWorldLevelData[ CurrentNode ].ubTerrainID ]; } } } } - else if ( fNonSwimmer && (nextCost == TRAVELCOST_SHORE || nextCost == TRAVELCOST_KNEEDEEP || nextCost == TRAVELCOST_DEEPWATER ) ) { + else if ( fNonSwimmer && ISWATER( gpWorldLevelData[ CurrentNode ].ubTerrainID)) + { // creatures and animals can't go in water nextCost = TRAVELCOST_OBSTACLE; } @@ -1371,22 +1586,27 @@ int AStarPathfinder::CalcG(int* pPrevCost) // Apr. '96 - moved up be ahead of AP_Budget stuff if ( nextCost >= NOPASS ) // || ( nextCost == TRAVELCOST_DOOR ) ) + { return -1; - + } // make water cost attractive for water to water paths - if (bWaterToWater && ISWATER(nextCost) ) { - nextCost = EASYWATERCOST; - } + // Why? If a path through water gets you there sooner, you shouldn't need + // artificial inflation to figure that out. And if not, then get out of the water! + //if (bWaterToWater && ISWATER(nextCost) ) + //{ + // nextCost = EASYWATERCOST; + //} return nextCost; }//end CalcG int AStarPathfinder::CalcH() { - if ( fCopyReachable ) { - //if this is a reachability test, turn AStar into breadth first Djikstra - return 1000; + if ( fCopyReachable ) + { + //if this is a reachability test, turn AStar into breadth first Dijkstra + return 0; // Technically speaking, 0 makes it Dijkstra best-first search } // #define ESTIMATEC ( ( (dx= y) { - return TRAVELCOST_FLAT * (x * 10 + y * 4); + + //I changed the cost scale to APs, so TRAVELCOST * anything is a gross overestimate. Let's do something + //more on an AP scale. The minimum between X and Y is calculated as a diagonal traversal and the remainder + //is calculated as straight traversal. + + GridNode *n1, *n2; + + n1 = GridNode::MapXY + CurrentNode; + n2 = GridNode::MapXY + DestNode; + + int x = abs(n1->x - n2->x); + int y = abs(n1->y - n2->y); +#if 0 + if (x >= y) + { + return this->travelcostDiag * y + this->travelcostOrth * (x-y); } - else { - return TRAVELCOST_FLAT * (y * 10 + x * 4); + else + { + return this->travelcostDiag * x + this->travelcostOrth * (y-x); } +#else + // Try a real distance method. This should underestimate in some cases + // However, the distances need to be increased for the moment because running orthogonal is 1AP while running diagonal is 2AP + // so the total to reach a diagonal tile is identical for 2 moves. So we have to trick the pathing calc into thinking it's + // a longer distance and also calculate the other costs accordingly. + + x *= 100; + y *= 100; + + int d = x*x + y*y; + int r = 1200; // Just a guess + + if (d == 0) + { + return d; + } + + while (1) + { + int gr = (r + (d/r)) / 2; + if (gr == r || gr == r+1) + { + break; + } + r = gr; + } + + return r * travelcostOrth; +#endif } #ifdef ASTAR_USING_EXTRACOVER @@ -1718,14 +1980,16 @@ int AStarPathfinder::CalcCoverValue(INT16 sMyGridNo, INT32 iMyThreat, INT32 iMyA void AStarPathfinder::InitVehicle() { fMultiTile = ((pSoldier->uiStatusFlags & SOLDIER_MULTITILE) != 0); - if (fMultiTile) { + if (fMultiTile) + { // Get animation surface... // Chris_C... change this to use parameter..... UINT16 usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, movementMode ); // Get structure ref... pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, movementMode ); - if ( pStructureFileRef ) { + if ( pStructureFileRef ) + { fContinuousTurnNeeded = ( ( pSoldier->uiStatusFlags & (SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE) ) != 0 ); /* @@ -1739,24 +2003,29 @@ void AStarPathfinder::InitVehicle() fTurnSlow = TRUE; } */ - if ( gfEstimatePath ) { + if ( gfEstimatePath ) + { bOKToAddStructID = IGNORE_PEOPLE_STRUCTURE_ID; } - else if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) { + else if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL ) + { bOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID; } - else { + else + { bOKToAddStructID = INVALID_STRUCTURE_ID; } } - else { + else + { // turn off multitile pathing fMultiTile = FALSE; fContinuousTurnNeeded = FALSE; } } - else { + else + { fContinuousTurnNeeded = FALSE; } return; @@ -1780,20 +2049,26 @@ int AStarPathfinder::VehicleObstacleCheck() */ int iStructIndex; - if ( bLoopState == LOOPING_REVERSE ) { + if ( bLoopState == LOOPING_REVERSE ) + { iStructIndex = gOppositeDirection[ gOneCDirection[ direction ] ]; } else { iStructIndex = gOneCDirection[ direction ]; } - if (fMultiTile) { - if ( fContinuousTurnNeeded ) { - if ( direction != lastDir ) { - if ( !OkayToAddStructureToWorld( (INT16) CurrentNodeIndex, onRooftop, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), bOKToAddStructID ) ) { + if (fMultiTile) + { + if ( fContinuousTurnNeeded ) + { + if ( direction != lastDir ) + { + if ( !OkayToAddStructureToWorld( CurrentNode, onRooftop, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), bOKToAddStructID ) ) + { // we have to abort this loop and possibly reset the loop conditions to // search in the other direction (if we haven't already done the other dir) - if (bLoopState == LOOPING_CLOCKWISE) { + if (bLoopState == LOOPING_CLOCKWISE) + { startDir = lastDir; endDir = direction; bLoopState = LOOPING_COUNTERCLOCKWISE; // backwards @@ -1803,7 +2078,8 @@ int AStarPathfinder::VehicleObstacleCheck() return 1; //goto NEXTDIR; } - else if ( bLoopState == LOOPING_COUNTERCLOCKWISE && !fCheckedBehind ) { + else if ( bLoopState == LOOPING_COUNTERCLOCKWISE && !fCheckedBehind ) + { // check rear dir bLoopState = LOOPING_REVERSE; @@ -1816,7 +2092,8 @@ int AStarPathfinder::VehicleObstacleCheck() return 1; //goto NEXTDIR; } - else { + else + { // done //goto ENDOFLOOP; return 2; @@ -1835,7 +2112,11 @@ int AStarPathfinder::VehicleObstacleCheck() // because of the order in which animations are stored (dir 7 first, // then 0 1 2 3 4 5 6), we must subtract 1 from the direction // ATE: Send in our existing structure ID so it's ignored! - if (!OkayToAddStructureToWorld( (INT16) CurrentNodeIndex, onRooftop, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), bOKToAddStructID ) ) { + // 0verhaul: The turn happens before the move, and the check for change dir happens on the turn. So + // we have to check whether the struct can be added at the PARENT node, as well as the intended new node. + if (!OkayToAddStructureToWorld( ParentNode, onRooftop, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), bOKToAddStructID ) || + !OkayToAddStructureToWorld( CurrentNode, onRooftop, &(pStructureFileRef->pDBStructureRef[ iStructIndex ]), bOKToAddStructID )) + { return 1; //goto NEXTDIR; } @@ -1844,54 +2125,67 @@ int AStarPathfinder::VehicleObstacleCheck() } #endif -bool AStarPathfinder::CanTraverse() +bool AStarPathfinder::WantToTraverse() { // 0verhaul: Cannot change direction over a fence! - if (GetPrevCost(ParentNode) == TRAVELCOST_FENCE && GetDirection(ParentNode) != direction) - { - return false; - } + // Decided on a different way to handle this + //if (GetPrevCost(ParentNode) == TRAVELCOST_FENCE && GetDirection(ParentNode) != direction) + //{ + // return false; + //} - if ( fVisitSpotsOnlyOnce && GetAStarStatus(CurrentNode) != AStar_Init ) { + if ( fVisitSpotsOnlyOnce && GetAStarStatus(CurrentNode) != AStar_Init ) + { // on a "reachable" test, never revisit locations, even if open! return false; } //checks distance, mines, cliffs - if (gubNPCDistLimit) { - if ( gfNPCCircularDistLimit ) { - if (PythSpacesAway( StartNode, CurrentNode) > gubNPCDistLimit) { + if (gubNPCDistLimitSq) + { + if ( gfNPCCircularDistLimit ) + { + if (PythSpacesAway( StartNode, CurrentNode) > gubNPCDistLimitSq) + { return false; } } - else { - if (SpacesAway( StartNode, CurrentNode) > gubNPCDistLimit) { + else + { + if (SpacesAway( StartNode, CurrentNode) > gubNPCDistLimit) + { return false; } } } // AI check for mines - if ( gpWorldLevelData[CurrentNodeIndex].uiFlags & MAPELEMENT_ENEMY_MINE_PRESENT && pSoldier->bSide != 0) { + if ( gpWorldLevelData[CurrentNode].uiFlags & MAPELEMENT_ENEMY_MINE_PRESENT && pSoldier->bSide != 0) + { return false; } // WANNE: Know mines (for enemy or player) do not explode - BEGIN - if ( gpWorldLevelData[CurrentNodeIndex].uiFlags & (MAPELEMENT_ENEMY_MINE_PRESENT | MAPELEMENT_PLAYER_MINE_PRESENT) ) { + if ( gpWorldLevelData[CurrentNode].uiFlags & (MAPELEMENT_ENEMY_MINE_PRESENT | MAPELEMENT_PLAYER_MINE_PRESENT) ) + { if (pSoldier->bSide == 0) { // For our team, skip a location with a known mines unless it is the end of our // path; for others on our side, skip such locations completely; - if (pSoldier->bTeam != gbPlayerNum || CurrentNode != DestNode) { - if (gpWorldLevelData[CurrentNodeIndex].uiFlags & MAPELEMENT_PLAYER_MINE_PRESENT) { + if (pSoldier->bTeam != gbPlayerNum || CurrentNode != DestNode) + { + if (gpWorldLevelData[CurrentNode].uiFlags & MAPELEMENT_PLAYER_MINE_PRESENT) + { return false; } } } - else { + else + { // For the enemy, always skip known mines - if (gpWorldLevelData[CurrentNodeIndex].uiFlags & MAPELEMENT_ENEMY_MINE_PRESENT) { + if (gpWorldLevelData[CurrentNode].uiFlags & MAPELEMENT_ENEMY_MINE_PRESENT) + { return false; } } @@ -1900,7 +2194,8 @@ bool AStarPathfinder::CanTraverse() //ATE: Movement onto cliffs? Check vs the soldier's gridno height // CJC: PREVIOUS LOCATION's height - if ( gpWorldLevelData[ CurrentNodeIndex ].sHeight != gpWorldLevelData[ ParentNodeIndex ].sHeight ) { + if ( gpWorldLevelData[ CurrentNode ].sHeight != gpWorldLevelData[ ParentNode ].sHeight ) + { return false; } @@ -1911,10 +2206,12 @@ bool AStarPathfinder::IsSomeoneInTheWay() { // if contemplated tile is NOT final dest and someone there, disqualify route // when doing a reachable test, ignore all locations with people in them - if (fPathAroundPeople && ( (CurrentNode != DestNode) || fCopyReachable) ) { + if (fPathAroundPeople && ( (CurrentNode != DestNode) || fCopyReachable) ) + { // ATE: ONLY cancel if they are moving..... - UINT8 ubMerc = WhoIsThere2( (UINT16) CurrentNodeIndex, pSoldier->bLevel); - if ( ubMerc < NOBODY && ubMerc != pSoldier->ubID ) { + UINT8 ubMerc = WhoIsThere2( (UINT16) CurrentNode, pSoldier->bLevel); + if ( ubMerc < NOBODY && ubMerc != pSoldier->ubID ) + { // Check for movement.... //if ( fTurnBased || ( (Menptr[ ubMerc ].sFinalDestination == Menptr[ ubMerc ].sGridNo) || (Menptr[ ubMerc ].fDelayedMovement) ) ) //{ @@ -1931,7 +2228,6 @@ bool AStarPathfinder::IsSomeoneInTheWay() #endif//end ifdef USE_ASTAR_PATHS - INT8 RandomSkipListLevel( void ) { INT8 bLevel = 1; @@ -1999,13 +2295,18 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT16 sDestination, INT8 ubLevel, INT16 usMo { #ifdef USE_ASTAR_PATHS int retVal = ASTAR::AStarPathfinder::GetInstance().GetPath(s, sDestination, ubLevel, usMovementMode, bCopy, fFlags); - if (retVal || sDestination == NOWHERE) { + if (retVal) { return retVal; } else { DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "ASTAR path failed!" ) ); } -#endif + +// if (sDestination == NOWHERE) + { + return 0; + } +#else //__try //{ INT32 iDestination = sDestination, iOrigination; @@ -2205,7 +2506,7 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT16 sDestination, INT8 ubLevel, INT16 usMo } else { - gubNPCAPBudget -= ubAPCost; + gubNPCAPBudget = gubNPCAPBudget - ubAPCost; } } @@ -2326,7 +2627,7 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT16 sDestination, INT8 ubLevel, INT16 usMo SETLOC( *pQueueHead, iOrigination ); pQueueHead->usCostSoFar = MAXCOST; - pQueueHead->bLevel = iMaxSkipListLevel - 1; + pQueueHead->bLevel = (INT8) (iMaxSkipListLevel - 1); pClosedHead->pNext[0] = pClosedHead; pClosedHead->pNext[1] = pClosedHead; @@ -2344,10 +2645,10 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT16 sDestination, INT8 ubLevel, INT16 usMo } else { - pathQ[1].usCostToGo = REMAININGCOST( &(pathQ[1]) ); + pathQ[1].usCostToGo = (INT16) REMAININGCOST( &(pathQ[1]) ); } pathQ[1].usTotalCost = pathQ[1].usCostSoFar + pathQ[1].usCostToGo; - pathQ[1].ubLegDistance = LEGDISTANCE( iLocX, iLocY, iDestX, iDestY ); + pathQ[1].ubLegDistance = (UINT8)LEGDISTANCE( iLocX, iLocY, iDestX, iDestY ); pathQ[1].bLevel = 1; pQueueHead->pNext[0] = &( pathQ[1] ); iSkipListSize++; @@ -3017,7 +3318,7 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT16 sDestination, INT8 ubLevel, INT16 usMo case RUNNING: case ADULTMONSTER_WALKING: // save on casting - ubAPCost = ubAPCost * 10 / ( (UINT8) (RUNDIVISOR * 10)); + ubAPCost = (UINT8)(ubAPCost * 10 / ( (UINT8) (RUNDIVISOR * 10))); //ubAPCost = (INT16)(DOUBLE)( (sTileCost / RUNDIVISOR) ); break; break; case WALKING: @@ -3280,8 +3581,8 @@ INT32 FindBestPath(SOLDIERTYPE *s , INT16 sDestination, INT8 ubLevel, INT16 usMo pNewPtr->usCostToGo = (UINT16) REMAININGCOST(pNewPtr); } - pNewPtr->usTotalCost = newTotCost + pNewPtr->usCostToGo; - pNewPtr->ubLegDistance = LEGDISTANCE( iLocX, iLocY, iDestX, iDestY ); + pNewPtr->usTotalCost = (UINT16) (newTotCost + pNewPtr->usCostToGo); + pNewPtr->ubLegDistance = (UINT8) LEGDISTANCE( iLocX, iLocY, iDestX, iDestY ); if (gubNPCAPBudget) { @@ -3541,6 +3842,7 @@ ENDOFLOOP: } + //Assert(!iCnt); return(iCnt); } @@ -3565,6 +3867,7 @@ ENDOFLOOP: //{ // return (0); //} +#endif } void GlobalReachableTest( INT16 sStartGridNo ) @@ -3683,7 +3986,7 @@ void RoofReachableTest( INT16 sStartGridNo, UINT8 ubBuildingID ) gubBuildingInfoToSet = ubBuildingID; ReconfigurePathAI( ABSMAX_SKIPLIST_LEVEL, ABSMAX_TRAIL_TREE, ABSMAX_PATHQ ); - FindBestPath( &s, NOWHERE, 1, WALKING, COPYREACHABLE, 0 ); + FindBestPath( &s, NOWHERE, 1, WALKING, FINDCLIMBPOINTS, 0 ); RestorePathAIToDefaults(); // set start position to reachable since path code sets it unreachable @@ -3790,7 +4093,7 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl // distance limit to reduce the cost of plotting a path to a location we can't reach // For now, use known hight adjustment - if ( gfRecalculatingExistingPathCost || FindBestPath( pSold, sDestGridno, (INT8)pSold->bLevel, usMovementMode, bCopyRoute, 0 ) ) + if ( gfRecalculatingExistingPathCost || FindBestPath( pSold, sDestGridno, pSold->bLevel, usMovementMode, bCopyRoute, 0 ) ) { // if soldier would be STARTING to run then he pays a penalty since it takes time to // run full speed @@ -3801,12 +4104,12 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl } // Add to points, those needed to start from different stance! - sPoints += MinAPsToStartMovement( pSold, usMovementMode ); + sPoints = sPoints + MinAPsToStartMovement( pSold, usMovementMode ); // We should reduce points for starting to run if first tile is a fence... - sTestGridno = NewGridNo(pSold->sGridNo, DirectionInc( guiPathingData[0])); - if ( gubWorldMovementCosts[ sTestGridno ][ (INT8)guiPathingData[0] ][ pSold->bLevel] == TRAVELCOST_FENCE ) + sTestGridno = NewGridNo(pSold->sGridNo, DirectionInc( (UINT8)guiPathingData[0])); + if ( gubWorldMovementCosts[ sTestGridno ][ guiPathingData[0] ][ pSold->bLevel] == TRAVELCOST_FENCE ) { if ( usMovementMode == RUNNING && pSold->usAnimState != RUNNING ) { @@ -3834,8 +4137,8 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl } */ - sPoints += sAnimCost; - gusAPtsToMove += sAnimCost; + sPoints = sPoints + sAnimCost; + gusAPtsToMove = gusAPtsToMove + sAnimCost; @@ -3859,7 +4162,7 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl // what is the next gridno in the path? sOldGrid = sTempGrid; - sTempGrid = NewGridNo(sTempGrid, DirectionInc( guiPathingData[iCnt])); + sTempGrid = NewGridNo(sTempGrid, DirectionInc( (UINT8)guiPathingData[iCnt])); // Get switch value... sSwitchValue = gubWorldMovementCosts[ sTempGrid ][ (INT8)guiPathingData[iCnt] ][ pSold->bLevel]; @@ -3884,7 +4187,7 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl // ATE: If we have a 'special cost, like jump fence... if ( sSwitchValue == TRAVELCOST_FENCE ) { - sPoints += sTileCost; + sPoints = sPoints + sTileCost; bIgnoreNextCost = TRUE; @@ -3907,7 +4210,7 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl sExtraCostStand++; } - sPoints += sExtraCostStand; + sPoints = sPoints + sExtraCostStand; } break; @@ -3915,7 +4218,7 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl // Add cost to stand once there BEFORE.... sExtraCostSwat += AP_CROUCH; - sPoints += sExtraCostSwat; + sPoints = sPoints + sExtraCostSwat; break; case CRAWLING: @@ -3951,7 +4254,7 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl case WALKING : sPoints += (sTileCost + WALKCOST) + sExtraCostStand; break; case SWATTING: sPoints += (sTileCost + SWATCOST) + sExtraCostSwat; break; case CRAWLING: sPoints += (sTileCost + CRAWLCOST) + sExtraCostCrawl; break; - default : sPoints += sTileCost; break; + default : sPoints = sPoints + sTileCost; break; } } } @@ -4045,12 +4348,12 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl if ( (gTacticalStatus.uiFlags & REALTIME ) || !(gTacticalStatus.uiFlags & INCOMBAT ) ) { // find out which color we're using - usTileIndex += sFootOrder[ 4 ]; + usTileIndex = usTileIndex + sFootOrder[ 4 ]; } else // turn based { // find out which color we're using - usTileIndex += sFootOrder[sFootOrderIndex]; + usTileIndex = usTileIndex + sFootOrder[sFootOrderIndex]; } @@ -4101,12 +4404,12 @@ INT16 PlotPath( SOLDIERTYPE *pSold, INT16 sDestGridno, INT8 bCopyRoute, INT8 bPl if ( (gTacticalStatus.uiFlags & REALTIME ) || !(gTacticalStatus.uiFlags & INCOMBAT ) ) { // find out which color we're using - usTileIndex += sFootOrder[ 4 ]; + usTileIndex = usTileIndex + sFootOrder[ 4 ]; } else // turnbased { // find out which color we're using - usTileIndex += sFootOrder[sFootOrderIndex]; + usTileIndex = usTileIndex + sFootOrder[sFootOrderIndex]; } diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index 7f209093..12b58636 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -83,12 +83,21 @@ INT16 TerrainActionPoints( SOLDIERTYPE *pSoldier, INT16 sGridno, INT8 bDir, INT8 } else if (IS_TRAVELCOST_DOOR( sSwitchValue )) { + // Can't travel diagonally through a door + if (bDir & 1) + { + return -1; + } sSwitchValue = DoorTravelCost( pSoldier, sGridno, (UINT8) sSwitchValue, (BOOLEAN) (pSoldier->bTeam == gbPlayerNum), NULL ); } - - if (sSwitchValue >= TRAVELCOST_BLOCKED && sSwitchValue != TRAVELCOST_DOOR ) + else if (gfPlotPathToExitGrid && sSwitchValue == TRAVELCOST_EXITGRID) { - return(100); // Cost too much to be considered! + sSwitchValue = gTileTypeMovementCost[ gpWorldLevelData[ sGridno ].ubTerrainID ]; + } + + if (sSwitchValue >= TRAVELCOST_BLOCKED && sSwitchValue != TRAVELCOST_DOOR) + { + return(-1); // Cost too much to be considered! } switch( sSwitchValue ) @@ -115,7 +124,7 @@ INT16 TerrainActionPoints( SOLDIERTYPE *pSoldier, INT16 sGridno, INT8 bDir, INT8 case TRAVELCOST_VEINMID : sAPCost += AP_MOVEMENT_FLAT; break; */ - case TRAVELCOST_DOOR : sAPCost += AP_MOVEMENT_FLAT; + case TRAVELCOST_DOOR : sAPCost += AP_MOVEMENT_FLAT + AP_OPEN_DOOR + AP_OPEN_DOOR; // Include open and close costs! break; // cost for jumping a fence REPLACES all other AP costs! @@ -265,6 +274,10 @@ INT16 ActionPointCost( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDir, UINT16 u // get the tile cost for that tile based on WALKING sTileCost = TerrainActionPoints( pSoldier, sGridNo, bDir, pSoldier->bLevel ); + if (sTileCost == -1) + { + return 100; + } // Get switch value... sSwitchValue = gubWorldMovementCosts[ sGridNo ][ bDir ][ pSoldier->bLevel ]; @@ -343,6 +356,10 @@ INT16 EstimateActionPointCost( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDir, // get the tile cost for that tile based on WALKING sTileCost = TerrainActionPoints( pSoldier, sGridNo, bDir, pSoldier->bLevel ); + if (sTileCost == -1) + { + return 100; + } // so, then we must modify it for other movement styles and accumulate if (sTileCost > 0) @@ -1484,7 +1501,7 @@ UINT8 MinAPsToPunch(SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubAddTurningCost INT8 MinPtsToMove(SOLDIERTYPE *pSoldier) { // look around all 8 directions and return lowest terrain cost - INT8 cnt; + UINT8 cnt; INT16 sLowest=127; INT16 sGridno,sCost; @@ -1493,9 +1510,9 @@ INT8 MinPtsToMove(SOLDIERTYPE *pSoldier) return( (INT8)sLowest); } - for (cnt=0; cnt <= 7; cnt++) + for (cnt=0; cnt < 8; cnt++) { - sGridno = NewGridNo(pSoldier->sGridNo, DirectionInc( cnt )); + sGridno = NewGridNo(pSoldier->sGridNo,DirectionInc(cnt)); if (sGridno != pSoldier->sGridNo) { if ( (sCost=ActionPointCost( pSoldier, sGridno, cnt , pSoldier->usUIMovementMode ) ) < sLowest ) @@ -1513,7 +1530,7 @@ INT8 PtsToMoveDirection(SOLDIERTYPE *pSoldier, INT8 bDirection ) INT8 bOverTerrainType; UINT16 usMoveModeToUse; - sGridno = NewGridNo( pSoldier->sGridNo, DirectionInc( bDirection ) ); + sGridno = NewGridNo( pSoldier->sGridNo, DirectionInc(bDirection ) ); usMoveModeToUse = pSoldier->usUIMovementMode; diff --git a/Tactical/Rain.h b/Tactical/Rain.h index b22ba4f0..f7c78c74 100644 --- a/Tactical/Rain.h +++ b/Tactical/Rain.h @@ -3,5 +3,6 @@ BOOLEAN IsItAllowedToRenderRain(); void RenderRain(); +void ResetRain(); #endif \ No newline at end of file diff --git a/Tactical/Rotting Corpses.cpp b/Tactical/Rotting Corpses.cpp index 580bb76e..9a605d00 100644 --- a/Tactical/Rotting Corpses.cpp +++ b/Tactical/Rotting Corpses.cpp @@ -1817,7 +1817,7 @@ void GetBloodFromCorpse( SOLDIERTYPE *pSoldier ) { ROTTING_CORPSE *pCorpse; INT8 bObjSlot; - OBJECTTYPE Object; + OBJECTTYPE Object = {}; // OK, get corpse pCorpse = &( gRottingCorpse[ pSoldier->uiPendingActionData4 ] ); @@ -1847,7 +1847,7 @@ void GetBloodFromCorpse( SOLDIERTYPE *pSoldier ) if ( bObjSlot != NO_SLOT ) { - SwapObjs( &(pSoldier->inv[ bObjSlot ] ), &Object ); + SwapObjs( pSoldier, bObjSlot, &Object ); } } diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 2e086fd2..1582e26e 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -2020,7 +2020,9 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) // ReduceAttackBusyCount( pSoldier->ubSuppressorID, FALSE ); // } - if ( pSoldier->usPendingAnimation == NO_PENDING_ANIMATION && ( pSoldier->fTurningFromPronePosition != 3 ) && ( pSoldier->fTurningFromPronePosition != 1 ) ) + if ( pSoldier->usPendingAnimation == NO_PENDING_ANIMATION && + ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) && + ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ON ) ) { if ( gTacticalStatus.ubAttackBusyCount == 0 ) { @@ -2033,7 +2035,10 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) // Check to see if we have changed stance and need to update visibility if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STANCECHANGEANIM) { - if ( pSoldier->usPendingAnimation == NO_PENDING_ANIMATION && gTacticalStatus.ubAttackBusyCount == 0 && pSoldier->fTurningFromPronePosition != 3 && pSoldier->fTurningFromPronePosition != 1 ) + if ( pSoldier->usPendingAnimation == NO_PENDING_ANIMATION && + gTacticalStatus.ubAttackBusyCount == 0 && + pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE && + pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ON ) { HandleSight(pSoldier,SIGHT_LOOK | SIGHT_RADIO | SIGHT_INTERRUPT ); } @@ -2057,7 +2062,12 @@ BOOLEAN AdjustToNextAnimationFrame( SOLDIERTYPE *pSoldier ) } // Have we finished opening doors? - if ( pSoldier->usAnimState == END_OPEN_DOOR || pSoldier->usAnimState == END_OPEN_DOOR_CROUCHED || pSoldier->usAnimState == CRIPPLE_CLOSE_DOOR || pSoldier->usAnimState == CRIPPLE_END_OPEN_DOOR ) + // 0verhaul: Added additional check: Are we told to stop at this point, maybe due to being interrupted? + if ( !pSoldier->fNoAPToFinishMove && + (pSoldier->usAnimState == END_OPEN_DOOR || + pSoldier->usAnimState == END_OPEN_DOOR_CROUCHED || + pSoldier->usAnimState == CRIPPLE_CLOSE_DOOR || + pSoldier->usAnimState == CRIPPLE_END_OPEN_DOOR ) ) { // Are we told to continue movement...? if ( pSoldier->bEndDoorOpenCode == 1 ) @@ -3565,7 +3575,7 @@ void CheckForAndHandleSoldierIncompacitated( SOLDIERTYPE *pSoldier ) return; } } - else if ( pSoldier->usAnimState == GENERIC_HIT_CROUCH ) + else if ( pSoldier->usAnimState == GENERIC_HIT_CROUCH || pSoldier->usAnimState == CIV_COWER_HIT) { ChangeSoldierState( pSoldier, FALLFORWARD_FROMHIT_CROUCH, 0 , FALSE); BeginTyingToFall( pSoldier ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 8d91974f..fd1c1db1 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -94,6 +94,7 @@ #include "Campaign Types.h" #include "Strategic Status.h" #include "civ quotes.h" +#include "Strategic Pathing.h" #endif //turnspeed @@ -113,11 +114,6 @@ extern INT16 DirIncrementer[8]; #define LOW_MORALE_BATTLE_SND_THREASHOLD 35 -#define TURNING_FROM_PRONE_OFF 0 -#define TURNING_FROM_PRONE_ON 1 -#define TURNING_FROM_PRONE_START_UP_FROM_MOVE 2 -#define TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE 3 - #define MIN_SUBSEQUENT_SNDS_DELAY 2000 // Enumerate extended directions @@ -1756,7 +1752,7 @@ BOOLEAN ReevaluateEnemyStance( SOLDIERTYPE *pSoldier, UINT16 usAnimState ) // make the chosen one not turn to face us if ( OK_ENEMY_MERC( pSoldier ) && pSoldier->ubID != gTacticalStatus.ubTheChosenOne && gAnimControl[ usAnimState ].ubEndHeight == ANIM_STAND && !( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL) ) { - if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_OFF ) + if ( pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_OFF ) { // If we are a queen and see enemies, goto ready if ( pSoldier->ubBodyType == QUEENMONSTER ) @@ -2000,7 +1996,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection ); pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->usPendingAnimation = CLIMBDOWNROOF; - pSoldier->fTurningFromPronePosition = FALSE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; pSoldier->fTurningUntilDone = TRUE; SoldierGotoStationaryStance( pSoldier ); return( TRUE ); @@ -2236,20 +2232,29 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT // Unset paused for no APs..... AdjustNoAPToFinishMove( pSoldier, FALSE ); +#if 0 + // 0verhaul: This is a test. The only time I have been able to make this code hit is when + // the player goes prone while moving. And that is not what this part is intended for. I + // have seen the soldier in the middle of crawling, get up, turn, and then go prone again to + // continue along his path. But this code was not hit for that part. And this code seems + // to be made for that part. So apparently they found another way to deal with it. So + // I disabled the "locked" code for usDontUpdateNewGridNoOnMoveAnimChange since it can cause + // problems of its own. Now we see if we can do without this part too. if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == 1 ) { - if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) + if ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) { - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE; } // ATE: IF we are starting to crawl, but have to getup to turn first...... - if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_START_UP_FROM_MOVE ) + if ( pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_START_UP_FROM_MOVE ) { usNewState = PRONE_UP; - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE; } } +#endif // We are about to start moving // Handle buddy beginning to move... @@ -2303,7 +2308,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT { // Change desired direction // Just change direction - EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState ); + EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState ); } //check for services @@ -2336,7 +2341,14 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT // ( Unless locked ) if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING ) { - if ( pSoldier->usDontUpdateNewGridNoOnMoveAnimChange != LOCKED_NO_NEWGRIDNO ) + // 0verhaul: **** Special hack!!!! + // If a merc begins to go prone while moving, the LOCKED_NO_NEWGRIDNO is set. If the merc never finishes + // going prone, either due to interrupting the stance change with a different stance change, or other possible + // factors such as maybe getting shot (this is realtime so an enemy could see him), it stays on locked. Once + // it stays on locked, the soldier will be unable to navigate around obstacles but will simply stay put + // twitching. Since the LOCKED is only set when going prone, this unsets it. + if ( pSoldier->usDontUpdateNewGridNoOnMoveAnimChange != LOCKED_NO_NEWGRIDNO || + (pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == LOCKED_NO_NEWGRIDNO && pSoldier->usAnimState != PRONE_DOWN)) { pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = FALSE; } @@ -2367,7 +2379,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT uiOldAnimFlags = gAnimControl[ pSoldier->usAnimState ].uiFlags; uiNewAnimFlags = gAnimControl[ usNewState ].uiFlags; - usNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( pSoldier->usPathingData[ pSoldier->usPathIndex ] ) ); + usNewGridNo = NewGridNo( pSoldier->sGridNo, DirectionInc( (UINT8) pSoldier->usPathingData[ pSoldier->usPathIndex ] ) ); // CHECKING IF WE HAVE A HIT FINISH BUT NO DEATH IS DONE WITH A SPECIAL ANI CODE @@ -2479,7 +2491,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT case CRAWLING: // Turn off flag... - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; pSoldier->ubPendingActionAnimCount = 0; pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; break; @@ -2521,7 +2533,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT case PRONE_DOWN: // ATE: If we are NOT waiting for prone down... - if ( pSoldier->fTurningFromPronePosition < TURNING_FROM_PRONE_START_UP_FROM_MOVE && !pSoldier->fDontChargeAPsForStanceChange ) + if ( pSoldier->bTurningFromPronePosition < TURNING_FROM_PRONE_START_UP_FROM_MOVE && !pSoldier->fDontChargeAPsForStanceChange ) { // ATE: Don't do this if we are still 'moving'.... if ( pSoldier->sGridNo == pSoldier->sFinalDestination || pSoldier->usPathIndex == 0 ) @@ -2649,7 +2661,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT pSoldier->usPathDataSize++; pSoldier->sFinalDestination = usNewGridNo; // Set direction - EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, JUMP_OVER_BLOCKING_PERSON ); + EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, JUMP_OVER_BLOCKING_PERSON ); } break; @@ -3256,7 +3268,7 @@ void SetSoldierGridNo( SOLDIERTYPE *pSoldier, INT16 sNewGridNo, BOOLEAN fForceRe // Make transition from low to deep EVENT_InitNewSoldierAnim( pSoldier, LOW_TO_DEEP_WATER, 0 , FALSE ); pSoldier->usPendingAnimation = DEEP_WATER_SWIM; - + pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1; PlayJA2Sample( ENTER_DEEP_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } @@ -3273,6 +3285,7 @@ void SetSoldierGridNo( SOLDIERTYPE *pSoldier, INT16 sNewGridNo, BOOLEAN fForceRe { // Make transition from low to deep EVENT_InitNewSoldierAnim( pSoldier, DEEP_TO_LOW_WATER, 0 , FALSE ); + pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1; pSoldier->usPendingAnimation = pSoldier->usUIMovementMode; } } @@ -3494,7 +3507,7 @@ void EVENT_FireSoldierWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo ) pSoldier->fDontUnsetLastTargetFromTurn = TRUE; // Make sure we don't try and do fancy prone turning..... - pSoldier->fTurningFromPronePosition = FALSE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; // Force our direction! EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection ); @@ -5032,7 +5045,7 @@ BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGrid pSoldier->sGridNo = pSoldier->sDestination; // Check if path is good before copying it into guy's path... - if ( FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, fFlags ) == 0 ) + if ( !(uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags ) ) ) { // Set to old.... pSoldier->sGridNo = sMercGridNo; @@ -5040,7 +5053,7 @@ BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGrid return( FALSE ); } - uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags ); + //uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags ); pSoldier->sGridNo = sMercGridNo; pSoldier->sFinalDestination = sDestGridNo; @@ -5124,6 +5137,11 @@ BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGrid { usMoveAnimState = DEEP_WATER_SWIM; } + // Can't forget shallow water! AI will sometimes attempt to swat through it, which is not legal either + else if ( MercInWater( pSoldier) ) + { + usMoveAnimState = WALKING; + } // If we were aiming, end aim! usAnimState = PickSoldierReadyAnimation( pSoldier, TRUE ); @@ -5330,9 +5348,9 @@ void EVENT_InternalSetSoldierDestination( SOLDIERTYPE *pSoldier, UINT8 ubNewDire } } -void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection ) +void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT8 ubNewDirection ) { - EVENT_InternalSetSoldierDestination( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState ); + EVENT_InternalSetSoldierDestination( pSoldier, ubNewDirection, FALSE, pSoldier->usAnimState ); } @@ -5402,7 +5420,7 @@ INT8 MultiTiledTurnDirection( SOLDIERTYPE * pSoldier, INT8 bStartDirection, INT8 } bLoop++; - if ( bLoop < 2 ) + //if ( bLoop < 2 ) { // change direction of loop etc bCurrentDirection = bStartDirection; @@ -5469,9 +5487,9 @@ void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT8 ubNe { if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE ) { - if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) + if ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) { - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE; } } } @@ -5485,7 +5503,7 @@ void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT8 ubNe //if ( pSoldier->sDestination == pSoldier->sGridNo ) if ( !fInitalMove ) { - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ON; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_ON; // Set a pending animation to change stance first... SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); @@ -5922,7 +5940,11 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) { if ( pSoldier->ubDirection == pSoldier->bDesiredDirection ) { - if ( ( (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !pSoldier->fTurningFromPronePosition ) || pSoldier->ubBodyType == ROBOTNOWEAPON || pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE ) + if ( ( (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && + pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_OFF) || + pSoldier->ubBodyType == ROBOTNOWEAPON || + pSoldier->ubBodyType == TANK_NW || + pSoldier->ubBodyType == TANK_NE ) { DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("TurnSoldier: EVENT_InitNewSoldierAnim") ); EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE ); @@ -5933,7 +5955,7 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) } // Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn... - else if ( pSoldier->fTurningFromPronePosition ) + else if ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_OFF ) { if ( IsValidStance( pSoldier, ANIM_PRONE ) ) { @@ -5945,7 +5967,7 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, ANIM_CROUCH ), 0, FALSE ); } pSoldier->fTurningToShoot = FALSE; - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; } } } @@ -6002,7 +6024,9 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) pSoldier->fDontUnsetLastTargetFromTurn = FALSE; // Unset ui busy if from ui - if ( pSoldier->bTurningFromUI && ( pSoldier->fTurningFromPronePosition != 3 ) && ( pSoldier->fTurningFromPronePosition != 1 ) ) + if ( pSoldier->bTurningFromUI && + ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE ) && + ( pSoldier->bTurningFromPronePosition != TURNING_FROM_PRONE_ON ) ) { UnSetUIBusy( pSoldier->ubID ); pSoldier->bTurningFromUI = FALSE; @@ -6017,23 +6041,30 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) // Undo our flag for prone turning... // Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn... - if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ON ) + if ( pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_ON ) { // ATE: Don't do this if we have something in our hands we are going to throw! if ( IsValidStance( pSoldier, ANIM_PRONE ) && pSoldier->pTempObject == NULL ) { SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE ); } - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; } // If a special code, make guy crawl after stance change! - if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE && pSoldier->usAnimState != PRONE_UP && pSoldier->usAnimState != PRONE_DOWN ) + if ( pSoldier->bTurningFromPronePosition == TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE && + pSoldier->usAnimState != PRONE_UP && + pSoldier->usAnimState != PRONE_DOWN ) { if ( IsValidStance( pSoldier, ANIM_PRONE ) ) { EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0, FALSE ); } + // Else swat for a tile so that there's room to resume prone + else + { + EVENT_InitNewSoldierAnim( pSoldier, pSoldier->usUIMovementMode, 0 , FALSE ); + } } if ( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) @@ -6181,7 +6212,7 @@ void TurnSoldier( SOLDIERTYPE *pSoldier) { // OK, we want to getup, turn and go prone again.... SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH ); - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE; } // If we are a creature, or multi-tiled, cancel AI action.....? else if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE ) @@ -7090,7 +7121,7 @@ void BeginSoldierClimbFence( SOLDIERTYPE *pSoldier ) EVENT_InternalSetSoldierDesiredDirection( pSoldier, bDirection, FALSE, pSoldier->usAnimState ); pSoldier->fTurningUntilDone = TRUE; // ATE: Reset flag to go back to prone... - pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; pSoldier->usPendingAnimation = HOPFENCE; } @@ -9603,7 +9634,7 @@ void EVENT_SoldierBeginKnifeThrowAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, U // SET TARGET GRIDNO pSoldier->sTargetGridNo = sGridNo; pSoldier->sLastTarget = sGridNo; - pSoldier->fTurningFromPronePosition = 0; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; // NB target level must be set by functions outside of here... but I think it // is already set in HandleItem or in the AI code - CJC pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); @@ -10158,7 +10189,7 @@ void HaultSoldierFromSighting( SOLDIERTYPE *pSoldier, BOOLEAN fFromSightingEnemy if ( !pSoldier->fTurningToShoot ) { - pSoldier->fTurningFromPronePosition = FALSE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; } } @@ -10195,7 +10226,7 @@ void EVENT_StopMerc( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDirection ) } pSoldier->bEndDoorOpenCode = 0; - pSoldier->fTurningFromPronePosition = 0; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; // Cancel path data! pSoldier->usPathIndex = pSoldier->usPathDataSize = 0; @@ -10982,15 +11013,17 @@ void SoldierCollapse( SOLDIERTYPE *pSoldier ) // Crouched or prone, only for mercs! BeginTyingToFall( pSoldier ); - if ( fMerc ) - { + // 0verhaul: No special case here! First the FALLFORWARD_FROMHIT_CROUCH can be filled in to use the FALLFORWARD_FROMHIT_STAND anim + // then when real anims come, use them instead. + //if ( fMerc ) + //{ EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_CROUCH, 0 , FALSE); - } - else - { - // For civs... use fall from stand... - EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0 , FALSE); - } + //} + //else + //{ + // // For civs... use fall from stand... + // EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0 , FALSE); + //} break; case ANIM_PRONE: @@ -11572,7 +11605,7 @@ void ChangeToFlybackAnimation( SOLDIERTYPE *pSoldier, UINT8 ubDirection ) pSoldier->usPathingData[ pSoldier->usPathDataSize ] = ubOppositeDir; pSoldier->usPathDataSize++; pSoldier->sFinalDestination = sNewGridNo; - EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FLYBACK_HIT ); + EVENT_InternalSetSoldierDestination( pSoldier, (UINT8) pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FLYBACK_HIT ); // Get a new direction based on direction EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE ); @@ -12047,14 +12080,21 @@ void HandleSoldierTakeDamageFeedback( SOLDIERTYPE *pSoldier ) void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC ) { // Are we an AI guy? + // 0verhaul: + // This code will only stop a soldier if it is not the player's turn. The problem here is that the soldier's + // actions may have triggered an interrupt. This code is called in order to cancel the soldier's movement + // after the interrupt is triggered, so if the AI causes an interrupt and it's the player's turn, he will + // continue doing what he was going to do. We need this function to work even when it's the player's turn, + // at least in this case. if ( gTacticalStatus.ubCurrentTeam != gbPlayerNum && pSoldier->bTeam != gbPlayerNum ) + //if ( pSoldier->bTeam != gbPlayerNum ) { if ( pSoldier->bNewSituation == IS_NEW_SITUATION ) { // Cancel what they were doing.... pSoldier->usPendingAnimation = NO_PENDING_ANIMATION; pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION; - pSoldier->fTurningFromPronePosition = FALSE; + pSoldier->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; pSoldier->ubPendingDirection = NO_PENDING_DIRECTION; pSoldier->ubPendingAction = NO_PENDING_ACTION; pSoldier->bEndDoorOpenCode = 0; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index af612971..e37b0c2a 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -172,6 +172,11 @@ extern UINT16 CivLastNames[MAXCIVLASTNAMES][10]; //so that we can increase it if we can stand breaking the saves #define MAX_BURST_SPREAD_TARGETS 6 +#define TURNING_FROM_PRONE_OFF 0 +#define TURNING_FROM_PRONE_ON 1 +#define TURNING_FROM_PRONE_START_UP_FROM_MOVE 2 +#define TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE 3 + //ENUMERATIONS FOR ACTIONS enum { @@ -633,7 +638,7 @@ public: INT8 bMarksmanship; INT8 bExplosive; THROW_PARAMS *pThrowParams; - BOOLEAN fTurningFromPronePosition; + INT8 bTurningFromPronePosition; INT8 bReverse; LEVELNODE *pLevelNode; LEVELNODE *pExternShadowLevelNode; @@ -1153,7 +1158,7 @@ BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT BOOLEAN ChangeSoldierState( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce ); void EVENT_SetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ); -void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection ); +void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT8 ubNewDirection ); void EVENT_GetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim ); BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestart ); diff --git a/Tactical/Soldier Init List.cpp b/Tactical/Soldier Init List.cpp index 4ee1ef67..a28ef6dd 100644 --- a/Tactical/Soldier Init List.cpp +++ b/Tactical/Soldier Init List.cpp @@ -1,51 +1,51 @@ #ifdef PRECOMPILEDHEADERS - #include "Tactical All.h" +#include "Tactical All.h" #else - #include "builddefines.h" - #include - #include +#include "builddefines.h" +#include +#include - #include "types.h" +#include "types.h" - #include "strategicmap.h" - #include "overhead.h" - #include "isometric utils.h" +#include "strategicmap.h" +#include "overhead.h" +#include "isometric utils.h" - #include "soldier add.h" - #include "soldier create.h" - #include "Soldier Init List.h" - #include "debug.h" - #include "Random.h" - #include "items.h" +#include "soldier add.h" +#include "soldier create.h" +#include "Soldier Init List.h" +#include "debug.h" +#include "Random.h" +#include "items.h" - #include "cursors.h" +#include "cursors.h" - #include "Map Information.h" - #include "soldier profile.h" - #include "Sys Globals.h" - #include "EditorMercs.h" - #include "Animation Data.h" - #include "message.h" - #include "Font Control.h" - #include "Sound Control.h" - #include "Quests.h" - #include "Render Fun.h" - #include "meanwhile.h" - #include "Map Screen Interface Map.h" - #include "mapscreen.h" - #include "Debug Control.h" - #include "Inventory Choosing.h" - #include "Strategic AI.h" - #include "strategic.h" - #include "Campaign Types.h" - #include "ai.h" - #include "NPC.h" - #include "Scheduling.h" - #include "opplist.h" - #include "MessageBoxScreen.h" - #include "screenids.h" - #include "SaveLoadScreen.h" - #include "Rotting Corpses.h" +#include "Map Information.h" +#include "soldier profile.h" +#include "Sys Globals.h" +#include "EditorMercs.h" +#include "Animation Data.h" +#include "message.h" +#include "Font Control.h" +#include "Sound Control.h" +#include "Quests.h" +#include "Render Fun.h" +#include "meanwhile.h" +#include "Map Screen Interface Map.h" +#include "mapscreen.h" +#include "Debug Control.h" +#include "Inventory Choosing.h" +#include "Strategic AI.h" +#include "strategic.h" +#include "Campaign Types.h" +#include "ai.h" +#include "NPC.h" +#include "Scheduling.h" +#include "opplist.h" +#include "MessageBoxScreen.h" +#include "screenids.h" +#include "SaveLoadScreen.h" +#include "Rotting Corpses.h" #endif #include "Map Edgepoints.h" @@ -58,8 +58,8 @@ SOLDIERINITNODE *gOriginalSoldierInitListHead = NULL; SOLDIERINITNODE *gAlternateSoldierInitListHead = NULL; #ifdef JA2BETAVERSION - BOOLEAN ValidateSoldierInitLinks( UINT8 ubCode ); - BOOLEAN gfDoDialogOnceGameScreenFadesIn = FALSE; +BOOLEAN ValidateSoldierInitLinks( UINT8 ubCode ); +BOOLEAN gfDoDialogOnceGameScreenFadesIn = FALSE; #endif UINT32 CountNumberOfNodesWithSoldiers() @@ -83,14 +83,14 @@ void SortSoldierInitList(); void InitSoldierInitList() { if( gSoldierInitHead ) - KillSoldierInitList(); + KillSoldierInitList(); gSoldierInitHead = NULL; gSoldierInitTail = NULL; } void KillSoldierInitList() { - while( gSoldierInitHead ) + while( gSoldierInitHead ) RemoveSoldierNodeFromInitList( gSoldierInitTail ); if( gfOriginalList ) gOriginalSoldierInitListHead = NULL; @@ -206,7 +206,7 @@ BOOLEAN SaveSoldiersToMap( HWFILE fp ) UINT32 i; UINT32 uiBytesWritten; SOLDIERINITNODE *curr; - + if( !fp ) return FALSE; @@ -220,11 +220,11 @@ BOOLEAN SaveSoldiersToMap( HWFILE fp ) //mode, and kill them all, while replacing them with the proper ones. Not only that, the alternate //editing mode is turned off, and if intentions are to play the game, the user will be facing many //enemies! - #ifdef JA2EDITOR - if( !gfOriginalList ) - ResetAllMercPositions(); - #endif - +#ifdef JA2EDITOR + if( !gfOriginalList ) + ResetAllMercPositions(); +#endif + curr = gSoldierInitHead; for( i=0; i < gMapInformation.ubNumIndividuals; i++ ) { @@ -237,7 +237,7 @@ BOOLEAN SaveSoldiersToMap( HWFILE fp ) { if( !curr->pDetailedPlacement ) return FALSE; - // WDS - Clean up inventory handling + // WDS - Clean up inventory handling curr->pDetailedPlacement->CopyNewInventoryToOld(); FileWrite( fp, curr->pDetailedPlacement, SIZEOF_SOLDIERCREATE_STRUCT_POD /*SIZEOF_SOLDIERCREATE_STRUCT*/, &uiBytesWritten ); } @@ -263,7 +263,7 @@ BOOLEAN LoadSoldiersFromMap( INT8 **hBuffer ) KillSoldierInitList(); UseEditorOriginalList(); KillSoldierInitList(); - + InitSoldierInitList(); if( ubNumIndividuals > MAX_INDIVIDUALS ) @@ -275,7 +275,7 @@ BOOLEAN LoadSoldiersFromMap( INT8 **hBuffer ) { return TRUE; //no mercs } - + //Because we are loading the map, we needed to know how many //guys are being loaded, but when we add them to the list here, it //automatically increments that number, effectively doubling it, which @@ -295,7 +295,7 @@ BOOLEAN LoadSoldiersFromMap( INT8 **hBuffer ) } if( tempBasicPlacement.fDetailedPlacement ) { //Add the static detailed placement information in the same newly created node as the basic placement. - // WDS - Clean up inventory handling + // WDS - Clean up inventory handling tempDetailedPlacement.initialize(); //read static detailed placement from file LOADDATA( &tempDetailedPlacement, *hBuffer, SIZEOF_SOLDIERCREATE_STRUCT_POD ); @@ -553,7 +553,7 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) } //else //{ - //break; + //break; //} } while (pCorpse); } @@ -567,13 +567,13 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) if( ubProfile != NO_PROFILE && !gfEditMode ) { if( gMercProfiles[ ubProfile ].sSectorX != gWorldSectorX || - gMercProfiles[ ubProfile ].sSectorY != gWorldSectorY || - gMercProfiles[ ubProfile ].bSectorZ != gbWorldSectorZ || - gMercProfiles[ ubProfile ].ubMiscFlags & (PROFILE_MISC_FLAG_RECRUITED | PROFILE_MISC_FLAG_EPCACTIVE) || -// gMercProfiles[ ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_DONT_ADD_TO_SECTOR || - !gMercProfiles[ ubProfile ].bLife || - gMercProfiles[ ubProfile ].fUseProfileInsertionInfo - ) + gMercProfiles[ ubProfile ].sSectorY != gWorldSectorY || + gMercProfiles[ ubProfile ].bSectorZ != gbWorldSectorZ || + gMercProfiles[ ubProfile ].ubMiscFlags & (PROFILE_MISC_FLAG_RECRUITED | PROFILE_MISC_FLAG_EPCACTIVE) || + // gMercProfiles[ ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_DONT_ADD_TO_SECTOR || + !gMercProfiles[ ubProfile ].bLife || + gMercProfiles[ ubProfile ].fUseProfileInsertionInfo + ) { return FALSE; } @@ -587,8 +587,8 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) if( curr->pDetailedPlacement->bBodyType == ICECREAMTRUCK ) { //Check to see if Hamous is here and not recruited. If so, add truck if( gMercProfiles[ HAMOUS ].sSectorX != gWorldSectorX || - gMercProfiles[ HAMOUS ].sSectorY != gWorldSectorY || - gMercProfiles[ HAMOUS ].bSectorZ ) + gMercProfiles[ HAMOUS ].sSectorY != gWorldSectorY || + gMercProfiles[ HAMOUS ].bSectorZ ) { //not here, so don't add return TRUE; } @@ -632,15 +632,15 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) switch( PreRandom( 3 ) ) { - case 0: - tempDetailedPlacement.bOrders = ONGUARD; - break; - case 1: - tempDetailedPlacement.bOrders = CLOSEPATROL; - break; - case 2: - tempDetailedPlacement.bOrders = ONCALL; - break; + case 0: + tempDetailedPlacement.bOrders = ONGUARD; + break; + case 1: + tempDetailedPlacement.bOrders = CLOSEPATROL; + break; + case 2: + tempDetailedPlacement.bOrders = ONCALL; + break; } } @@ -707,7 +707,7 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) } } } - + //SPECIAL! Certain events in the game can cause profiled NPCs to become enemies. The two cases are //adding Mike and Iggy. We will only add one NPC in any given combat and the conditions for setting //the associated facts are done elsewhere. There is also another place where NPCs can get added, which @@ -776,7 +776,7 @@ UINT8 AddSoldierInitListTeamToWorld( INT8 bTeam, UINT8 ubMaxNum ) UINT8 ubSlotsToFill; UINT8 ubSlotsAvailable; SOLDIERINITNODE *curr; - + DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddSoldierInitListTeamToWorld")); //Sort the list in the following manner: //-Priority placements first @@ -857,6 +857,7 @@ UINT8 AddSoldierInitListTeamToWorld( INT8 bTeam, UINT8 ubMaxNum ) if( AddPlacementToWorld( curr ) ) { ubNumAdded++; + ubSlotsToFill--; } else { @@ -864,9 +865,17 @@ UINT8 AddSoldierInitListTeamToWorld( INT8 bTeam, UINT8 ubMaxNum ) //engine are already full. Besides, the strategic AI shouldn't be trying to fill a map with //more than the maximum allowable soldiers of team. All teams can have a max of 32 individuals, //except for the player which is 20. Players aren't processed in this list anyway. - return ubNumAdded; + // 0verhaul: Bzzzzzzzttttt.... Wrong! Other reasons for a false return: + // Profiled merc not in sector + // Profiled merc is recruited + // Profiled merc is escorted + // Profiled merc is dead + // Profiled merc needs to use profile insertion data instead of placement + // None of these instances is a reason to give up on the rest of the placements. + //return ubNumAdded; } - ubSlotsToFill--; + // 0verhaul: However, move this to the point of successful insertion! + //ubSlotsToFill--; } ubSlotsAvailable--; //With the decrementing of the slot vars in this manner, the chances increase so that all slots @@ -884,7 +893,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr INT32 iRandom; UINT8 ubMaxNum; INT8 bTeam = ENEMY_TEAM; - UINT8 ubElitePDSlots = 0, ubEliteDSlots = 0, ubElitePSlots = 0, ubEliteBSlots = 0; + UINT8 ubElitePDSlots = 0, ubEliteDSlots = 0, ubElitePSlots = 0, ubEliteBSlots = 0; UINT8 ubTroopPDSlots = 0, ubTroopDSlots = 0, ubTroopPSlots = 0, ubTroopBSlots = 0; UINT8 ubAdminPDSlots = 0, ubAdminDSlots = 0, ubAdminPSlots = 0, ubAdminBSlots = 0; UINT8 ubFreeSlots; @@ -898,13 +907,13 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr //Specs call for only one profiled enemy can be in a sector at a time due to flavor reasons. gfProfiledEnemyAdded = FALSE; - + //Because the enemy defence forces work differently than the regular map placements, the numbers //of each type of enemy may not be the same. Elites will choose the best placements, then army, then //administrators. ubMaxNum = ubTotalAdmin + ubTotalTroops + ubTotalElite; - + //Sort the list in the following manner: //-Priority placements first //-Basic placements next @@ -921,36 +930,36 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr { switch( curr->pBasicPlacement->ubSoldierClass ) { - case SOLDIER_CLASS_ELITE: - if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) - ubElitePDSlots++; - else if( curr->pBasicPlacement->fPriorityExistance ) - ubElitePSlots++; - else if( curr->pDetailedPlacement ) - ubEliteDSlots++; - else - ubEliteBSlots++; - break; - case SOLDIER_CLASS_ADMINISTRATOR: - if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) - ubAdminPDSlots++; - else if( curr->pBasicPlacement->fPriorityExistance ) - ubAdminPSlots++; - else if( curr->pDetailedPlacement ) - ubAdminDSlots++; - else - ubAdminBSlots++; - break; - case SOLDIER_CLASS_ARMY: - if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) - ubTroopPDSlots++; - else if( curr->pBasicPlacement->fPriorityExistance ) - ubTroopPSlots++; - else if( curr->pDetailedPlacement ) - ubTroopDSlots++; - else - ubTroopBSlots++; - break; + case SOLDIER_CLASS_ELITE: + if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) + ubElitePDSlots++; + else if( curr->pBasicPlacement->fPriorityExistance ) + ubElitePSlots++; + else if( curr->pDetailedPlacement ) + ubEliteDSlots++; + else + ubEliteBSlots++; + break; + case SOLDIER_CLASS_ADMINISTRATOR: + if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) + ubAdminPDSlots++; + else if( curr->pBasicPlacement->fPriorityExistance ) + ubAdminPSlots++; + else if( curr->pDetailedPlacement ) + ubAdminDSlots++; + else + ubAdminBSlots++; + break; + case SOLDIER_CLASS_ARMY: + if( curr->pBasicPlacement->fPriorityExistance && curr->pDetailedPlacement ) + ubTroopPDSlots++; + else if( curr->pBasicPlacement->fPriorityExistance ) + ubTroopPSlots++; + else if( curr->pDetailedPlacement ) + ubTroopDSlots++; + else + ubTroopBSlots++; + break; } } curr = curr->next; @@ -964,23 +973,23 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr //First, prepare the counters. switch( ubCurrClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: - pCurrSlots = &ubAdminPDSlots; - pCurrTotal = &ubTotalAdmin; - break; - case SOLDIER_CLASS_ELITE: - pCurrSlots = &ubElitePDSlots; - pCurrTotal = &ubTotalElite; - break; - case SOLDIER_CLASS_ARMY: - pCurrSlots = &ubTroopPDSlots; - pCurrTotal = &ubTotalTroops; - break; + case SOLDIER_CLASS_ADMINISTRATOR: + pCurrSlots = &ubAdminPDSlots; + pCurrTotal = &ubTotalAdmin; + break; + case SOLDIER_CLASS_ELITE: + pCurrSlots = &ubElitePDSlots; + pCurrTotal = &ubTotalElite; + break; + case SOLDIER_CLASS_ARMY: + pCurrSlots = &ubTroopPDSlots; + pCurrTotal = &ubTotalTroops; + break; } //Now, loop through the priority existance and detailed placement section of the list. curr = gSoldierInitHead; while( curr && ubMaxNum && *pCurrTotal && *pCurrSlots && - curr->pDetailedPlacement && curr->pBasicPlacement->fPriorityExistance ) + curr->pDetailedPlacement && curr->pBasicPlacement->fPriorityExistance ) { if( !curr->pSoldier && curr->pBasicPlacement->bTeam == ENEMY_TEAM ) { @@ -1020,23 +1029,23 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr //First, prepare the counters. switch( ubCurrClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: - pCurrSlots = &ubAdminPSlots; - pCurrTotal = &ubTotalAdmin; - break; - case SOLDIER_CLASS_ELITE: - pCurrSlots = &ubElitePSlots; - pCurrTotal = &ubTotalElite; - break; - case SOLDIER_CLASS_ARMY: - pCurrSlots = &ubTroopPSlots; - pCurrTotal = &ubTotalTroops; - break; + case SOLDIER_CLASS_ADMINISTRATOR: + pCurrSlots = &ubAdminPSlots; + pCurrTotal = &ubTotalAdmin; + break; + case SOLDIER_CLASS_ELITE: + pCurrSlots = &ubElitePSlots; + pCurrTotal = &ubTotalElite; + break; + case SOLDIER_CLASS_ARMY: + pCurrSlots = &ubTroopPSlots; + pCurrTotal = &ubTotalTroops; + break; } //Now, loop through the priority existance and non detailed placement section of the list. curr = mark; while( curr && ubMaxNum && *pCurrTotal && *pCurrSlots && - !curr->pDetailedPlacement && curr->pBasicPlacement->fPriorityExistance ) + !curr->pDetailedPlacement && curr->pBasicPlacement->fPriorityExistance ) { if( !curr->pSoldier && curr->pBasicPlacement->bTeam == ENEMY_TEAM ) { @@ -1067,7 +1076,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr while( curr && !curr->pDetailedPlacement && curr->pBasicPlacement->fPriorityExistance ) curr = curr->next; mark = curr; - + //ADD PLACEMENTS WITH NO DETAILED PLACEMENT AND PRIORITY EXISTANCE INFORMATION SECOND //we now have the numbers of available slots for each soldier class, so loop through three times //and randomly choose some (or all) of the matching slots to fill. This is done randomly. @@ -1076,23 +1085,23 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr //First, prepare the counters. switch( ubCurrClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: - pCurrSlots = &ubAdminDSlots; - pCurrTotal = &ubTotalAdmin; - break; - case SOLDIER_CLASS_ELITE: - pCurrSlots = &ubEliteDSlots; - pCurrTotal = &ubTotalElite; - break; - case SOLDIER_CLASS_ARMY: - pCurrSlots = &ubTroopDSlots; - pCurrTotal = &ubTotalTroops; - break; + case SOLDIER_CLASS_ADMINISTRATOR: + pCurrSlots = &ubAdminDSlots; + pCurrTotal = &ubTotalAdmin; + break; + case SOLDIER_CLASS_ELITE: + pCurrSlots = &ubEliteDSlots; + pCurrTotal = &ubTotalElite; + break; + case SOLDIER_CLASS_ARMY: + pCurrSlots = &ubTroopDSlots; + pCurrTotal = &ubTotalTroops; + break; } //Now, loop through the priority existance and detailed placement section of the list. curr = mark; while( curr && ubMaxNum && *pCurrTotal && *pCurrSlots && - curr->pDetailedPlacement && !curr->pBasicPlacement->fPriorityExistance ) + curr->pDetailedPlacement && !curr->pBasicPlacement->fPriorityExistance ) { if( !curr->pSoldier && curr->pBasicPlacement->bTeam == ENEMY_TEAM ) { @@ -1165,7 +1174,7 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr } if( !ubMaxNum ) return; - + //ADD REMAINING PLACEMENTS WITH BASIC PLACEMENT INFORMATION //we now have the numbers of available slots for each soldier class, so loop through three times //and randomly choose some (or all) of the matching slots to fill. This is done randomly. @@ -1174,18 +1183,18 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr //First, prepare the counters. switch( ubCurrClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: - pCurrSlots = &ubAdminBSlots; - pCurrTotal = &ubTotalAdmin; - break; - case SOLDIER_CLASS_ELITE: - pCurrSlots = &ubEliteBSlots; - pCurrTotal = &ubTotalElite; - break; - case SOLDIER_CLASS_ARMY: - pCurrSlots = &ubTroopBSlots; - pCurrTotal = &ubTotalTroops; - break; + case SOLDIER_CLASS_ADMINISTRATOR: + pCurrSlots = &ubAdminBSlots; + pCurrTotal = &ubTotalAdmin; + break; + case SOLDIER_CLASS_ELITE: + pCurrSlots = &ubEliteBSlots; + pCurrTotal = &ubTotalElite; + break; + case SOLDIER_CLASS_ARMY: + pCurrSlots = &ubTroopBSlots; + pCurrTotal = &ubTotalTroops; + break; } //Now, loop through the regular basic placements section of the list. curr = mark; @@ -1266,9 +1275,9 @@ void AddSoldierInitListEnemyDefenceSoldiers( UINT8 ubTotalAdmin, UINT8 ubTotalTr /* DISABLE THE OVERRIDE FOR NOW... if( curr->pDetailedPlacement ) { //delete the detailed placement information. - MemFree( curr->pDetailedPlacement ); - curr->pDetailedPlacement = NULL; - curr->pBasicPlacement->fDetailedPlacement = FALSE; + MemFree( curr->pDetailedPlacement ); + curr->pDetailedPlacement = NULL; + curr->pBasicPlacement->fDetailedPlacement = FALSE; } */ if( AddPlacementToWorld( curr ) ) @@ -1299,7 +1308,7 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl UINT8 ubMaxNum; BOOLEAN fDoPlacement; INT8 bTeam = ENEMY_TEAM; - UINT8 ubEliteSlots = 0; + UINT8 ubEliteSlots = 0; UINT8 ubRegSlots = 0; UINT8 ubGreenSlots = 0; UINT8 ubFreeSlots; @@ -1310,7 +1319,7 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddSoldierInitListMilitia")); ubMaxNum = ubNumGreen + ubNumRegs + ubNumElites; - + //Sort the list in the following manner: //-Priority placements first //-Basic placements next @@ -1380,9 +1389,9 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl { switch( curr->pBasicPlacement->ubSoldierClass ) { - case SOLDIER_CLASS_ELITE: ubEliteSlots++; break; - case SOLDIER_CLASS_ADMINISTRATOR: ubGreenSlots++; break; - case SOLDIER_CLASS_ARMY: ubRegSlots++; break; + case SOLDIER_CLASS_ELITE: ubEliteSlots++; break; + case SOLDIER_CLASS_ADMINISTRATOR: ubGreenSlots++; break; + case SOLDIER_CLASS_ARMY: ubRegSlots++; break; } } curr = curr->next; @@ -1395,18 +1404,18 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl //First, prepare the counters. switch( ubCurrClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: - pCurrSlots = &ubGreenSlots; - pCurrTotal = &ubNumGreen; - break; - case SOLDIER_CLASS_ELITE: - pCurrSlots = &ubEliteSlots; - pCurrTotal = &ubNumElites; - break; - case SOLDIER_CLASS_ARMY: - pCurrSlots = &ubRegSlots; - pCurrTotal = &ubNumRegs; - break; + case SOLDIER_CLASS_ADMINISTRATOR: + pCurrSlots = &ubGreenSlots; + pCurrTotal = &ubNumGreen; + break; + case SOLDIER_CLASS_ELITE: + pCurrSlots = &ubEliteSlots; + pCurrTotal = &ubNumElites; + break; + case SOLDIER_CLASS_ARMY: + pCurrSlots = &ubRegSlots; + pCurrTotal = &ubNumRegs; + break; } //Now, loop through the basic placement of the list. curr = mark; //mark is the marker where the basic placements start. @@ -1422,15 +1431,15 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl curr->pBasicPlacement->bOrders = STATIONARY; switch( ubCurrClass ) { - case SOLDIER_CLASS_ADMINISTRATOR: - curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_GREEN_MILITIA; - break; - case SOLDIER_CLASS_ARMY: - curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_REG_MILITIA; - break; - case SOLDIER_CLASS_ELITE: - curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA; - break; + case SOLDIER_CLASS_ADMINISTRATOR: + curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_GREEN_MILITIA; + break; + case SOLDIER_CLASS_ARMY: + curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_REG_MILITIA; + break; + case SOLDIER_CLASS_ELITE: + curr->pBasicPlacement->ubSoldierClass = SOLDIER_CLASS_ELITE_MILITIA; + break; } //found matching team, so add this soldier to the game. if( AddPlacementToWorld( curr ) ) @@ -1524,8 +1533,8 @@ void AddSoldierInitListMilitia( UINT8 ubNumGreen, UINT8 ubNumRegs, UINT8 ubNumEl } void AddSoldierInitListCreatures( BOOLEAN fQueen, UINT8 ubNumLarvae, UINT8 ubNumInfants, - UINT8 ubNumYoungMales, UINT8 ubNumYoungFemales, UINT8 ubNumAdultMales, - UINT8 ubNumAdultFemales ) + UINT8 ubNumYoungMales, UINT8 ubNumYoungFemales, UINT8 ubNumAdultMales, + UINT8 ubNumAdultFemales ) { SOLDIERINITNODE *curr; INT32 iRandom; @@ -1556,9 +1565,9 @@ void AddSoldierInitListCreatures( BOOLEAN fQueen, UINT8 ubNumLarvae, UINT8 ubNum } if( !fQueen ) { - #ifdef JA2BETAVERSION - ScreenMsg( FONT_RED, MSG_ERROR, L"Couldn't place the queen." ); - #endif +#ifdef JA2BETAVERSION + ScreenMsg( FONT_RED, MSG_ERROR, L"Couldn't place the queen." ); +#endif } } @@ -1622,7 +1631,7 @@ void AddSoldierInitListCreatures( BOOLEAN fQueen, UINT8 ubNumLarvae, UINT8 ubNum { //Choose which team to use. iRandom = Random( ubNumCreatures ); - + if( ubNumLarvae && iRandom < ubNumLarvae ) { ubNumLarvae--; @@ -1839,7 +1848,7 @@ BOOLEAN LoadSoldierInitListLinks( HWFILE hfile ) UINT32 uiNumBytesRead; SOLDIERINITNODE *curr; UINT8 ubSlots, ubSoldierID, ubNodeID; - + FileRead( hfile, &ubSlots, 1, &uiNumBytesRead ); if( uiNumBytesRead != 1 ) { @@ -1867,9 +1876,9 @@ BOOLEAN LoadSoldierInitListLinks( HWFILE hfile ) { curr->ubSoldierID = ubSoldierID; if( ubSoldierID >= gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID && - ubSoldierID <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID || - ubSoldierID >= gTacticalStatus.Team[ CIV_TEAM ].bFirstID && - ubSoldierID <= gTacticalStatus.Team[ CIV_TEAM ].bLastID ) + ubSoldierID <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID || + ubSoldierID >= gTacticalStatus.Team[ CIV_TEAM ].bFirstID && + ubSoldierID <= gTacticalStatus.Team[ CIV_TEAM ].bLastID ) { //only enemies and creatures. curr->pSoldier = MercPtrs[ ubSoldierID ]; } @@ -1900,7 +1909,7 @@ void AddSoldierInitListBloodcats() { //This map has no bloodcat placements, so don't waste CPU time. return; } - + if( pSector->bBloodCatPlacements ) { //We don't yet know the number of bloodcat placements in this sector so //count them now, and permanently record it. @@ -1915,14 +1924,14 @@ void AddSoldierInitListBloodcats() curr = curr->next; } if( bBloodCatPlacements != pSector->bBloodCatPlacements && - ubSectorID != SEC_I16 && ubSectorID != SEC_N5 ) + ubSectorID != SEC_I16 && ubSectorID != SEC_N5 ) { - #ifdef JA2BETAVERSION - CHAR16 str[200]; - swprintf( str, L"Table specifies that there are %d bloodcat placements in sector %c%d, but the map actually has %d bloodcat placements. Map value takes precedence. KM,LC:1", - pSector->bBloodCatPlacements, gWorldSectorY + 'A' - 1, gWorldSectorX, bBloodCatPlacements ); - DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); - #endif +#ifdef JA2BETAVERSION + CHAR16 str[200]; + swprintf( str, L"Table specifies that there are %d bloodcat placements in sector %c%d, but the map actually has %d bloodcat placements. Map value takes precedence. KM,LC:1", + pSector->bBloodCatPlacements, gWorldSectorY + 'A' - 1, gWorldSectorX, bBloodCatPlacements ); + DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, NULL ); +#endif pSector->bBloodCatPlacements = bBloodCatPlacements; pSector->bBloodCats = -1; if( !bBloodCatPlacements ) @@ -1939,7 +1948,7 @@ void AddSoldierInitListBloodcats() UINT8 ubSlotsToFill; UINT8 ubSlotsAvailable; SOLDIERINITNODE *curr; - + //Sort the list in the following manner: //-Priority placements first //-Basic placements next @@ -2057,13 +2066,13 @@ void AddProfilesUsingProfileInsertionData() //Perform various checks to make sure the soldier is actually in the same sector, alive, and so on. //More importantly, the flag to use profile insertion data must be set. if( gMercProfiles[ i ].sSectorX != gWorldSectorX || - gMercProfiles[ i ].sSectorY != gWorldSectorY || - gMercProfiles[ i ].bSectorZ != gbWorldSectorZ || - gMercProfiles[ i ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED || - gMercProfiles[ i ].ubMiscFlags & PROFILE_MISC_FLAG_EPCACTIVE || -// gMercProfiles[ i ].ubMiscFlags2 & PROFILE_MISC_FLAG2_DONT_ADD_TO_SECTOR || - !gMercProfiles[ i ].bLife || - !gMercProfiles[ i ].fUseProfileInsertionInfo + gMercProfiles[ i ].sSectorY != gWorldSectorY || + gMercProfiles[ i ].bSectorZ != gbWorldSectorZ || + gMercProfiles[ i ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED || + gMercProfiles[ i ].ubMiscFlags & PROFILE_MISC_FLAG_EPCACTIVE || + // gMercProfiles[ i ].ubMiscFlags2 & PROFILE_MISC_FLAG2_DONT_ADD_TO_SECTOR || + !gMercProfiles[ i ].bLife || + !gMercProfiles[ i ].fUseProfileInsertionInfo ) { //Don't add, so skip to the next soldier. continue; @@ -2075,7 +2084,8 @@ void AddProfilesUsingProfileInsertionData() UINT8 ubID; //Set up the create struct so that we can properly create the profile soldier. - memset( &MercCreateStruct, 0, sizeof( MercCreateStruct ) ); + // It's been C++'d, so don't do this! + //memset( &MercCreateStruct, 0, sizeof( MercCreateStruct ) ); MercCreateStruct.bTeam = CIV_TEAM; MercCreateStruct.ubProfile = (UINT8)i; MercCreateStruct.sSectorX = gWorldSectorX; @@ -2135,11 +2145,11 @@ void AddProfilesNotUsingProfileInsertionData() while( curr ) { if( !curr->pSoldier && - curr->pBasicPlacement->bTeam == CIV_TEAM && - curr->pDetailedPlacement && - curr->pDetailedPlacement->ubProfile != NO_PROFILE && - !gMercProfiles[ curr->pDetailedPlacement->ubProfile ].fUseProfileInsertionInfo && - gMercProfiles[ curr->pDetailedPlacement->ubProfile ].bLife ) + curr->pBasicPlacement->bTeam == CIV_TEAM && + curr->pDetailedPlacement && + curr->pDetailedPlacement->ubProfile != NO_PROFILE && + !gMercProfiles[ curr->pDetailedPlacement->ubProfile ].fUseProfileInsertionInfo && + gMercProfiles[ curr->pDetailedPlacement->ubProfile ].bLife ) { AddPlacementToWorld( curr ); } @@ -2171,31 +2181,31 @@ BOOLEAN ValidateSoldierInitLinks( UINT8 ubCode ) { switch( ubCode ) { - case 1: //loading save - swprintf( str, L"Error detected in save file WHILE LOADING. Please send save and text files associated with save to Kris and Dave." - L" After doing so, go back into the game and immediately resave the game which will fix the problem." - L" This is the bug responsible for mercs disappearing. Be prepared to answer lots of questions..." ); - DoSaveLoadMessageBox( MSG_BOX_BASIC_STYLE, str, SAVE_LOAD_SCREEN, MSG_BOX_FLAG_OK, ErrorDetectedInSaveCallback ); - break; - case 2: //saving game - //swprintf( str, L"Error detected WHILE SAVING file. Please send save and text files associated with save to Kris and Dave." - // L" After doing so, go back into the game and try reloading the new save and saving it again which *could* fix the problem." - // L" This is the bug responsible for mercs disappearing. Be prepared to answer lots of questions..." ); - //if( guiPreviousOptionScreen == MAP_SCREEN ) - // DoMapMessageBox( MSG_BOX_BASIC_STYLE, str, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL ); - //else - // DoMessageBox( MSG_BOX_BASIC_STYLE, str, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL ); - break; - case 3: //entering sector using temp files (before fade in) - gfDoDialogOnceGameScreenFadesIn = TRUE; - break; - case 4: //after fade in - gfDoDialogOnceGameScreenFadesIn = FALSE; - swprintf( str, L"Error detected while entering sector USING TEMP FILES. Please send previous save and text files associated with save to Kris and Dave." - L" After doing so, go back into the game and saving the game, reloading it, and saving it again *could* fix it." - L" This is the bug responsible for mercs disappearing. Be prepared to answer lots of questions..." ); - DoMessageBox( MSG_BOX_BASIC_STYLE, str, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL ); - break; + case 1: //loading save + swprintf( str, L"Error detected in save file WHILE LOADING. Please send save and text files associated with save to Kris and Dave." + L" After doing so, go back into the game and immediately resave the game which will fix the problem." + L" This is the bug responsible for mercs disappearing. Be prepared to answer lots of questions..." ); + DoSaveLoadMessageBox( MSG_BOX_BASIC_STYLE, str, SAVE_LOAD_SCREEN, MSG_BOX_FLAG_OK, ErrorDetectedInSaveCallback ); + break; + case 2: //saving game + //swprintf( str, L"Error detected WHILE SAVING file. Please send save and text files associated with save to Kris and Dave." + // L" After doing so, go back into the game and try reloading the new save and saving it again which *could* fix the problem." + // L" This is the bug responsible for mercs disappearing. Be prepared to answer lots of questions..." ); + //if( guiPreviousOptionScreen == MAP_SCREEN ) + // DoMapMessageBox( MSG_BOX_BASIC_STYLE, str, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL ); + //else + // DoMessageBox( MSG_BOX_BASIC_STYLE, str, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL ); + break; + case 3: //entering sector using temp files (before fade in) + gfDoDialogOnceGameScreenFadesIn = TRUE; + break; + case 4: //after fade in + gfDoDialogOnceGameScreenFadesIn = FALSE; + swprintf( str, L"Error detected while entering sector USING TEMP FILES. Please send previous save and text files associated with save to Kris and Dave." + L" After doing so, go back into the game and saving the game, reloading it, and saving it again *could* fix it." + L" This is the bug responsible for mercs disappearing. Be prepared to answer lots of questions..." ); + DoMessageBox( MSG_BOX_BASIC_STYLE, str, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL ); + break; } return FALSE; } @@ -2209,7 +2219,7 @@ BOOLEAN NewWayOfLoadingEnemySoldierInitListLinks( HWFILE hfile ) UINT32 uiNumBytesRead; SOLDIERINITNODE *curr; UINT8 ubSlots, ubSoldierID, ubNodeID; - + FileRead( hfile, &ubSlots, 1, &uiNumBytesRead ); if( uiNumBytesRead != 1 ) { @@ -2237,7 +2247,7 @@ BOOLEAN NewWayOfLoadingEnemySoldierInitListLinks( HWFILE hfile ) { curr->ubSoldierID = ubSoldierID; if( ubSoldierID >= gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID && - ubSoldierID <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID ) + ubSoldierID <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID ) { //only enemies and creatures. curr->pSoldier = MercPtrs[ ubSoldierID ]; } @@ -2255,7 +2265,7 @@ BOOLEAN NewWayOfLoadingCivilianInitListLinks( HWFILE hfile ) UINT32 uiNumBytesRead; SOLDIERINITNODE *curr; UINT8 ubSlots, ubSoldierID, ubNodeID; - + FileRead( hfile, &ubSlots, 1, &uiNumBytesRead ); if( uiNumBytesRead != 1 ) { @@ -2283,7 +2293,7 @@ BOOLEAN NewWayOfLoadingCivilianInitListLinks( HWFILE hfile ) { curr->ubSoldierID = ubSoldierID; if( ubSoldierID >= gTacticalStatus.Team[ CIV_TEAM ].bFirstID && - ubSoldierID <= gTacticalStatus.Team[ CIV_TEAM ].bLastID ) + ubSoldierID <= gTacticalStatus.Team[ CIV_TEAM ].bLastID ) { //only enemies and creatures. curr->pSoldier = MercPtrs[ ubSoldierID ]; } @@ -2301,7 +2311,7 @@ BOOLEAN LookAtButDontProcessEnemySoldierInitListLinks( HWFILE hfile ) UINT32 uiNumBytesRead; SOLDIERINITNODE *curr; UINT8 ubSlots, ubSoldierID, ubNodeID; - + FileRead( hfile, &ubSlots, 1, &uiNumBytesRead ); if( uiNumBytesRead != 1 ) { @@ -2329,7 +2339,7 @@ BOOLEAN LookAtButDontProcessEnemySoldierInitListLinks( HWFILE hfile ) { curr->ubSoldierID = ubSoldierID; if( ubSoldierID >= gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID && - ubSoldierID <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID ) + ubSoldierID <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID ) { //only enemies and creatures. curr->pSoldier = MercPtrs[ ubSoldierID ]; } @@ -2345,7 +2355,7 @@ void StripEnemyDetailedPlacementsIfSectorWasPlayerLiberated() { SECTORINFO *pSector; SOLDIERINITNODE *curr; - + if( !gfWorldLoaded || gbWorldSectorZ ) { //No world loaded or underground. Underground sectors don't matter //seeing enemies (not creatures) never rejuvenate underground. @@ -2407,15 +2417,15 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu switch( ubStrategicInsertionCode ) { - case INSERTION_CODE_NORTH: bDesiredDirection = SOUTHEAST; break; - case INSERTION_CODE_EAST: bDesiredDirection = SOUTHWEST; break; - case INSERTION_CODE_SOUTH: bDesiredDirection = NORTHWEST; break; - case INSERTION_CODE_WEST: bDesiredDirection = NORTHEAST; break; - default: AssertMsg( 0, "Illegal direction passed to AddSoldierInitListMilitiaOnEdge()" ); break; + case INSERTION_CODE_NORTH: bDesiredDirection = SOUTHEAST; break; + case INSERTION_CODE_EAST: bDesiredDirection = SOUTHWEST; break; + case INSERTION_CODE_SOUTH: bDesiredDirection = NORTHWEST; break; + case INSERTION_CODE_WEST: bDesiredDirection = NORTHEAST; break; + default: AssertMsg( 0, "Illegal direction passed to AddSoldierInitListMilitiaOnEdge()" ); break; } - #ifdef JA2TESTVERSION - ScreenMsg( FONT_RED, MSG_INTERFACE, L"Militia reinforcements have arrived! (%d admins, %d troops, %d elite)", ubNumGreen, ubNumReg, ubNumElites ); - #endif +#ifdef JA2TESTVERSION + ScreenMsg( FONT_RED, MSG_INTERFACE, L"Militia reinforcements have arrived! (%d admins, %d troops, %d elite)", ubNumGreen, ubNumReg, ubNumElites ); +#endif ChooseMapEdgepoints( &MapEdgepointInfo, ubStrategicInsertionCode, (UINT8)(ubNumGreen + ubNumReg + ubNumElites) ); ubCurrSlot = 0; @@ -2448,7 +2458,7 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu pSoldier->sNoiseGridno = (INT16)(CENTRAL_GRIDNO + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) + ( Random( CENTRAL_RADIUS * 2 + 1 ) - CENTRAL_RADIUS ) * WORLD_COLS); pSoldier->ubNoiseVolume = MAX_MISC_NOISE_DURATION; } - + //Setup the position if( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint @@ -2529,10 +2539,10 @@ void AddSoldierInitListMilitiaOnEdge( UINT8 ubStrategicInsertionCode, UINT8 ubNu pSoldier->ubNoiseVolume = MAX_MISC_NOISE_DURATION; } -// if ( GetTimeOfDayAmbientLightLevel() < NORMAL_LIGHTLEVEL_DAY + 2 ) -// gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition = TRUE; -// gTacticalStatus.Team[ MILITIA_TEAM ].bAwareOfOpposition = TRUE; - + // if ( GetTimeOfDayAmbientLightLevel() < NORMAL_LIGHTLEVEL_DAY + 2 ) + // gTacticalStatus.Team[ ENEMY_TEAM ].bAwareOfOpposition = TRUE; + // gTacticalStatus.Team[ MILITIA_TEAM ].bAwareOfOpposition = TRUE; + //Setup the position if( ubCurrSlot < MapEdgepointInfo.ubNumPoints ) { //using an edgepoint diff --git a/Tactical/Soldier Profile.cpp b/Tactical/Soldier Profile.cpp index fdb15adf..7301e86a 100644 --- a/Tactical/Soldier Profile.cpp +++ b/Tactical/Soldier Profile.cpp @@ -53,6 +53,7 @@ #include "personnel.h" #include "environment.h" #include "Player Command.h" + #include "strategic.h" #endif #ifdef JA2EDITOR @@ -1083,7 +1084,7 @@ BOOLEAN RecruitRPC( UINT8 ubCharNum ) } } // swap item to hand - SwapObjs( &(pNewSoldier->inv[ bSlot ]), &(pNewSoldier->inv[ HANDPOS ]) ); + SwapObjs( pNewSoldier, bSlot, HANDPOS ); } } @@ -1211,8 +1212,10 @@ BOOLEAN UnRecruitEPC( UINT8 ubCharNum ) } // how do we decide whether or not to set this? - gMercProfiles[ ubCharNum ].fUseProfileInsertionInfo = TRUE; gMercProfiles[ ubCharNum ].ubMiscFlags3 |= PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE; + gMercProfiles[ ubCharNum ].usStrategicInsertionData = pSoldier->sGridNo; + gMercProfiles[ ubCharNum ].fUseProfileInsertionInfo = TRUE; + gMercProfiles[ ubCharNum ].ubStrategicInsertionCode = INSERTION_CODE_GRIDNO; // Add this guy to CIV team! pNewSoldier = ChangeSoldierTeam( pSoldier, CIV_TEAM ); diff --git a/Tactical/Spread Burst.cpp b/Tactical/Spread Burst.cpp index a3193c7a..3115e966 100644 --- a/Tactical/Spread Burst.cpp +++ b/Tactical/Spread Burst.cpp @@ -150,7 +150,7 @@ void PickBurstLocations( SOLDIERTYPE *pSoldier ) ubShotsPerBurst = pSoldier->bDoAutofire; } else if ( gbNumBurstLocations > 0 ) - ubShotsPerBurst = pSoldier->bDoAutofire / gbNumBurstLocations; + ubShotsPerBurst = pSoldier->bDoAutofire; // / gbNumBurstLocations; } else @@ -163,8 +163,15 @@ void PickBurstLocations( SOLDIERTYPE *pSoldier ) ubShotsPerBurst = __min( ubShotsPerBurst, MAX_BURST_SPREAD_TARGETS); + if (ubShotsPerBurst == 1) + { + pSoldier->fDoSpread = FALSE; + return; + } + // Use # gridnos accululated and # burst shots to determine accululator - dStep = gbNumBurstLocations / (FLOAT)ubShotsPerBurst; + // Calculate it so that the actual last chosen shot location is the last spread point + dStep = (gbNumBurstLocations-1) / (FLOAT)(ubShotsPerBurst-1); //Loop through our shots! for ( cnt = 0; cnt < ubShotsPerBurst; cnt++ ) diff --git a/Tactical/Tactical Save.cpp b/Tactical/Tactical Save.cpp index 65668016..34da1e75 100644 --- a/Tactical/Tactical Save.cpp +++ b/Tactical/Tactical Save.cpp @@ -1198,7 +1198,7 @@ BOOLEAN LoadCurrentSectorsInformationFromTempItemsFile() } - //if the save is an older version, use theold way of oading it up + //if the save is an older version, use the old way of loading it up if( guiSavedGameVersion < 57 ) { if( DoesTempFileExistsForMap( SF_ENEMY_PRESERVED_TEMP_FILE_EXISTS, gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) ) diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index 70801afe..838a9280 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -556,7 +556,7 @@ void DisplayHiddenInterrupt( SOLDIERTYPE * pSoldier ) // Stop our guy.... AdjustNoAPToFinishMove( MercPtrs[ LATEST_INTERRUPT_GUY ], TRUE ); // Stop him from going to prone position if doing a turn while prone - MercPtrs[ LATEST_INTERRUPT_GUY ]->fTurningFromPronePosition = FALSE; + MercPtrs[ LATEST_INTERRUPT_GUY ]->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"about to call AddTopMessage"); // get rid of any old overlay message @@ -862,7 +862,7 @@ void StartInterrupt( void ) { // Stop this guy.... AdjustNoAPToFinishMove( MercPtrs[ LATEST_INTERRUPT_GUY ], TRUE ); - MercPtrs[ LATEST_INTERRUPT_GUY ]->fTurningFromPronePosition = FALSE; + MercPtrs[ LATEST_INTERRUPT_GUY ]->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; } DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"StartInterrupt done"); @@ -1770,7 +1770,7 @@ void VerifyOutOfTurnOrderArray() AdjustNoAPToFinishMove( MercPtrs[ gubOutOfTurnOrder[ ubNextIndex ] ], TRUE ); // If they were turning from prone, stop them - MercPtrs[ gubOutOfTurnOrder[ ubNextIndex ] ]->fTurningFromPronePosition = FALSE; + MercPtrs[ gubOutOfTurnOrder[ ubNextIndex ] ]->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; DeleteFromIntList( ubNextIndex, FALSE ); } @@ -1826,7 +1826,7 @@ void VerifyOutOfTurnOrderArray() AdjustNoAPToFinishMove( MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ], TRUE ); // If they were turning from prone, stop them - MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->fTurningFromPronePosition = FALSE; + MercPtrs[ gubOutOfTurnOrder[ ubLoop ] ]->bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; DeleteFromIntList( ubLoop, FALSE ); diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 07ec3a50..8c89903b 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -3110,7 +3110,7 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } else if ( bSlot2 != ITEM_NOT_FOUND ) { - SwapObjs( &(pTeamSoldier->inv[bSlot1]), &(pTeamSoldier->inv[bSlot2] ) ); + SwapObjs( pTeamSoldier, bSlot1, bSlot2 ); } break; } @@ -3153,7 +3153,7 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } else if ( bSlot2 != ITEM_NOT_FOUND ) { - SwapObjs( &(pTeamSoldier->inv[bSlot1]), &(pTeamSoldier->inv[bSlot2] ) ); + SwapObjs( pTeamSoldier, bSlot1, bSlot2 ); } break; } diff --git a/Tactical/UI Cursors.cpp b/Tactical/UI Cursors.cpp index 8743c30a..79308fdd 100644 --- a/Tactical/UI Cursors.cpp +++ b/Tactical/UI Cursors.cpp @@ -201,7 +201,8 @@ UINT8 GetProperItemCursor( UINT8 ubSoldierID, UINT16 ubItemIndex, UINT16 usMapPo if ( fRecalc && gfUIFullTargetFound ) { // ATE: Check for ammo - if ( IsValidTargetMerc( (UINT8)gusUIFullTargetID ) && EnoughAmmo( pSoldier, FALSE, HANDPOS ) ) + if ( IsValidTargetMerc( (UINT8)gusUIFullTargetID ) && EnoughAmmo( pSoldier, FALSE, HANDPOS ) && + (!IsValidSecondHandShotForReloadingPurposes( pSoldier) || EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS) ) ) { // IF it's an ememy, goto confirm action mode if ( ( guiUIFullTargetFlags & ENEMY_MERC ) && ( guiUIFullTargetFlags & VISIBLE_MERC ) && !( guiUIFullTargetFlags & DEAD_MERC ) && !gfCannotGetThrough ) @@ -972,7 +973,7 @@ UINT8 HandleNonActivatedTargetCursor( SOLDIERTYPE *pSoldier, UINT16 usMapPos , B } // Check for enough ammo... - if ( !EnoughAmmo( pSoldier, FALSE, HANDPOS ) ) + if ( !EnoughAmmo( pSoldier, FALSE, HANDPOS ) || (IsValidSecondHandShotForReloadingPurposes( pSoldier) && !EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS) ) ) { // Check if ANY ammo exists..... if ( FindAmmoToReload( pSoldier, HANDPOS, NO_SLOT ) == NO_SLOT ) diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 892d10bc..b288c8a8 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -1300,7 +1300,7 @@ BOOLEAN FireWeapon( SOLDIERTYPE *pSoldier , INT16 sTargetGridNo ) // SET ATTACKER TO NOBODY, WILL GET SET EVENTUALLY pSoldier->ubOppNum = NOBODY ; - UINT16 usItemClass = Item[ pSoldier->usAttackingWeapon ].usItemClass; + UINT32 usItemClass = Item[ pSoldier->usAttackingWeapon ].usItemClass; if ( pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO ) usItemClass = IC_LAUNCHER; @@ -1316,15 +1316,19 @@ BOOLEAN FireWeapon( SOLDIERTYPE *pSoldier , INT16 sTargetGridNo ) // ATE: PAtch up - bookkeeping for spreading done out of whak if ( pSoldier->fDoSpread) { - if (pSoldier->bDoBurst ) - { - pSoldier->fDoSpread = FALSE; - } + + // 0verhaul: This check does not work! All auto-fire has bDoBurst turned on. And only allowing a spread + // for a single-shot mode is useless. + //if (pSoldier->bDoBurst ) + //{ + // pSoldier->fDoSpread = FALSE; + //} + //else // 0verhaul: The original code seemed brain damaged: If the current spread target was 0 it would shoot at the // non-spread target grid # instead. Also fDoSpread is used as a counter from 1 to MAX_BURST_SPREAD_TARGETS, // but was actually reset before it got there. So the final spread target would never be shot at. Hopefully this // will work better. - else if (pSoldier->fDoSpread > MAX_BURST_SPREAD_TARGETS || + if (pSoldier->fDoSpread > MAX_BURST_SPREAD_TARGETS || pSoldier->sSpreadLocations[ pSoldier->fDoSpread - 1 ] == 0) { if (pSoldier->fDoSpread == 1) @@ -1574,7 +1578,7 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT16 sTargetGridNo ) sprintf( zBurstString, gzBurstSndStrings[ Weapon[ usItemNum ].sBurstSound ], bShotsToFire ); INT8 volume = HIGHVOLUME; - if ( noisefactor < 100 ) volume = (volume * noisefactor) / 100; + if ( noisefactor < 100 ) volume = (INT8) ((volume * noisefactor) / 100); // Try playing sound... pSoldier->iBurstSoundID = PlayJA2SampleFromFile( zBurstString, RATE_11025, SoundVolume( (INT8) volume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); } diff --git a/Tactical/fov.cpp b/Tactical/fov.cpp index 084e75dc..5cb0ed6b 100644 --- a/Tactical/fov.cpp +++ b/Tactical/fov.cpp @@ -288,7 +288,8 @@ void ExamineSlantRoofFOVSlots( ) void RevealRoofsAndItems(SOLDIERTYPE *pSoldier, UINT32 itemsToo, BOOLEAN fShowLocators, UINT8 ubLevel, BOOLEAN fForce ) { - UINT32 maincnt,markercnt,marker,tilesLeftToSee,cnt,prevmarker; + UINT32 maincnt,markercnt,marker,tilesLeftToSee,prevmarker; + UINT8 cnt; INT32 Inc[6],Dir[6]; INT8 itemVisible = FALSE; INT8 Blocking,twoMoreTiles,markerDir; @@ -371,9 +372,7 @@ void RevealRoofsAndItems(SOLDIERTYPE *pSoldier, UINT32 itemsToo, BOOLEAN fShowLo BuildSightDir(dir,(UINT32 *)&Dir[0],(UINT32 *)&Dir[1],(UINT32 *)&Dir[2],(UINT32 *)&Dir[3],(UINT32 *)&Dir[4]); for (cnt = 0; cnt < 5; cnt++) - { - Inc[cnt] = DirectionInc( Dir[cnt] ); - } + Inc[cnt] = DirectionInc( (UINT8) Dir[cnt]); // create gridno increment for NOVIEW - in other words, no increment! Inc[5] = 0; diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index 00db003a..fa95a921 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -4409,8 +4409,8 @@ void WriteQuantityAndAttachments( OBJECTTYPE *pObject, INT32 yp ) { //ammo if( pObject->ubNumberOfObjects > 1 ) { - CHAR16 str[50]; - CHAR16 temp[5]; + CHAR16 str[100]; + CHAR16 temp[10]; UINT8 i; swprintf( str, L"Clips: %d (%d", pObject->ubNumberOfObjects, pObject->ItemData.Generic.bStatus[0] ); for( i = 1; i < pObject->ubNumberOfObjects; i++ )