diff --git a/GameVersion.cpp b/GameVersion.cpp index cd9e66b5..0f064d5b 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8399 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8399 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8399 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8399 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8399 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8399 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8399 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8398 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8399 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 17.04.02" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 17.04.12" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Init.cpp b/Init.cpp index 450254f6..9fc9db45 100644 --- a/Init.cpp +++ b/Init.cpp @@ -1036,7 +1036,10 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) char tauntFileNamePattern[MAX_PATH]; strcpy(tauntFileNamePattern, directoryName); strcat(tauntFileNamePattern, TAUNTSFOLDERNAME); - strcat(tauntFileNamePattern, TAUNTSFILENAMEBEGINNING"*"TAUNTSFILENAMEENDING); + strcat(tauntFileNamePattern, TAUNTSFILENAMEBEGINNING); + strcat(tauntFileNamePattern, "*"); + strcat(tauntFileNamePattern, TAUNTSFILENAMEENDING); + if( GetFileFirst(tauntFileNamePattern, &FileInfo) ) { strcpy(fileName, directoryName); @@ -1650,7 +1653,6 @@ UINT32 InitializeJA2(void) //Lua IniLuaGlobal(); - return( INIT_SCREEN ); } diff --git a/Laptop/PMC.cpp b/Laptop/PMC.cpp index d30dc574..f1f30ccb 100644 --- a/Laptop/PMC.cpp +++ b/Laptop/PMC.cpp @@ -557,7 +557,7 @@ void PMCTeamHireCallback( GUI_BUTTON *btn, INT32 reason ) } } - if ( found = TRUE ) + if ( found == TRUE ) break; } diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index b2a0c658..67ea4012 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -3259,7 +3259,7 @@ BOOLEAN OBJECTTYPE::Save( HWFILE hFile, bool fSavingMap ) } if (this->IsActiveLBE(x) == true) { LBENODE* pLBE = this->GetLBEPointer(x); - if (! pLBE->Save(hFile, fSavingMap)) { + if (!pLBE || !pLBE->Save(hFile, fSavingMap)) { return FALSE; } //CHRISL: I've remarked this to resolve a problem when the autosave option is active where LBEArray info is diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index b547f1ad..4288c16d 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -4587,6 +4587,9 @@ OBJECTTYPE* FindRepairableItemInLBENODE(SOLDIERTYPE * pSoldier, OBJECTTYPE * pOb if(pObj->IsActiveLBE(subObject) == true) { LBENODE* pLBE = pObj->GetLBEPointer(subObject); + + if (!pLBE) return(NULL); + UINT8 invsize = pLBE->inv.size(); for(UINT8 lbePocket = 0; lbePocket < invsize; ++lbePocket) { @@ -4608,7 +4611,7 @@ OBJECTTYPE* FindRepairableItemInLBENODE(SOLDIERTYPE * pSoldier, OBJECTTYPE * pOb } } } - return( 0 ); + return(NULL); } OBJECTTYPE* FindRepairableItemInSpecificPocket(SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, UINT8 subObject) @@ -4799,19 +4802,22 @@ BOOLEAN RepairObject( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOwner, OBJECTTYPE * } //CHRISL: Now check and see if this is an LBENODE with items that need repairing - if(UsingNewInventorySystem() == true && Item[pObj->usItem].usItemClass == IC_LBEGEAR && pObj->IsActiveLBE(ubLoop) == true) + if (UsingNewInventorySystem() == true && Item[pObj->usItem].usItemClass == IC_LBEGEAR && pObj->IsActiveLBE(ubLoop) == true) { LBENODE* pLBE = pObj->GetLBEPointer(ubLoop); - UINT8 invsize = pLBE->inv.size(); - for(lbeLoop = 0; lbeLoop < invsize; ++lbeLoop) - { - if(RepairObject(pSoldier, pOwner, &pLBE->inv[lbeLoop], pubRepairPtsLeft)) + if (pLBE) { + + UINT8 invsize = pLBE->inv.size(); + for (lbeLoop = 0; lbeLoop < invsize; ++lbeLoop) { - fSomethingWasRepaired = true; - if ( *pubRepairPtsLeft == 0 ) + if (RepairObject(pSoldier, pOwner, &pLBE->inv[lbeLoop], pubRepairPtsLeft)) { - // we're out of points! - return true; + fSomethingWasRepaired = true; + if (*pubRepairPtsLeft == 0) + { + // we're out of points! + return true; + } } } } @@ -17082,7 +17088,14 @@ void ReEvaluateEveryonesNothingToDo() INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; BOOLEAN fNothingToDo; - + + UINT32 numberOfMovableItemsCache[MAXIMUM_VALID_X_COORDINATE][MAXIMUM_VALID_Y_COORDINATE]; + for (int i = 0; i < MAXIMUM_VALID_X_COORDINATE; i++) { + for (int j = 0; j < MAXIMUM_VALID_Y_COORDINATE; j++) { + numberOfMovableItemsCache[i][j] = INT_MAX; + } + } + for( iCounter = 0; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++iCounter ) { pSoldier = &Menptr[ iCounter ]; @@ -17168,7 +17181,12 @@ void ReEvaluateEveryonesNothingToDo() INT16 targetX = SECTORX( pSoldier->usItemMoveSectorID ); INT16 targetY = SECTORY( pSoldier->usItemMoveSectorID ); - fNothingToDo = (GetNumberOfMovableItems( targetX, targetY, 0 ) == 0); + if (numberOfMovableItemsCache[targetX][targetY] == INT_MAX) + { + numberOfMovableItemsCache[targetX][targetY] = GetNumberOfMovableItems(targetX, targetY, 0); + } + + fNothingToDo = (numberOfMovableItemsCache[targetX][targetY] == 0); } break; diff --git a/Strategic/Hourly Update.cpp b/Strategic/Hourly Update.cpp index cac65d72..e0734b9b 100644 --- a/Strategic/Hourly Update.cpp +++ b/Strategic/Hourly Update.cpp @@ -855,7 +855,7 @@ void HourlyHelicopterRepair() { return; } - if( pVehicleList[ iHelicopterVehicleId ].sSectorX == gMercProfiles[ WALDO ].sSectorX && pVehicleList[ iHelicopterVehicleId ].sSectorX == gMercProfiles[ WALDO ].sSectorX ) + if( pVehicleList[ iHelicopterVehicleId ].sSectorX == gMercProfiles[ WALDO ].sSectorX && pVehicleList[ iHelicopterVehicleId ].sSectorY == gMercProfiles[ WALDO ].sSectorY ) { gubHelicopterHoursToRepair--; } diff --git a/Strategic/Map Screen Interface Map Inventory.cpp b/Strategic/Map Screen Interface Map Inventory.cpp index 1ea96673..11aa6967 100644 --- a/Strategic/Map Screen Interface Map Inventory.cpp +++ b/Strategic/Map Screen Interface Map Inventory.cpp @@ -1491,7 +1491,7 @@ void MapInvenPoolSlots(MOUSE_REGION * pRegion, INT32 iReason ) // Buggler: sector inventory item deletion before item reachable & in-sector checks to allow deletion of any item if ( _KeyDown ( DEL ) ) { - if ( _KeyDown ( 89 )) //Lalien: delete all items of this type + if ( _KeyDown ( 89 )) //Lalien: delete all items of this type (Y) { DeleteItemsOfType( pInventoryPoolList[ ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) + iCounter ].object.usItem ); ScreenMsg( FONT_MCOLOR_LTRED, MSG_INTERFACE, New113Message[MSG113_DELETE_ALL] ); diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index a03d6259..c29a6873 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -7081,7 +7081,11 @@ void GetMapKeyboardInput( UINT32 *puiNewEvent ) break; case SPACE: - if( fShowUpdateBox ) + + if (fCtrl) { + checkLBEArrayIntegrity(); + } + else if( fShowUpdateBox ) { EndUpdateBox( TRUE ); // restart time compression } diff --git a/Tactical/DisplayCover.cpp b/Tactical/DisplayCover.cpp index b1457e37..2ac7ca37 100644 --- a/Tactical/DisplayCover.cpp +++ b/Tactical/DisplayCover.cpp @@ -433,7 +433,7 @@ void RemoveCoverObjectsFromViewArea() INT8& bOverlayType = gCoverViewArea[ubX][ubY][ubZ].bOverlayType; - if ( bOverlayType != INVALID_COVER ) + if ( bOverlayType != INVALID_COVER && sGridNo != -1 ) { TileDefines tile = GetOverlayIndex( bOverlayType ); RemoveCoverObjectFromWorld( sGridNo, tile, (BOOLEAN) ubZ ); diff --git a/Tactical/DynamicDialogue.cpp b/Tactical/DynamicDialogue.cpp index a3f6ddac..811c5d13 100644 --- a/Tactical/DynamicDialogue.cpp +++ b/Tactical/DynamicDialogue.cpp @@ -1275,7 +1275,7 @@ INT8 GetDynamicOpinionDay( UINT8 usProfileA, UINT8 usProfileB, UINT8 usDay ) { INT32 opinion = 0; - if ( usProfileA == NO_PROFILE || usProfileA == NO_PROFILE ) + if ( usProfileA == NO_PROFILE || usProfileB == NO_PROFILE ) return opinion; // only for today (0) or one of the last 3 days (1-3) diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index 46c75a81..d26f0e38 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -675,30 +675,22 @@ void popupCallbackItem(INT16 itemId){ //UpdateAttachmentTooltips(gpItemDescObject, gubItemDescStatusIndex); return; } -/* - for(UINT16 i = 0; i < pInventoryPoolList.size(); i++){ - if( pInventoryPoolList[i].object.usItem == itemId ) { - - gpItemPointer = &pInventoryPoolList[i].object; // pick up the object (or stack) - DoAttachment((UINT8)gubPopupStatusIndex, guiPopupItemPos); // try to attach it - gpItemPointer = NULL; // and drop it - - gItemDescAttachmentPopups[giActiveAttachmentPopup]->hide(); - RenderItemDescriptionBox(); - giActiveAttachmentPopup = -1; - - //UpdateAttachmentTooltips(gpItemDescObject, gubItemDescStatusIndex); - return; - - } - } -*/ } -bool popupCallbackItemInSector(INT16 itemId){ - for(UINT16 i = 0; i < pInventoryPoolList.size(); i++) - if( pInventoryPoolList[i].object.usItem == itemId ) return true; - +bool popupCallbackItemInSector(INT16 usAttachment, OBJECTTYPE * pObj, POPUP_OPTION * o){ + for (UINT16 i = 0; i < pInventoryPoolList.size(); i++) { + if (pInventoryPoolList[i].object.usItem == usAttachment) { + if (ValidItemAttachmentSlot(pObj, usAttachment, true, false, 0, -1, true, NULL, GetItemSlots(pObj))) { + return true; + } + else { + o->color_shade = COLOR_RED; + return false; + } + } + } + //return ValidAttachment(itemId, pObj); + o->color_shade = COLOR_DKGREY; return false; } @@ -2504,11 +2496,11 @@ void PocketPopupDefault( SOLDIERTYPE *pSoldier, INT16 sPocket ){ if( LBEPocketPopup.find(pocketType) == LBEPocketPopup.end() ){ // default for LBE slots - grenades + ammo for merc's guns - addAmmoToPocketPopup( pSoldier, sPocket, popup ); + addAmmoToPocketPopup( pSoldier, sPocket, popup ); - POPUP * subPopup = popup->addSubMenuOption( new std::wstring( BobbyRFilter[28]/*Grenades*/ ) ); - popup->getSubPopupOption( popup->subPopupOptionCount-1 )->setPopupPosition( 10,10,POPUP_POSITION_RELATIVE ); - addGrenadesToPocketPopup( pSoldier, sPocket, subPopup ); + POPUP * subPopup = popup->addSubMenuOption( new std::wstring( BobbyRFilter[28]/*Grenades*/ ) ); + popup->getSubPopupOption( popup->subPopupOptionCount-1 )->setPopupPosition( 10,10,POPUP_POSITION_RELATIVE ); + addGrenadesToPocketPopup( pSoldier, sPocket, subPopup ); } else { // popup definition for this slot exists - apply it to the popup gsPocketUnderCursor = sPocket; @@ -5885,16 +5877,24 @@ void UpdateAttachmentTooltips(OBJECTTYPE *pObject, UINT8 ubStatusIndex) POPUP_OPTION * o = new POPUP_OPTION( &std::wstring( Item[ usAttachment ].szItemName ), new popupCallbackFunction(&popupCallbackItem,usAttachment)); // set an availiability callback to gray out any compatible attachments not found in this sector - o->setAvail( new popupCallbackFunction(&popupCallbackItemInSector,usAttachment) ); + o->setAvail( new popupCallbackFunction3(&popupCallbackItemInSector,usAttachment, pObject, o) ); if (loop == 11 && attachList.size() > 11){ // if there's too much stuff to list, we create a subpopup for the rest gItemDescAttachmentPopups[slotCount]->addSubMenuOption( &std::wstring(L"More...") ); POPUP_SUB_POPUP_OPTION * tmp = gItemDescAttachmentPopups[slotCount]->getSubPopupOption(0); // positioning sub popups is handled through the option that holds them - tmp->setPopupPosition( (gsInvDescX + gItemDescAttachmentsXY[slotCount].sX), - (gsInvDescY + gItemDescAttachmentsXY[slotCount].sY) + 32 , - POPUP_POSITION_TOP_RIGHT); // put it to the right of the main box + if (gItemDescAttachmentPopups[slotCount]->Position.iX + 200 > SCREEN_WIDTH) { + // near screen edge, put it below the original box + tmp->setPopupPosition( + gItemDescAttachmentPopups[slotCount]->Position.iX, + gItemDescAttachmentPopups[slotCount]->Position.iY + gItemDescAttachmentPopups[slotCount]->getCurrentHeight(), + POPUP_POSITION_TOP_LEFT + ); + } + else { // we still got room, position the next box to the right of the previous one + tmp->setPopupPosition(10,0,POPUP_POSITION_RELATIVE); + } // hide the other box too // note that we're working with the sub popup options's sub-popup here, not the option itself @@ -6392,9 +6392,10 @@ void ItemDescAttachmentsCallback( MOUSE_REGION * pRegion, INT32 iReason ) { LBENODE* pLBE = gpItemDescObject->GetLBEPointer(gubItemDescStatusIndex); // we have an item in this pocket - if( pLBE->inv[(AttachmentSlots[usAttachmentSlotIndexVector[slotCount]].ubPocketMapping -1)].exists() ) + if (pLBE && pLBE->inv[(AttachmentSlots[usAttachmentSlotIndexVector[slotCount]].ubPocketMapping - 1)].exists()) { // place item on the ground - AutoPlaceObjectToWorld(gpItemDescSoldier, &pLBE->inv[(AttachmentSlots[usAttachmentSlotIndexVector[slotCount]].ubPocketMapping -1)], TRUE); + AutoPlaceObjectToWorld(gpItemDescSoldier, &pLBE->inv[(AttachmentSlots[usAttachmentSlotIndexVector[slotCount]].ubPocketMapping - 1)], TRUE); + } } else // the soldier is wearing the LBE { @@ -6696,12 +6697,16 @@ INT8 DetermineShowLBE( ) } else { - if(gpItemDescObject->IsActiveLBE(gubItemDescStatusIndex)) - return (gpItemDescObject->GetLBEPointer(gubItemDescStatusIndex)->lbeClass - 1); - else if(Item[gpItemDescObject->usItem].usItemClass == IC_LBEGEAR) + if (gpItemDescObject->IsActiveLBE(gubItemDescStatusIndex)) { + LBENODE* pLBE = gpItemDescObject->GetLBEPointer(gubItemDescStatusIndex); + return pLBE ? (pLBE->lbeClass - 1) : -1; + } + else if (Item[gpItemDescObject->usItem].usItemClass == IC_LBEGEAR) { return (LoadBearingEquipment[Item[gpItemDescObject->usItem].ubClassIndex].lbeClass - 1); - else + } + else { return -1; + } } } } @@ -8049,9 +8054,8 @@ void RenderLBENODEItems( OBJECTTYPE *pObj, int subObject ) } LBENODE* pLBE = NULL; - if(activeNode == true) - { - pLBE = pObj->GetLBEPointer(subObject); + if(activeNode == true && ( pLBE = pObj->GetLBEPointer(subObject) ) != NULL ) + { lClass = pLBE->lbeClass; } else { diff --git a/Tactical/Interface Panels.cpp b/Tactical/Interface Panels.cpp index 03c862a1..5ba8f6b0 100644 --- a/Tactical/Interface Panels.cpp +++ b/Tactical/Interface Panels.cpp @@ -4154,7 +4154,8 @@ BOOLEAN ChangeDropPackStatus(SOLDIERTYPE *pSoldier, BOOLEAN newStatus) break; } if(gWorldItems[wi].object.IsActiveLBE(x)) { - if(gWorldItems[wi].object.GetLBEPointer(x)->lbeIndex != NONE) { + LBENODE* pLBE = gWorldItems[wi].object.GetLBEPointer(x); + if(pLBE && pLBE->lbeIndex != NONE) { // Found an associated backpack so figure out how far that pack is from us iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, gWorldItems[wi].sGridNo ); if(gWorldItems[wi].sGridNo == pSoldier->sGridNo) // standing on pack - pickup regardless diff --git a/Tactical/Item Types.cpp b/Tactical/Item Types.cpp index 264d960f..581fc948 100644 --- a/Tactical/Item Types.cpp +++ b/Tactical/Item Types.cpp @@ -6,6 +6,9 @@ #include "screenids.h" #include "Action Items.h" // added by Flugente for the ACTION_ITEM_BLOW_UP value #include "Random.h" // added by Flugente +#include "Message.h" // added by BOB for missing LBE info messages +#include "overhead.h" // added by BOB for missing LBE info messages +#include "Map Screen Interface.h" // added by BOB for missing LBE info messages int BODYPOSFINAL = GUNSLINGPOCKPOS;//RESET in initInventory @@ -24,6 +27,83 @@ extern UINT32 guiCurrentItemDescriptionScreen; extern BOOLEAN fShowMapInventoryPool; //extern BOOLEAN AutoPlaceObjectInInventoryStash( OBJECTTYPE *pItemPtr, INT32 sGridNo ); +bool checkObjectLBEIntegrity(OBJECTTYPE * object) { + bool integrityCheck = TRUE; + + + for (int i = 0; i < object->ubNumberOfObjects; i++) + { + if ((*object)[i]->data.lbe.bLBE == -1 && object->GetLBEPointer(i) == NULL) { + integrityCheck = false; + } + } + + return integrityCheck; +} + +bool checkLBEArrayIntegrity(bool verbose) { + bool integrityCheck = TRUE; + int itemCnt = 0; + + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"LBENODE integrity check start: checking soldier items..."); + + for (int i = 0; i < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; i++) { + if (!gCharactersList[i].fValid || gCharactersList[i].usSolID < 0) continue; + + int id = gCharactersList[i].usSolID; + SOLDIERTYPE soldier = Menptr[id]; + + if (verbose)ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"LBENODE integrity check start: checking soldier items (%s)...", soldier.name); + + for (int j = 0; j < soldier.inv.size(); j++) { + OBJECTTYPE * object = &(soldier.inv[j]); + if (object->HasAnyActiveLBEs()) { + + if (!checkObjectLBEIntegrity(object)) { + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"> LBENODE missing: %s -> %s!", soldier.name, Item[object->usItem].szItemName); + integrityCheck = false; + } + else { + if (verbose)ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"> OK: %s -> %s", soldier.name, Item[object->usItem].szItemName); + } + } + else { + if (verbose)ScreenMsg(FONT_MCOLOR_LTGRAY, MSG_INTERFACE, L"> SKIP: %s -> %s", soldier.name, Item[object->usItem].szItemName); + } + } + } + + + + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"LBENODE integrity check start: checking around %i world items", gWorldItems.size()); + for (std::vector::iterator worldItem = gWorldItems.begin(); worldItem != gWorldItems.end(); ++worldItem) + { + OBJECTTYPE* object = &(worldItem->object); + if (object->HasAnyActiveLBEs()) { + if (!checkObjectLBEIntegrity(object)) { + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"> LBENODE missing: loaded sector -> %s!", Item[object->usItem].szItemName); + integrityCheck = false; + } + else { + if (verbose)ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"> OK: loaded sector -> %s", Item[object->usItem].szItemName); + } + } + else { + if (verbose)ScreenMsg(FONT_MCOLOR_LTGRAY, MSG_INTERFACE, L"> SKIP: loaded sector -> %s", Item[object->usItem].szItemName); + } + itemCnt++; + } + + if (integrityCheck) { + ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"LBENODE integrity check passed!"); + } + else { + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Missing LBENODE(s) detected! Restart the game and check last save for corruption."); + } + + return integrityCheck; +} + bool IsSlotAnLBESlot(int slot) { switch(slot) { @@ -179,13 +259,15 @@ void MoveItemsInSlotsToLBE( SOLDIERTYPE *pSoldier, std::vector& LBESlots, // CHRISL: New function to move items from default pockets to usable pockets BOOLEAN MoveItemsToActivePockets( SOLDIERTYPE *pSoldier, std::vector& LBESlots, UINT32 uiHandPos, OBJECTTYPE *pObj ) { - BOOLEAN flag=FALSE; + BOOLEAN flag= FALSE; if(pObj->IsActiveLBE(0) == false) { CreateLBE(pObj, pSoldier->ubID, LBESlots.size()); } LBENODE* pLBE = pObj->GetLBEPointer(0); + if (!pLBE) return FALSE; + MoveItemsInSlotsToLBE(pSoldier, LBESlots, pLBE, pObj); DestroyLBEIfEmpty(pObj); @@ -327,7 +409,7 @@ BOOLEAN MoveItemToLBEItem( SOLDIERTYPE *pSoldier, UINT32 uiHandPos ) CreateLBE(&(pSoldier->inv[uiHandPos]), pSoldier->ubID, LBESlots.size()); LBENODE* pLBE = pSoldier->inv[uiHandPos].GetLBEPointer(0); - for(unsigned int i=0; iinv[LBESlots[i]].exists() == true) @@ -359,7 +441,7 @@ BOOLEAN MoveItemFromLBEItem( SOLDIERTYPE *pSoldier, UINT32 uiHandPos, OBJECTTYPE } LBENODE* pLBE = pObj->GetLBEPointer(0); - for(unsigned int i=0; iinv[i].exists() == true) @@ -480,18 +562,38 @@ bool OBJECTTYPE::HasAnyActiveLBEs(SOLDIERTYPE * pSoldier, UINT8 iter) LBENODE* OBJECTTYPE::GetLBEPointer(unsigned int index) { + CHAR16 msgTemp[150]; if (LBEArray.empty() == false) { int uniqueID = (*this)[index]->data.lbe.uniqueID; + for (std::list::iterator iter = LBEArray.begin(); iter != LBEArray.end(); ++iter) { if (iter->uniqueID == uniqueID) { return &(*iter); } } + + //BOB: if we got here somehow it means we're missing LBE data for this uniqueID + { + + swprintf(msgTemp, L"> Missing LBENODE for %s!", Item[this->usItem].szItemName); + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, msgTemp); + // mark as no longer active LBE + (*this)[index]->data.lbe.bLBE = 0; + } + + return NULL; } - //CHRISL: if the LBEArray is empty, we probably need to force the creation of an entry so that future functions don't crash. - //return NULL; - LBEArray.resize(1); - return &(*LBEArray.begin()); + else //BOB: only do this if the LBEArray was actually empty! Otherwise this would create a cascading clusterfuck of missing LBE data. + { + //CHRISL: if the LBEArray is empty, we probably need to force the creation of an entry so that future functions don't crash. + + swprintf(msgTemp, L"Reinit LBEArray!"); + ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, msgTemp); + + LBEArray.resize(1); + return &(*LBEArray.begin()); + } + } bool OBJECTTYPE::exists() diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index b3d21e17..86b8de8b 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -254,6 +254,8 @@ public: std::vector inv; }; #define SIZEOF_LBENODE_POD (offsetof(LBENODE, endOfPOD)) +bool checkLBEArrayIntegrity(bool verbose=false); // BOB: checks all worldItems for missing LBE info + void CreateLBE(OBJECTTYPE* pObj, UINT8 ubID, int numSubPockets); bool DestroyLBEIfEmpty(OBJECTTYPE* pObj); void DestroyLBE(OBJECTTYPE* pObj); diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 133171b3..c8e36d7d 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -4836,7 +4836,7 @@ BOOLEAN OBJECTTYPE::AttachObjectNAS( SOLDIERTYPE * pSoldier, OBJECTTYPE * pAttac } LBENODE* pLBE = pAttachment->GetLBEPointer(0); - for(unsigned int i=0; iinv[i].exists() ) diff --git a/Tactical/RandomMerc.cpp b/Tactical/RandomMerc.cpp index f110fac8..885a04ac 100644 --- a/Tactical/RandomMerc.cpp +++ b/Tactical/RandomMerc.cpp @@ -989,7 +989,7 @@ void RandomizeMerc(UINT8 profile_id, MERCPROFILESTRUCT* merc, BOOL random_gear_k } //sidearm for snipers/rifle users (50% chance), and sometimes to just someone randomly (10% chance). - if (((weapon->ubWeaponType == GUN_SN_RIFLE || weapon->ubWeaponType == GUN_SN_RIFLE) && Random(2) < 1) || (Random(10) < 1 && weapon->ubWeaponType != GUN_PISTOL)) + if ((( weapon->ubWeaponType == GUN_SN_RIFLE || weapon->ubWeaponType == GUN_RIFLE) && Random(2) < 1) || (Random(10) < 1 && weapon->ubWeaponType != GUN_PISTOL)) { INVTYPE* pistol = GearGetRandomWeapon(min_kit_level, max_kit_level - 1, GUN_PISTOL, -1); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 5e668077..9922b236 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -22397,7 +22397,7 @@ BOOLEAN IsValidArtilleryOrderSector( INT16 sSectorX, INT16 sSectorY, INT8 bSecto UINT16 usEnemies = (UINT16)NumNonPlayerTeamMembersInSector( sSectorX, sSectorY, ENEMY_TEAM ); UINT16 usMilitia = (UINT16)NumNonPlayerTeamMembersInSector( sSectorX, sSectorY, MILITIA_TEAM ); - UINT16 usMercs = NumPlayerTeamMembersInSector( sSectorX, sSectorY, bSectorZ ); + UINT16 usMercs = (UINT16)PlayerMercsInSector( (UINT8)sSectorX, (UINT8)sSectorY, (UINT8)bSectorZ ); SECTORINFO *pSectorInfo = &(SectorInfo[SECTOR( sSectorX, sSectorY )]); diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index b7bf8b87..26042ccd 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -1101,7 +1101,10 @@ void StartInterrupt( void ) if ( !gfHiddenInterrupt ) { // Stop this guy.... - if (MercPtrs[LATEST_INTERRUPT_GUY]->exists() )//MM: this was crashing if the LATEST_INTERRUPT_GUY wasn't set + if ( LATEST_INTERRUPT_GUY != END_OF_INTERRUPTS // BOB: is this just a blank? + && LATEST_INTERRUPT_GUY <= TOTAL_SOLDIERS // BOB: sanity check + && MercPtrs[LATEST_INTERRUPT_GUY]->exists() //MM: this was crashing if the LATEST_INTERRUPT_GUY wasn't set + ) { MercPtrs[ LATEST_INTERRUPT_GUY ]->AdjustNoAPToFinishMove( TRUE ); MercPtrs[ LATEST_INTERRUPT_GUY ]->flags.bTurningFromPronePosition = TURNING_FROM_PRONE_OFF; diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index b14aa7b1..5f329ff1 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -2208,7 +2208,16 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) { if ( !InKeyRingPopup( ) ) { - if ( _KeyDown( SHIFT ) ) + + if (_KeyDown(CTRL)) + { + checkLBEArrayIntegrity(); + } + else if (_KeyDown(ALT)) + { + checkLBEArrayIntegrity(true); + } + else if (_KeyDown(SHIFT)) { SOLDIERTYPE *pNewSoldier; INT32 iCurrentSquad; diff --git a/TileEngine/Render Dirty.cpp b/TileEngine/Render Dirty.cpp index c0c2d60d..7519de1c 100644 --- a/TileEngine/Render Dirty.cpp +++ b/TileEngine/Render Dirty.cpp @@ -1182,22 +1182,29 @@ void SaveVideoOverlaysArea( UINT32 uiSrcBuffer ) { if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled ) { + iBackIndex = gVideoOverlays[uiCount].uiBackground; + // OK, if our saved area is null, allocate it here! if ( gVideoOverlays[uiCount].pSaveArea == NULL ) { AllocateVideoOverlayArea( uiCount ); } - if ( gVideoOverlays[uiCount].pSaveArea != NULL ) + if ( gVideoOverlays[uiCount].pSaveArea != NULL && gBackSaves[iBackIndex].sHeight > 0 && gBackSaves[iBackIndex].sWidth > 0) { - iBackIndex = gVideoOverlays[uiCount].uiBackground; - - // Save data from frame buffer! - Blt16BPPTo16BPP((UINT16 *)gVideoOverlays[uiCount].pSaveArea, gBackSaves[ iBackIndex ].sWidth*2, - (UINT16 *)pSrcBuf, uiSrcPitchBYTES, - 0, 0, - gBackSaves[ iBackIndex ].sLeft , gBackSaves[ iBackIndex ].sTop, - gBackSaves[ iBackIndex ].sWidth, gBackSaves[ iBackIndex ].sHeight ); + // Save data from frame buffer! + Blt16BPPTo16BPP( + (UINT16 *)gVideoOverlays[uiCount].pSaveArea, + gBackSaves[iBackIndex].sWidth * 2, + (UINT16 *)pSrcBuf, + uiSrcPitchBYTES, + 0, + 0, + gBackSaves[iBackIndex].sLeft, + gBackSaves[iBackIndex].sTop, + gBackSaves[iBackIndex].sWidth, + gBackSaves[iBackIndex].sHeight + ); } } } diff --git a/Utils/Timer Control.cpp b/Utils/Timer Control.cpp index 46e1b707..972ec790 100644 --- a/Utils/Timer Control.cpp +++ b/Utils/Timer Control.cpp @@ -14,7 +14,6 @@ #include "renderworld.h" #include "interface control.h" #include "keymap.h" - #endif #ifndef WIN32_LEAN_AND_MEAN @@ -27,7 +26,7 @@ // Base resolution of callback timer static INT32 BASETIMESLICE = 10; const INT32 FASTFORWARDTIMESLICE = 1000; -const UINT32 FREQUENCY_CONST = 1000000; +const LONGLONG FREQUENCY_CONST = 1000000; static INT32 MIN_NOTIFY_TIME = 16000; static INT32 UPDATETIMESLICE = 10000; @@ -118,6 +117,12 @@ SOLDIERTYPE *gPSOLDIER; UINT32 guiClockDiff = 0; UINT32 guiClockStart = 0; +// BOB: made global to help track freeze issue +LONGLONG gliTimestampDiff = 0; +LONGLONG gliWaitTime = 0; +LONGLONG giIncrement = 0; +UINT32 giSleepTime = 0; + extern UINT32 guiCompressionStringBaseTime; extern INT32 giFlashHighlightedItemBaseTime; @@ -151,14 +156,13 @@ static void BroadcastTimerNotify(INT32 ); static BOOLEAN UpdateTimeCounter( INT32 &counter, INT32 &iTimeLeft ); static BOOLEAN UpdateCounter( INT32 counter, INT32 &iTimeLeft); static void UpdateTimer(); - +void ResetJA2ClockGlobalTimers(void); UINT32 InitializeJA2TimerCallback( UINT32 uiDelay, LPTIMECALLBACK TimerProc, UINT32 uiUser ); // CALLBACKS void CALLBACK FlashItem( UINT uiID, UINT uiMsg, DWORD uiUser, DWORD uiDw1, DWORD uiDw2 ); - void CALLBACK TimeProc( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2 ) { static BOOLEAN fInFunction = FALSE; @@ -180,7 +184,8 @@ void CALLBACK TimeProc( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2 if (gliPerfCount.QuadPart > gliPerfCountNext.QuadPart) { INT32 iNext = IsFastForwardMode() ? giFastForwardPeriod : UPDATETIMESLICE; - gliPerfCountNext.QuadPart = gliPerfCount.QuadPart + (iNext * gliPerfFreq.QuadPart) / FREQUENCY_CONST; + giIncrement = (iNext * gliPerfFreq.QuadPart) / FREQUENCY_CONST; + gliPerfCountNext.QuadPart = gliPerfCount.QuadPart + giIncrement; iTimeLeft = iNext; timerDone = IsFastForwardMode(); tickTime = TRUE; @@ -265,20 +270,32 @@ void CALLBACK TimeProc( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2 } } - - static UINT32 MIN_TIMER(UINT32 timer, UINT32 other) { UINT32 value = TIME_MS_TO_US(timer); return ( value && value < other ? value : other ); } +// checks if the clock based on QueryPerformanceCounter is using sane values +static inline bool TimerSanityCheck() { + // quick and drity check for messed up gliPerfCountNext - if the high part is ahead by more than 2, something very bad is going on. + return !( gliPerfCountNext.HighPart > gliPerfCount.HighPart + 1L ); +} + // Returns the smallest time interval for a counter currently in use UINT32 GetNextCounterDoneTime(void) { QueryPerformanceCounter(&gliPerfCount); - INT32 time = (INT32)(((gliPerfCountNext.QuadPart - gliPerfCount.QuadPart) * FREQUENCY_CONST) / gliPerfFreq.QuadPart); - return (UINT32)((time > 0) ? time : 0); + + if (TimerSanityCheck() == false) { + // timer has gone off the rails, try to reset and hope for the best. + gliPerfCountNext.QuadPart = gliPerfCount.QuadPart + 25; + } + + gliTimestampDiff = gliPerfCountNext.QuadPart - gliPerfCount.QuadPart; + gliWaitTime = (gliTimestampDiff * FREQUENCY_CONST) / gliPerfFreq.QuadPart; + + return (UINT32)((gliWaitTime > 0) ? gliWaitTime : 0); } // Function to test if there are any outstanding timers. Used in fast forward routines @@ -301,18 +318,28 @@ DWORD WINAPI JA2ClockThread( LPVOID lpParam ) YieldProcessor(); // Sleep for a couple of milliseconds if not in fast forward mode - if (!IsFastForwardMode()) - Sleep( TIME_US_TO_MS( GetNextCounterDoneTime() ) ); + if (!IsFastForwardMode()) { + + giSleepTime = TIME_US_TO_MS(GetNextCounterDoneTime()); + + // monitor the returned sleep times, if we try to sleep for more than 2 secs then somehing must be very wrong. + if (giSleepTime > 2000) { + giSleepTime = 250; + } + + Sleep(giSleepTime); + } } } __except( EXCEPTION_EXECUTE_HANDLER ) { // Unhandled exception just exit + __debugbreak(); } return 0L; } -DWORD WINAPI JA2NotifyThread( LPVOID lpParam ) +DWORD WINAPI JA2NotifyThread( LPVOID lpParam ) { HANDLE waitHandles[] = {ghClockThreadShutdown, ghNotifyThreadEvent}; for(;;) @@ -337,6 +364,8 @@ DWORD WINAPI JA2NotifyThread( LPVOID lpParam ) else { // unexpected failure + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "JA2NotifyThread failed!"); + __debugbreak(); } } SetEvent(ghNotifyThreadShutdownComplete); @@ -451,6 +480,7 @@ UINT32 InitializeJA2TimerCallback( UINT32 uiDelay, LPTIMECALLBACK TimerProc, UIN if ( mmResult != TIMERR_NOERROR ) { + __debugbreak(); DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not get timer properties"); } @@ -459,6 +489,7 @@ UINT32 InitializeJA2TimerCallback( UINT32 uiDelay, LPTIMECALLBACK TimerProc, UIN if ( !TimerID ) { + __debugbreak(); DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not create timer callback"); } @@ -668,7 +699,10 @@ static void InnerTimerNotify(INT32 timer) (*itr).callback( timer, (*itr).state ); } } - catch (...) {} + catch (...) { + __debugbreak(); + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "InnerTimerNotify Failed!"); + } } static void BroadcastTimerNotify(INT32 timer) { @@ -677,7 +711,10 @@ static void BroadcastTimerNotify(INT32 timer) { __try { InnerTimerNotify(timer); } __except( EXCEPTION_EXECUTE_HANDLER ) - { /* Not sure. exit? */ } + { /* Not sure. exit? */ + __debugbreak(); + DebugMsg(TOPIC_JA2, DBG_LEVEL_3, "BroadcastTimerNotify Failed!"); + } } __finally { @@ -792,6 +829,7 @@ void UpdateTimer() gTimerID = timeSetEvent( uiTimeSlice, uiTimeSlice, TimeProc, (DWORD)0, TIME_PERIODIC ); if ( !gTimerID ) { + __debugbreak(); DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Could not create timer callback"); } } diff --git a/Utils/popup_class.cpp b/Utils/popup_class.cpp index a25418bb..a2a2cca3 100644 --- a/Utils/popup_class.cpp +++ b/Utils/popup_class.cpp @@ -211,7 +211,7 @@ BOOLEAN POPUP_OPTION::runHoverCallback(MOUSE_REGION * pRegion) BOOLEAN POPUP_OPTION::run() { - // check if it's aaliable at this time + // check if it's available at this time if ( this->action != 0 && this->checkAvailability() ) { this->action->call(); @@ -1559,10 +1559,13 @@ void POPUP::HandleShadingOfLines( void ) // shade strings past the this->subPopupOptionCount offset for(std::vector::iterator cOption=this->options.begin(); cOption != this->options.end(); ++cOption) { - if ( (*cOption)->checkAvailability() ) - UnShadeStringInBox( this->boxId, i+this->subPopupOptionCount); - else - ShadeStringInBox( this->boxId, i+this->subPopupOptionCount); + if ((*cOption)->checkAvailability()) { + UnShadeStringInBox(this->boxId, i + this->subPopupOptionCount); + } + else { + SetStringShade((*cOption)->stringHandle, (*cOption)->color_shade); // handle checkAvailability() changing string shade color + ShadeStringInBox(this->boxId, i + this->subPopupOptionCount); + } i++; }