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++ )