diff --git a/GameVersion.cpp b/GameVersion.cpp index 353c05a3..8034ad55 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.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8440 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8440 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8440 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8440 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8440 (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.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8440 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8440 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8434 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8440 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 17.07.29" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 17.08.06" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Strategic/Map Screen Interface Map Inventory.cpp b/Strategic/Map Screen Interface Map Inventory.cpp index 31b4e351..18d3e58f 100644 --- a/Strategic/Map Screen Interface Map Inventory.cpp +++ b/Strategic/Map Screen Interface Map Inventory.cpp @@ -199,7 +199,7 @@ UINT32 guiMapInvenSortButton[4]; UINT32 guiMapInvenFilterButtonImage[MAP_INVENTORY_FILTER_BUTTONS]; UINT32 guiMapInvenFilterButton[MAP_INVENTORY_FILTER_BUTTONS]; - +BOOLEAN guiMapInvenFilterButtonDefined[MAP_INVENTORY_FILTER_BUTTONS]; BOOLEAN gfCheckForCursorOverMapSectorInventoryItem = FALSE; @@ -286,6 +286,11 @@ void MapInventoryPoolEjectAmmoBtn( GUI_BUTTON *btn, INT32 reason ); // HEADROCK HAM 5: Preliminary Filter Button void MapInventoryPoolFilterBtn( GUI_BUTTON *btn, INT32 reason ); void MapInventoryPoolFilterBtnMoveItemDisplay( GUI_BUTTON *btn, INT32 reason ); // Flugente + +// Flugente: gear templates +void MapInventoryWriteEquipmentTemplate(GUI_BUTTON *btn, INT32 reason); +void MapInventoryReadEquipmentTemplate(GUI_BUTTON *btn, INT32 reason); + void DisplayCurrentSector( void ); void ResizeInventoryList( void ); #ifdef INVFIX_Moa//dnl ch85 050214 @@ -821,7 +826,8 @@ void UpdateHelpTextForInvnentoryStashSlots( void ) return; } - +// Flugente: +extern void EquipmentListMenuCancel( ); // create and remove buttons for inventory void CreateDestroyMapInventoryPoolButtons( BOOLEAN fExitFromMapScreen ) @@ -1861,6 +1867,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 0 ] ]->UserData[1] = IC_MAPFILTER_ALL; ButtonList[ guiMapInvenFilterButton[ 0 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 0 ] ]->UserData[3] = 0; + guiMapInvenFilterButtonDefined[0] = TRUE; guiMapInvenFilterButtonImage[ 1 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 20, 20, -1, 21, -1 ); guiMapInvenFilterButton[ 1 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 1 ], INVEN_POOL_X+214 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, @@ -1872,6 +1879,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 1 ] ]->UserData[1] = IC_MAPFILTER_GUN; ButtonList[ guiMapInvenFilterButton[ 1 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 1 ] ]->UserData[3] = IC_MAPFILTER_GUN; + guiMapInvenFilterButtonDefined[1] = TRUE; guiMapInvenFilterButtonImage[ 2 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 22, 22, -1, 23, -1 ); guiMapInvenFilterButton[ 2 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 2 ], INVEN_POOL_X+214 + xResOffset, INVEN_POOL_Y + 24 + yResOffset, @@ -1883,6 +1891,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 2 ] ]->UserData[1] = IC_MAPFILTER_AMMO; ButtonList[ guiMapInvenFilterButton[ 2 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 2 ] ]->UserData[3] = IC_MAPFILTER_AMMO; + guiMapInvenFilterButtonDefined[2] = TRUE; guiMapInvenFilterButtonImage[ 3 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 24, 24, -1, 25, -1 ); guiMapInvenFilterButton[ 3 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 3 ], INVEN_POOL_X+243 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, @@ -1894,6 +1903,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 3 ] ]->UserData[1] = IC_MAPFILTER_EXPLOSV; ButtonList[ guiMapInvenFilterButton[ 3 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 3 ] ]->UserData[3] = IC_MAPFILTER_EXPLOSV; + guiMapInvenFilterButtonDefined[3] = TRUE; guiMapInvenFilterButtonImage[ 4 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 26, 26, -1, 27, -1 ); guiMapInvenFilterButton[ 4 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 4 ], INVEN_POOL_X+243 + xResOffset, INVEN_POOL_Y + 24 + yResOffset, @@ -1905,6 +1915,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 4 ] ]->UserData[1] = IC_MAPFILTER_MELEE; ButtonList[ guiMapInvenFilterButton[ 4 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 4 ] ]->UserData[3] = IC_MAPFILTER_MELEE; + guiMapInvenFilterButtonDefined[4] = TRUE; guiMapInvenFilterButtonImage[ 5 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 28, 28, -1, 29, -1 ); guiMapInvenFilterButton[ 5 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 5 ], INVEN_POOL_X+272 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, @@ -1916,6 +1927,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 5 ] ]->UserData[1] = IC_MAPFILTER_ARMOR; ButtonList[ guiMapInvenFilterButton[ 5 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 5 ] ]->UserData[3] = IC_MAPFILTER_ARMOR; + guiMapInvenFilterButtonDefined[5] = TRUE; guiMapInvenFilterButtonImage[ 6 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 30, 30, -1, 31, -1 ); guiMapInvenFilterButton[ 6 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 6 ], INVEN_POOL_X+272 + xResOffset, INVEN_POOL_Y + 24 + yResOffset, @@ -1927,6 +1939,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 6 ] ]->UserData[1] = IC_MAPFILTER_LBE; ButtonList[ guiMapInvenFilterButton[ 6 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 6 ] ]->UserData[3] = IC_MAPFILTER_LBE; + guiMapInvenFilterButtonDefined[6] = TRUE; guiMapInvenFilterButtonImage[ 7 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 32, 32, -1, 33, -1 ); guiMapInvenFilterButton[ 7 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 7 ], INVEN_POOL_X+301 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, @@ -1938,6 +1951,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 7 ] ]->UserData[1] = IC_MAPFILTER_KIT; ButtonList[ guiMapInvenFilterButton[ 7 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 7 ] ]->UserData[3] = IC_MAPFILTER_KIT; + guiMapInvenFilterButtonDefined[7] = TRUE; guiMapInvenFilterButtonImage[ 8 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 34, 34, -1, 35, -1 ); guiMapInvenFilterButton[ 8 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 8 ], INVEN_POOL_X+301 + xResOffset, INVEN_POOL_Y + 24 + yResOffset, @@ -1949,6 +1963,7 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 8 ] ]->UserData[1] = IC_MAPFILTER_MISC; ButtonList[ guiMapInvenFilterButton[ 8 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 8 ] ]->UserData[3] = IC_MAPFILTER_MISC; + guiMapInvenFilterButtonDefined[8] = TRUE; // Flugente: toggle button for move item display guiMapInvenFilterButtonImage[ 9 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 36, 36, -1, 37, -1 ); @@ -1963,24 +1978,33 @@ void CreateMapInventoryButtons( void ) ButtonList[ guiMapInvenFilterButton[ 9 ] ]->UserData[1] = IC_MAPFILTER_MISC; ButtonList[ guiMapInvenFilterButton[ 9 ] ]->UserData[2] = 0; ButtonList[ guiMapInvenFilterButton[ 9 ] ]->UserData[3] = IC_MAPFILTER_MISC; + guiMapInvenFilterButtonDefined[9] = TRUE; - /* - guiMapInvenFilterButtonImage[ 9 ]= LoadButtonImage( "INTERFACE\\sector_inventory_buttons.sti" , 19, 17, -1, 18, -1 ); - guiMapInvenFilterButton[ 9 ] = QuickCreateButton( guiMapInvenFilterButtonImage[ 9 ], INVEN_POOL_X+340 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, - BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, - NULL, (GUI_CALLBACK)MapInventoryPoolFilterBtn ); + // as no space is left, further buttons require better resolutions + if ( iResolution > _640x480 ) + { + // Flugente: gear templates + guiMapInvenFilterButtonImage[10] = LoadButtonImage("INTERFACE\\sector_inventory_buttons.sti", 40, 40, -1, 40, -1); + guiMapInvenFilterButton[10] = QuickCreateButton(guiMapInvenFilterButtonImage[10], INVEN_POOL_X + 371 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + NULL, (GUI_CALLBACK)MapInventoryWriteEquipmentTemplate); + + SetButtonFastHelpText(guiMapInvenFilterButton[10], pMapScreenInvenButtonHelpText[18]); + guiMapInvenFilterButtonDefined[10] = TRUE; - SetButtonFastHelpText( guiMapInvenFilterButton[ 9 ], pMapScreenInvenButtonHelpText[ 17 ] ); - ButtonList[ guiMapInvenFilterButton[ 9 ] ]->UserData[0] = 0; - ButtonList[ guiMapInvenFilterButton[ 9 ] ]->UserData[1] = 0; - */ + guiMapInvenFilterButtonImage[11] = LoadButtonImage("INTERFACE\\sector_inventory_buttons.sti", 42, 42, -1, 42, -1); + guiMapInvenFilterButton[11] = QuickCreateButton(guiMapInvenFilterButtonImage[11], INVEN_POOL_X + 406 + xResOffset, INVEN_POOL_Y + 10 + yResOffset, + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + NULL, (GUI_CALLBACK)MapInventoryReadEquipmentTemplate); + + SetButtonFastHelpText(guiMapInvenFilterButton[11], pMapScreenInvenButtonHelpText[19]); + guiMapInvenFilterButtonDefined[11] = TRUE; + } //reset the current inventory page to be the first page iCurrentInventoryPoolPage = 0; HandleSetFilterButtons(); - - return; } @@ -2010,12 +2034,19 @@ void DestroyMapInventoryButtons( void ) UnloadButtonImage( guiMapInvenSortButtonImage[ 3 ] ); // HEADROCK HAM 5: Filter button - for (INT32 iCounter = 0; iCounter < MAP_INVENTORY_FILTER_BUTTONS; iCounter++) + for (INT32 iCounter = 0; iCounter < MAP_INVENTORY_FILTER_BUTTONS; ++iCounter) { - RemoveButton( guiMapInvenFilterButton[ iCounter ] ); - UnloadButtonImage( guiMapInvenFilterButtonImage[ iCounter ] ); + if ( guiMapInvenFilterButtonDefined[ iCounter ] ) + { + RemoveButton( guiMapInvenFilterButton[ iCounter ] ); + UnloadButtonImage( guiMapInvenFilterButtonImage[ iCounter ] ); + guiMapInvenFilterButtonDefined[ iCounter ] = FALSE; + } } + // Flugente: destroy gear template selection, in case that thing is still open + EquipmentListMenuCancel( ); + /* // Destroy the popup. delete(gMapInventoryFilterPopup); @@ -2023,8 +2054,6 @@ void DestroyMapInventoryButtons( void ) gfMapInventoryFilterPopupInitialized = FALSE; gfMapInventoryFilterPopupVisible = FALSE; */ - - return; } //////////////////////////// @@ -2937,28 +2966,99 @@ void MapInventoryPoolFilterBtnMoveItemDisplay( GUI_BUTTON *btn, INT32 reason ) // The refresh function moves the necessary items from the Seen to the Unseen inventories, and vice versa. RefreshSeenAndUnseenPools(); - BlitInventoryPoolGraphic( ); + BlitInventoryPoolGraphic(); HandleSetFilterButtons(); } } - /*if (reason & MSYS_CALLBACK_REASON_RBUTTON_UP) +} + +// Flugente: gear templates +extern void WriteEquipmentTemplate(SOLDIERTYPE* pSoldier, STR16 name); +extern void EquipmentListMenu( ); + +void TemplateNameInputCallBack(UINT8 ubResult) +{ + if (ubResult == MSG_BOX_RETURN_OK && wcscmp(gszMsgBoxInputString, L"") > 0) { - if (btn->uiFlags & (BUTTON_CLICKED_ON)) + SOLDIERTYPE* pSoldier = &Menptr[gCharactersList[bSelectedInfoChar].usSolID]; + if (pSoldier) { - if (btn->UserData[2] == 0) + WriteEquipmentTemplate(pSoldier, gszMsgBoxInputString); + + // The refresh function moves the necessary items from the Seen to the Unseen inventories, and vice versa. + RefreshSeenAndUnseenPools(); + + BlitInventoryPoolGraphic(); + + HandleSetFilterButtons(); + } + } + memset(gszMsgBoxInputString, 0, sizeof(gszMsgBoxInputString)); +} + +void MapInventoryWriteEquipmentTemplate(GUI_BUTTON *btn, INT32 reason) +{ + if ( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN || + reason & MSYS_CALLBACK_REASON_RBUTTON_DWN ) + { + if ( !(btn->uiFlags & (BUTTON_CLICKED_ON)) ) + { + // Set as "clicked on", but do nothing until the mouse is released. + btn->uiFlags |= (BUTTON_CLICKED_ON); + } + } + + if ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + if ( btn->uiFlags & (BUTTON_CLICKED_ON) ) + { + SOLDIERTYPE* pSoldier = &Menptr[gCharactersList[bSelectedInfoChar].usSolID]; + if ( pSoldier ) { - MapInventoryFilterSet( btn->UserData[3] ); + DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, szGearTemplateText[0], guiCurrentScreen, MSG_BOX_FLAG_INPUTBOX, TemplateNameInputCallBack, NULL ); + } + } + } +} + +void MapInventoryReadEquipmentTemplate(GUI_BUTTON *btn, INT32 reason) +{ + if ( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN || + reason & MSYS_CALLBACK_REASON_RBUTTON_DWN ) + { + if ( !(btn->uiFlags & (BUTTON_CLICKED_ON)) ) + { + // Set as "clicked on", but do nothing until the mouse is released. + btn->uiFlags |= (BUTTON_CLICKED_ON); + } + } + + if ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + if ( btn->uiFlags & (BUTTON_CLICKED_ON) ) + { + SOLDIERTYPE* pSoldier = &Menptr[gCharactersList[bSelectedInfoChar].usSolID]; + if ( pSoldier && + pSoldier->sSectorX == sSelMapX && pSoldier->sSectorY == sSelMapY && pSoldier->bSectorZ == iCurrentMapSectorZ && + !pSoldier->flags.fBetweenSectors ) + { + if ( (gTacticalStatus.uiFlags & INCOMBAT || gTacticalStatus.fEnemyInSector) ) + { + DoScreenIndependantMessageBox( szGearTemplateText[1], MSG_BOX_FLAG_OK, NULL ); + } + else + { + // open the menu + EquipmentListMenu( ); + } } else { - MapInventoryFilterToggle( btn->UserData[3] ); + DoScreenIndependantMessageBox( szGearTemplateText[2], MSG_BOX_FLAG_OK, NULL ); } - - // HEADROCK HAM 5: Disabled for now, as we've got buttons for this. - //CreateMapInventoryFilterMenu( ); } - }*/ + } } // HEADROCK HAM 5: Zoom button. @@ -5138,7 +5238,11 @@ void SortSectorInventorySeparateAttachments() while ((size = pInventoryItem->object[x]->attachments.size()) != cnt) { OBJECTTYPE * pNewObj = new OBJECTTYPE(); - gpTempObject = pInventoryItem->object[x]->GetAttachmentAtIndex(size - 1 - cnt); + + // Flugente: prevent an underflow + UINT8 attindex = size > cnt ? size - 1 - cnt : 0; + + gpTempObject = pInventoryItem->object[x]->GetAttachmentAtIndex( attindex ); //WarmSteel - This actually still works with NAS, be it by accident if (gpTempObject != NULL && pInventoryItem->object.RemoveAttachment(gpTempObject, pNewObj, x)) @@ -5153,13 +5257,13 @@ void SortSectorInventorySeparateAttachments() } else { - cnt++; + ++cnt; } - uiLoopCnt++; + ++uiLoopCnt; // Failsafe - if (uiLoopCnt > 100) + if (uiLoopCnt > 50) { break; } @@ -5262,6 +5366,83 @@ void SortSectorInventoryStackAndMerge(bool ammoOnly ) fMapPanelDirty = TRUE; } +// Flugente: on a key press, we loop over each team members' inventory and exchange it with world items that have a higher status +// however, we ignore items that have inseparable attachments +// the goal is to simulate the player manually changing items, as that is rather tedious +BOOL GetBetterObject_InventoryPool(UINT16 usItem, INT16 status, UINT32& arLoop, UINT8& arIndex) +{ + OBJECTTYPE* pBestObj = NULL; + + INT16 beststatus = status; + BOOLEAN found = FALSE; + + for (UINT32 uiLoop = 0; uiLoop < pInventoryPoolList.size(); ++uiLoop) // ... for all items in the world ... + { + if (pInventoryPoolList[uiLoop].object.exists() && + pInventoryPoolList[uiLoop].usFlags & WORLD_ITEM_REACHABLE && + !(pInventoryPoolList[uiLoop].usFlags & WORLD_ITEM_ARMED_BOMB) && + pInventoryPoolList[uiLoop].bVisible == VISIBLE && + pInventoryPoolList[uiLoop].object.usItem == usItem) + { + OBJECTTYPE* pObj = &(pInventoryPoolList[uiLoop].object); + + if (pObj != NULL ) + { + for (UINT8 i = 0; i < pObj->ubNumberOfObjects; ++i) + { + if ((*pObj)[i]->data.objectStatus > beststatus) + { + // ignore if there are inseparable attachments - we wouldn't be able to change those by hand + attachmentList::iterator iterend = (*pObj)[i]->attachments.end(); + for (attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != iterend; ++iter) + { + if (iter->exists() && Item[iter->usItem].inseparable) + { + continue; + } + } + + beststatus = (*pObj)[i]->data.objectStatus; + arLoop = uiLoop; + arIndex = i; + found = TRUE; + } + } + } + } + } + + return found; +} + +// Flugente: tell us the position of the first inventory item with a fitting calibre and ammotype +BOOL GetFittingAmmo_InventoryPool( UINT8 usCalibre, UINT8 usAmmoType, UINT32& arLoop ) +{ + for ( UINT32 uiLoop = 0; uiLoop < pInventoryPoolList.size( ); ++uiLoop ) + { + if ( pInventoryPoolList[uiLoop].object.exists( ) && + pInventoryPoolList[uiLoop].usFlags & WORLD_ITEM_REACHABLE && + !(pInventoryPoolList[uiLoop].usFlags & WORLD_ITEM_ARMED_BOMB) && + pInventoryPoolList[uiLoop].bVisible == VISIBLE && + (Item[pInventoryPoolList[uiLoop].object.usItem].usItemClass & IC_AMMO) ) + { + UINT16 usMagIndex = Item[pInventoryPoolList[uiLoop].object.usItem].ubClassIndex; + + if ( usCalibre == Magazine[usMagIndex].ubCalibre ) + { + if ( usAmmoType == Magazine[usMagIndex].ubAmmoType ) + { + arLoop = uiLoop; + + return TRUE; + } + } + } + } + + return FALSE; +} + // HEADROCK HAM 5: This is a helper function that puts together all seen and unseen items from the two // sector-inventory pools, and then rebuilds the pool. By changing the flags used by the function that tests // whether an item should appear in the "SEEN" list, we can create filters to show or hide specific items. @@ -5767,96 +5948,126 @@ void MapInventoryFilterSet( UINT32 uiFlags ) void HandleSetFilterButtons() { // Show/Hide All button always off. - ButtonList[guiMapInvenFilterButton[ 0 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if ( guiMapInvenFilterButtonDefined[0] ) + { + ButtonList[guiMapInvenFilterButton[ 0 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } // Guns - if (guiMapInventoryFilter & IC_MAPFILTER_GUN) + if ( guiMapInvenFilterButtonDefined[1] ) { - ButtonList[guiMapInvenFilterButton[ 1 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 1 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_GUN) + { + ButtonList[guiMapInvenFilterButton[ 1 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 1 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Ammo - if (guiMapInventoryFilter & IC_MAPFILTER_AMMO) + if ( guiMapInvenFilterButtonDefined[2] ) { - ButtonList[guiMapInvenFilterButton[ 2 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 2 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_AMMO) + { + ButtonList[guiMapInvenFilterButton[ 2 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 2 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Explosives - if (guiMapInventoryFilter & IC_MAPFILTER_EXPLOSV) + if ( guiMapInvenFilterButtonDefined[3] ) { - ButtonList[guiMapInvenFilterButton[ 3 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 3 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_EXPLOSV) + { + ButtonList[guiMapInvenFilterButton[ 3 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 3 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Melee Weapons - if (guiMapInventoryFilter & IC_MAPFILTER_MELEE) - { - ButtonList[guiMapInvenFilterButton[ 4 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 4 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if ( guiMapInvenFilterButtonDefined[4] ) + { + if (guiMapInventoryFilter & IC_MAPFILTER_MELEE) + { + ButtonList[guiMapInvenFilterButton[ 4 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 4 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Armor - if (guiMapInventoryFilter & IC_MAPFILTER_ARMOR) + if ( guiMapInvenFilterButtonDefined[5] ) { - ButtonList[guiMapInvenFilterButton[ 5 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 5 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_ARMOR) + { + ButtonList[guiMapInvenFilterButton[ 5 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 5 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // LBEs - if (guiMapInventoryFilter & IC_MAPFILTER_LBE) + if ( guiMapInvenFilterButtonDefined[6] ) { - ButtonList[guiMapInvenFilterButton[ 6 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 6 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_LBE) + { + ButtonList[guiMapInvenFilterButton[ 6 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 6 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Kits - if (guiMapInventoryFilter & IC_MAPFILTER_KIT) + if ( guiMapInvenFilterButtonDefined[7] ) { - ButtonList[guiMapInvenFilterButton[ 7 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 7 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_KIT) + { + ButtonList[guiMapInvenFilterButton[ 7 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 7 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Misc. Items - if (guiMapInventoryFilter & IC_MAPFILTER_MISC) + if ( guiMapInvenFilterButtonDefined[8] ) { - ButtonList[guiMapInvenFilterButton[ 8 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 8 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if (guiMapInventoryFilter & IC_MAPFILTER_MISC) + { + ButtonList[guiMapInvenFilterButton[ 8 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 8 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } // Flugente: move item display - if ( IsShowMoveItem() ) + if ( guiMapInvenFilterButtonDefined[9] ) { - ButtonList[guiMapInvenFilterButton[ 9 ]]->uiFlags |= (BUTTON_CLICKED_ON); - } - else - { - ButtonList[guiMapInvenFilterButton[ 9 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + if ( IsShowMoveItem() ) + { + ButtonList[guiMapInvenFilterButton[ 9 ]]->uiFlags |= (BUTTON_CLICKED_ON); + } + else + { + ButtonList[guiMapInvenFilterButton[ 9 ]]->uiFlags &=~ (BUTTON_CLICKED_ON); + } } } diff --git a/Strategic/Map Screen Interface Map Inventory.h b/Strategic/Map Screen Interface Map Inventory.h index 36aa6806..14580cf4 100644 --- a/Strategic/Map Screen Interface Map Inventory.h +++ b/Strategic/Map Screen Interface Map Inventory.h @@ -8,7 +8,7 @@ #define MAX_DISTANCE_TO_PICKUP_ITEM 5 // Flugente: number of map inventory filter buttons -#define MAP_INVENTORY_FILTER_BUTTONS 10 // Flugente: 9 -> 10 +#define MAP_INVENTORY_FILTER_BUTTONS 12 // Flugente: 9 -> 12 // number of inventory slots //#define MAP_INVENTORY_POOL_SLOT_COUNT 84 //45 //45 diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 10f66227..9a0025cf 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -78,6 +78,9 @@ #include "Game Clock.h" // added by Flugente #include // added by Flugente for vfs-stuff #include "DynamicDialogue.h" // added by Flugente for SoldierRelation() + #include "MessageBoxScreen.h" // added by Flugente + #include "Map Screen Interface.h" // added by Flugente + #include "Map Screen Interface Map.h" // added by Flugente #endif #ifdef JA2UB @@ -92,6 +95,18 @@ // directory for fortification plans (located in Profiles sub-folder) #define FORTIFICATIONPLAN_DIRECTORY "FortificationPlan\\" +#define EQUIPMENTTEMPLATE_DIRECTORY "GearTemplate\\" + +extern BOOL GetBetterObject_InventoryPool( UINT16 usItem, INT16 status, UINT32& arLoop, UINT8& arIndex ); +extern BOOL GetFittingAmmo_InventoryPool( UINT8 usCalibre, UINT8 usAmmoType, UINT32& arLoop ); + +extern BOOLEAN HandleNailsVestFetish( SOLDIERTYPE *pSoldier, UINT32 uiHandPos, UINT16 usReplaceItem ); + +extern std::vector pInventoryPoolList; +extern INT32 GetAttachmentInfoIndex( UINT16 usItem ); + +extern INT32 giItemDescAmmoButton; + ITEM_POOL_LOCATOR FlashItemSlots[ NUM_ITEM_FLASH_SLOTS ]; UINT32 guiNumFlashItemSlots = 0; // sevenfm: remember network settings for last planted tripwire @@ -8970,3 +8985,769 @@ BOOLEAN SpendMoney( SOLDIERTYPE *pSoldier, UINT32 aAmount ) return (aAmount == 0); } + +// Flugente: gear templates +extern std::vector gTemplateVector; + +// this class is used to store our gear templates +class GEAR_NODE +{ +public: + GEAR_NODE(INT8 a, UINT16 b, UINT16 c) : + slot( a ), item( b ), ammoitem(c){} + + GEAR_NODE() : + slot( NOWHERE ), item( 0 ), ammoitem(0) {} + + void swap(GEAR_NODE& other) + { + std::swap( slot, other.slot ); + std::swap( item, other.item ); + std::swap( ammoitem, other.ammoitem ); + + std::vector tmp = other.attachments; + other.attachments = attachments; + attachments = tmp; + } + + INT8 slot; + UINT16 item; + UINT16 ammoitem; + + std::vector attachments; +}; + +std::ostream& operator<<(std::ostream& stream, GEAR_NODE const& data) +{ + stream << (int)(data.slot) << " " << (UINT16)(data.item) << " " << (UINT16)(data.ammoitem); + + std::vector tmp = data.attachments; + + for ( std::vector::iterator it = tmp.begin( ); it != tmp.end( ); ++it ) + stream << " " << (*it); + + stream << std::endl; + + return stream; +} + +std::vector LoadEquipmentTemplate( std::string aName ) +{ + CHAR8 filename[MAX_PATH]; + + sprintf(filename, "%s%s", EQUIPMENTTEMPLATE_DIRECTORY, aName.c_str()); + + // get full path to save file + vfs::Path vfsPath; + vfs::COpenWriteFile rfile( filename, true ); + rfile->_getRealPath(vfsPath); + std::string str = vfsPath.to_string(); + + const char* filenamewithpath = str.c_str(); + + // Read data + std::ifstream file; + file.open(filenamewithpath, std::ios::in); + std::vector vec; + + std::string line; + + while ( std::getline( file >> std::ws, line ) ) + { + if ( line[0] != '#' && line[0] != '/' ) + { + std::istringstream iss( line ); + + GEAR_NODE node; + + int num; + if ( iss >> num && iss >> (UINT16)node.item ) + { + node.slot = num; + + // additional data is optional + if ( iss >> (UINT16)node.ammoitem ) + { + while ( iss >> num ) + { + node.attachments.push_back( num ); + } + } + + vec.push_back(node); + } + } + } + + file.close(); + + return vec; +} + +void SaveEquipmentTemplate(std::vector aVec, STR16 aName) +{ + CHAR8 filename[MAX_PATH]; + + sprintf(filename, "%s%S.txt", EQUIPMENTTEMPLATE_DIRECTORY, aName); + + // get full path to save file + // what we are doing here might seem rather odd. If the file does not exist, we are creating a new file via vfs... and then create a fresh binary file afterwards. + // I found this to be the easiest solution for using both vfs pathing and being able to use a std::fstream + // If you don't like it, fix it yourself, or make vfs not such a pain to use + vfs::Path vfsPath; + vfs::COpenWriteFile rfile(filename, true); + rfile->_getRealPath(vfsPath); + std::string str = vfsPath.to_string(); + + const char* filenamewithpath = str.c_str(); + + // write + std::fstream binary_file(filenamewithpath, std::ios::out); + + for (std::vector::iterator it = aVec.begin(); it != aVec.end(); ++it) + { + GEAR_NODE node = (*it); + + binary_file << node; + } + + binary_file.close(); +} + +void WriteEquipmentTemplate(SOLDIERTYPE* pSoldier, STR16 name) +{ + if (pSoldier) + { + std::vector vec; + + INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly + + for (INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... + { + if (pSoldier->inv[bLoop].exists()) + { + // if this item is a gun, note what magazine is currently loaded + UINT16 ammoitem = 0; + if ( Item[(pSoldier->inv[bLoop]).usItem].usItemClass & IC_GUN ) + ammoitem = (pSoldier->inv[bLoop])[0]->data.gun.usGunAmmoItem; + + GEAR_NODE node( bLoop, (pSoldier->inv[bLoop]).usItem, ammoitem ); + + // add all attachments + attachmentList::iterator iterend = (pSoldier->inv[bLoop])[0]->attachments.end( ); + for ( attachmentList::iterator iter = (pSoldier->inv[bLoop])[0]->attachments.begin( ); iter != iterend; ++iter ) + { + if ( iter->exists( ) ) + node.attachments.push_back( iter->usItem ); + } + + vec.push_back(node); + } + } + + SaveEquipmentTemplate(vec, name); + } +} + +std::vector get_all_files_names_within_folder(std::string folder) +{ + std::vector names; + std::string search_path = folder + "/*.*"; + WIN32_FIND_DATA fd; + HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd); + + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + // read all (real) files in current folder + // , delete '!' read other 2 default folder . and .. + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + names.push_back(fd.cFileName); + } + } + while (::FindNextFile(hFind, &fd)); + + ::FindClose(hFind); + } + return names; +} + +// get a vector of all tilesets that are allowed to be built in this sector (the above filtered by structure construct/deconstruct basically) +void GetEquipmentTemplates() +{ + CHAR8 filename[MAX_PATH]; + + sprintf(filename, "%sUpdate Gear.txt", EQUIPMENTTEMPLATE_DIRECTORY); + + // get full path to save file + // what we are doing here might seem rather odd. If the file does not exist, we are creating a new empty file via vfs + // in this case, this is rather useful, as the above file simply updates our gear, which is already neat + // I found this to be the easiest solution for using both vfs pathing and being able to use a std::fstream + // If you don't like it, fix it yourself, or make vfs not such a pain to use + vfs::Path vfsPath; + vfs::COpenWriteFile rfile( filename, true ); + rfile->_getRealPath(vfsPath); + + vfs::Path dir, file; + vfsPath.splitLast(dir, file); + + std::string str = dir.to_string(); + + const char* filenamewithpath = str.c_str(); + + gTemplateVector = get_all_files_names_within_folder( filenamewithpath ); +} + +void ReadEquipmentTable( SOLDIERTYPE* pSoldier, std::string name ) +{ + // make sure the merc is actually in this sector + if ( pSoldier ) + { + UINT16 color = FONT_MCOLOR_WHITE; + + // for safety, test again + if ( pSoldier->sSectorX != sSelMapX || + pSoldier->sSectorY != sSelMapY || + pSoldier->bSectorZ != iCurrentMapSectorZ ) + { + ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[3], pSoldier->GetName( ) ); + + return; + } + + if ( (gTacticalStatus.uiFlags & INCOMBAT || gTacticalStatus.fEnemyInSector) ) + { + ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[1] ); + + return; + } + + std::vector vec = LoadEquipmentTemplate(name); + + UINT16 usReloadSound = 0; + BOOLEAN attachmentsound = FALSE; + + // 1. loop over the gear we should pick up and remove mismatching items if we can find a fitting one in the sector + for (std::vector::iterator it = vec.begin(); it != vec.end(); ++it) + { + GEAR_NODE node = (*it); + + if ( node.slot >= 0 && node.slot < NUM_INV_SLOTS && node.item != NOTHING ) + { + // is there already an item here? + if (pSoldier->inv[node.slot].exists()) + { + if ( (pSoldier->inv[node.slot]).usItem != node.item ) + { + // can we get the requested item in the first place? + UINT32 poolslot = 0; + UINT8 index = 0; + if ( GetBetterObject_InventoryPool( node.item, 0, poolslot, index ) ) + { + // We have to check whether we have any LBE equipped that isn't in the template + // in that case we have to dump it and it's contents, otherwise it can block slots that are used in the template + if ( node.slot >= VESTPOCKPOS && node.slot <= BPACKPOCKPOS ) + { + std::vector pocketKey; + GetLBESlots( node.slot, pocketKey ); + + // remove everything inside LBE + for ( std::vector::iterator lbeslotit = pocketKey.begin( ); lbeslotit != pocketKey.end( ); ++lbeslotit ) + { + if ( pSoldier->inv[(*lbeslotit)].exists( ) ) + { + AutoPlaceObjectInInventoryStash( &pSoldier->inv[(*lbeslotit)], pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &pSoldier->inv[(*lbeslotit)] ); + } + } + } + // if we want to equip a two-handed item in our first hand, also drop whatever we have in the second hand + else if ( node.slot == HANDPOS && Item[node.item].twohanded && pSoldier->inv[SECONDHANDPOS].exists( ) ) + { + AutoPlaceObjectInInventoryStash( &pSoldier->inv[SECONDHANDPOS], pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &pSoldier->inv[SECONDHANDPOS] ); + } + // if we are Nails and are ordered to drop our vest, refuse and complain! + else if ( node.slot == VESTPOS && HandleNailsVestFetish( pSoldier, node.slot, NOTHING ) ) + continue; + + // drop item + AutoPlaceObjectInInventoryStash( &pSoldier->inv[node.slot], pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &pSoldier->inv[node.slot] ); + } + } + } + } + } + + // 2. loop over the gear again and pick up what fits + for ( std::vector::iterator it = vec.begin( ); it != vec.end( ); ++it ) + { + GEAR_NODE node = (*it); + + if ( node.slot >= 0 && node.slot < NUM_INV_SLOTS ) + { + // only if slot is still empty... + if ( !pSoldier->inv[node.slot].exists( ) && node.item != NOTHING ) + { + // see if we can find a better object in this sector and put it into our slot + UINT32 poolslot = 0; + UINT8 index = 0; + if ( GetBetterObject_InventoryPool( node.item, 0, poolslot, index ) ) + { + (pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer ); + + if ( !PlaceObject( pSoldier, node.slot, &gItemPointer ) ) + { + ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[4], pSoldier->GetName( ), Item[node.item].szItemName ); + + AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &gItemPointer ); + } + } + else + { + // if this is ammo, we might still be able to create a mag from different-sized mags + if ( Item[node.item].usItemClass & IC_AMMO ) + { + UINT16 usMagIndex = Item[node.item].ubClassIndex; + + UINT16 neededammo = Magazine[usMagIndex].ubMagSize; + + if ( neededammo > 0 ) + { + UINT32 poolslot = 0; + if ( GetFittingAmmo_InventoryPool( Magazine[usMagIndex].ubCalibre, Magazine[usMagIndex].ubAmmoType, poolslot ) ) + { + UINT32 takeammo = min( neededammo, pInventoryPoolList[poolslot].object[pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1]->data.ubShotsLeft ); + + pInventoryPoolList[poolslot].object[pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1]->data.ubShotsLeft -= takeammo; + + if ( !pInventoryPoolList[poolslot].object[pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1]->data.ubShotsLeft ) + { + pInventoryPoolList[poolslot].object.RemoveObjectAtIndex( pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1 ); + } + + CreateAmmo( node.item, &gItemPointer, takeammo ); + + if ( !PlaceObject( pSoldier, node.slot, &gItemPointer ) ) + { + ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[4], pSoldier->GetName( ), Item[node.item].szItemName ); + + AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &gItemPointer ); + } + } + } + } + + //if ( !pSoldier->inv[node.slot].exists( ) ) + //ScreenMsg( color, MSG_INTERFACE, L"%s found no %s to equip.", pSoldier->GetName( ), Item[node.item].szItemName ); + } + } + + // if this slot is a LBE slot, we need to add attachments here. Otherwise we add the attachments after we fail to pick up other items as their LBE slots won't exist + if ( pSoldier->inv[node.slot].exists() && node.slot >= VESTPOCKPOS && node.slot <= BPACKPOCKPOS ) + { + // try to add attachments if nod entry fits the item, or if the node item is NOTHING (essentially a wildcard for this) + if ( !node.attachments.empty( ) && (pSoldier->inv[node.slot]).usItem == node.item || NOTHING == node.item ) + { + OBJECTTYPE* pObj = &(pSoldier->inv[node.slot]); + + for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i ) + { + for ( std::vector::iterator attit = node.attachments.begin( ); attit != node.attachments.end( ); ++attit ) + { + UINT16 item_attachment = (*attit); + + // only apply attachments if they don't require any checks! + INT32 bAttachInfoIndex = GetAttachmentInfoIndex( item_attachment ); + + if ( bAttachInfoIndex < 0 || AttachmentInfo[bAttachInfoIndex].bAttachmentSkillCheck == NO_CHECK ) + { + UINT32 poolslot = 0; + UINT8 index = 0; + if ( GetBetterObject_InventoryPool( item_attachment, 0, poolslot, index ) ) + { + (pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer ); + + BOOLEAN isAttachedNow = pObj->AttachObject( pSoldier, &gItemPointer, FALSE, i ); //do the actual attaching + + if ( !isAttachedNow ) + { + attachmentsound = TRUE; + + //ScreenMsg( color, MSG_INTERFACE, L"%s could not attach %s to %s", pSoldier->GetName( ), Item[item_attachment].szItemName, Item[pObj->usItem].szItemName ); + + AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &gItemPointer ); + } + } + } + } + } + } + } + } + } + + // 3. loop over all slots (not just those of the gear vector), improve, merge and fill stacks + for ( int slot = HELMETPOS; slot < NUM_INV_SLOTS; ++slot ) + { + if (pSoldier->inv[slot].exists()) + { + OBJECTTYPE* pObj = &(pSoldier->inv[slot]); + + if ( Item[pObj->usItem].usItemClass & (IC_KIT | IC_MEDKIT) && pObj->ubNumberOfObjects > 1 ) + { + CleanUpStack( pObj, pObj ); + } + + // if this is ammo, try merging mags first before replacing them, the not-full mag being the visible one + // by doing this we won't have so many used magazines lying around + if ( (Item[pObj->usItem].usItemClass & IC_AMMO) && pObj->ubNumberOfObjects > 1 ) + { + UINT16 usMagIndex = Item[pObj->usItem].ubClassIndex; + UINT16 magsize = Magazine[usMagIndex].ubMagSize; + + if ( magsize ) + { + UINT32 ammocount = 0; + for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i ) + ammocount += (*pObj)[i]->data.ubShotsLeft; + + UINT8 fullmags = ammocount / magsize; + + UINT16 leftover = ammocount - fullmags * magsize; + + UINT8 totalmags = fullmags + (leftover > 0 ? 1 : 0); + + if ( totalmags < pObj->ubNumberOfObjects ) + pObj->RemoveObjectsFromStack( pObj->ubNumberOfObjects - totalmags ); + + for ( INT16 i = 0; i < totalmags; ++i ) + (*pObj)[i]->data.ubShotsLeft = magsize; + + if ( leftover > 0 ) + (*pObj)[0]->data.ubShotsLeft = leftover; + } + } + + // for every object in the stack, try to find a better improvement + for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i ) + { + // see if we can find a better object in this sector and put it into our slot + UINT32 poolslot = 0; + UINT8 index = 0; + if ( (*pObj)[i]->data.objectStatus < 100 && GetBetterObject_InventoryPool( pObj->usItem, (*pObj)[i]->data.objectStatus, poolslot, index ) ) + { + // WARNING! The correct way of doing this would be to exchange both objects + // we are not doing this here on purpose, as we want to save time + // instead, we switch status and a few other properties + // Only do this if you know what you are doing - in the wrong location, we could do odd things to global pointers. Consider yourself warned! + + // tmp data + INT16 objectStatus = (*pObj)[i]->data.objectStatus; + INT8 bTrap = (*pObj)[i]->data.bTrap; + UINT8 fUsed = (*pObj)[i]->data.fUsed; + UINT8 ubImprintID = (*pObj)[i]->data.ubImprintID; + FLOAT bTemperature = (*pObj)[i]->data.bTemperature; + UINT8 ubDirection = (*pObj)[i]->data.ubDirection; + UINT32 ubWireNetworkFlag = (*pObj)[i]->data.ubWireNetworkFlag; + INT8 bDefuseFrequency = (*pObj)[i]->data.bDefuseFrequency; + INT16 sRepairThreshold = (*pObj)[i]->data.sRepairThreshold; + UINT64 sObjectFlag = (*pObj)[i]->data.sObjectFlag; + + // set data on our object + (*pObj)[i]->data.objectStatus = (pInventoryPoolList[poolslot].object)[index]->data.objectStatus; + (*pObj)[i]->data.bTrap = (pInventoryPoolList[poolslot].object)[index]->data.bTrap; + (*pObj)[i]->data.fUsed = (pInventoryPoolList[poolslot].object)[index]->data.fUsed; + (*pObj)[i]->data.ubImprintID = (pInventoryPoolList[poolslot].object)[index]->data.ubImprintID; + (*pObj)[i]->data.bTemperature = (pInventoryPoolList[poolslot].object)[index]->data.bTemperature; + (*pObj)[i]->data.ubDirection = (pInventoryPoolList[poolslot].object)[index]->data.ubDirection; + (*pObj)[i]->data.ubWireNetworkFlag = (pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag; + (*pObj)[i]->data.bDefuseFrequency = (pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency; + (*pObj)[i]->data.sRepairThreshold = (pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold; + (*pObj)[i]->data.sObjectFlag = (pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag; + + // set data on world object + (pInventoryPoolList[poolslot].object)[index]->data.objectStatus = objectStatus; + (pInventoryPoolList[poolslot].object)[index]->data.bTrap = bTrap; + (pInventoryPoolList[poolslot].object)[index]->data.fUsed = fUsed; + (pInventoryPoolList[poolslot].object)[index]->data.ubImprintID = ubImprintID; + (pInventoryPoolList[poolslot].object)[index]->data.bTemperature = bTemperature; + (pInventoryPoolList[poolslot].object)[index]->data.ubDirection = ubDirection; + (pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag = ubWireNetworkFlag; + (pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency = bDefuseFrequency; + (pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold = sRepairThreshold; + (pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag = sObjectFlag; + } + } + + // pick up new items from the sector and put them on the stack + UINT8 ubSlotLimit = ItemSlotLimit(pObj, slot, pSoldier, FALSE); + + while (pObj->ubNumberOfObjects < ubSlotLimit) + { + UINT32 poolslot = 0; + UINT8 index = 0; + if ( GetBetterObject_InventoryPool( pObj->usItem, 0, poolslot, index ) ) + { + (pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer ); + + if ( !TryToStackInSlot( pSoldier, &gItemPointer, slot ) ) + break; + } + else + { + break; + } + } + + // try to improve attachments, too + for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i ) + { + for ( attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != (*pObj)[i]->attachments.end(); ++iter ) + { + if ( iter->exists() ) + { + // see if we can find a better object in this sector and put it into our slot + UINT32 poolslot = 0; + UINT8 index = 0; + if ( (*iter)[0]->data.objectStatus < 100 && GetBetterObject_InventoryPool( iter->usItem, (*iter)[0]->data.objectStatus, poolslot, index ) ) + { + // WARNING! The correct way of doing this would be to exchange both objects + // we are not doing this here on purpose, as we want to save time + // instead, we switch status and a few other properties + // Only do this if you know what you are doing - in the wrong location, we could do odd things to global pointers. Consider yourself warned! + + // tmp data + INT16 objectStatus = (*iter)[0]->data.objectStatus; + INT8 bTrap = (*iter)[0]->data.bTrap; + UINT8 fUsed = (*iter)[0]->data.fUsed; + UINT8 ubImprintID = (*iter)[0]->data.ubImprintID; + FLOAT bTemperature = (*iter)[0]->data.bTemperature; + UINT8 ubDirection = (*iter)[0]->data.ubDirection; + UINT32 ubWireNetworkFlag = (*iter)[0]->data.ubWireNetworkFlag; + INT8 bDefuseFrequency = (*iter)[0]->data.bDefuseFrequency; + INT16 sRepairThreshold = (*iter)[0]->data.sRepairThreshold; + UINT64 sObjectFlag = (*iter)[0]->data.sObjectFlag; + + // set data on our object + (*iter)[0]->data.objectStatus = (pInventoryPoolList[poolslot].object)[index]->data.objectStatus; + (*iter)[0]->data.bTrap = (pInventoryPoolList[poolslot].object)[index]->data.bTrap; + (*iter)[0]->data.fUsed = (pInventoryPoolList[poolslot].object)[index]->data.fUsed; + (*iter)[0]->data.ubImprintID = (pInventoryPoolList[poolslot].object)[index]->data.ubImprintID; + (*iter)[0]->data.bTemperature = (pInventoryPoolList[poolslot].object)[index]->data.bTemperature; + (*iter)[0]->data.ubDirection = (pInventoryPoolList[poolslot].object)[index]->data.ubDirection; + (*iter)[0]->data.ubWireNetworkFlag = (pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag; + (*iter)[0]->data.bDefuseFrequency = (pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency; + (*iter)[0]->data.sRepairThreshold = (pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold; + (*iter)[0]->data.sObjectFlag = (pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag; + + // set data on world object + (pInventoryPoolList[poolslot].object)[index]->data.objectStatus = objectStatus; + (pInventoryPoolList[poolslot].object)[index]->data.bTrap = bTrap; + (pInventoryPoolList[poolslot].object)[index]->data.fUsed = fUsed; + (pInventoryPoolList[poolslot].object)[index]->data.ubImprintID = ubImprintID; + (pInventoryPoolList[poolslot].object)[index]->data.bTemperature = bTemperature; + (pInventoryPoolList[poolslot].object)[index]->data.ubDirection = ubDirection; + (pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag = ubWireNetworkFlag; + (pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency = bDefuseFrequency; + (pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold = sRepairThreshold; + (pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag = sObjectFlag; + } + } + } + } + } + } + + // 4. add attachments. If an attachment cannot be attached - for example because a different one is blocking it - tell us so + for ( std::vector::iterator it = vec.begin( ); it != vec.end( ); ++it ) + { + GEAR_NODE node = (*it); + + // try to add attachments if nod entry fits the item, or if the node item is NOTHING (essentially a wildcard for this) + if ( node.slot >= 0 && node.slot < NUM_INV_SLOTS && + (node.slot < VESTPOCKPOS || node.slot > BPACKPOCKPOS) && // we already did that for LBE slots though, no need to repeat + !node.attachments.empty( ) && pSoldier->inv[node.slot].exists( ) && + ((pSoldier->inv[node.slot]).usItem == node.item || NOTHING == node.item) ) + { + OBJECTTYPE* pObj = &(pSoldier->inv[node.slot]); + + for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i ) + { + for ( std::vector::iterator attit = node.attachments.begin( ); attit != node.attachments.end( ); ++attit ) + { + UINT16 item_attachment = (*attit); + + // only apply attachments if they don't require any checks! + INT32 bAttachInfoIndex = GetAttachmentInfoIndex( item_attachment ); + + if ( bAttachInfoIndex < 0 || AttachmentInfo[bAttachInfoIndex].bAttachmentSkillCheck == NO_CHECK ) + { + UINT32 poolslot = 0; + UINT8 index = 0; + if ( GetBetterObject_InventoryPool( item_attachment, 0, poolslot, index ) ) + { + (pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer ); + + BOOLEAN isAttachedNow = pObj->AttachObject( pSoldier, &gItemPointer, FALSE, i ); //do the actual attaching + + if ( !isAttachedNow ) + { + attachmentsound = TRUE; + + //ScreenMsg( color, MSG_INTERFACE, L"%s could not attach %s to %s", pSoldier->GetName( ), Item[item_attachment].szItemName, Item[pObj->usItem].szItemName ); + + AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &gItemPointer ); + } + } + } + } + } + } + } + + // 5. reload guns, fill mags + for ( int slot = HELMETPOS; slot < NUM_INV_SLOTS; ++slot ) + { + if ( pSoldier->inv[slot].exists( ) ) + { + if ( Item[(pSoldier->inv[slot]).usItem].usItemClass & (IC_KIT | IC_MEDKIT) ) + { + UINT32 poolslot = 0; + UINT8 index = 0; + if ( GetBetterObject_InventoryPool( (pSoldier->inv[slot]).usItem, 0, poolslot, index ) ) + { + DistributeStatus( &(pInventoryPoolList[poolslot].object), &(pSoldier->inv[slot]), 100); + } + } + + if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & (IC_GUN | IC_AMMO))) + { + OBJECTTYPE* pObj = &(pSoldier->inv[slot]); + + for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i ) + { + UINT16 ammoitem = (pSoldier->inv[slot]).usItem; + + if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) ) + { + UINT8 calibre = Weapon[pObj->usItem].ubCalibre; + UINT16 magsize = GetMagSize( pObj, i ); + + ammoitem = (*pObj)[i]->data.gun.usGunAmmoItem; + for ( std::vector::iterator it = vec.begin( ); it != vec.end( ); ++it ) + { + if ( (*it).slot == slot && (*it).item == pObj->usItem ) + { + ammoitem = (*it).ammoitem; + break; + } + } + + // if we somehow don't have a valid ammoitem (for example, if the gun was empty), find one + if ( !ammoitem ) + ammoitem = DefaultMagazine( pObj->usItem ); + + UINT16 usMagIndex = Item[ammoitem].ubClassIndex; + + // safety check: if the calibre or mag size doesn't fit, get a better mag + if ( calibre != Magazine[usMagIndex].ubCalibre || magsize != Magazine[usMagIndex].ubMagSize ) + { + ammoitem = FindReplacementMagazine( calibre, magsize, Magazine[usMagIndex].ubAmmoType ); + usMagIndex = Item[ammoitem].ubClassIndex; + } + + // if the requested ammo isn't the only currently loaded, take that one out + if ( (*pObj)[i]->data.gun.usGunAmmoItem != ammoitem && (*pObj)[i]->data.gun.usGunAmmoItem != NOTHING && ammoitem != NOTHING ) + { + CreateAmmo( (*pObj)[i]->data.gun.usGunAmmoItem, &gItemPointer, (*pObj)[i]->data.gun.ubGunShotsLeft ); + + AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel ); + DeleteObj( &gItemPointer ); + + (*pObj)[i]->data.gun.usGunAmmoItem = NOTHING; + (*pObj)[i]->data.gun.ubGunShotsLeft = 0; + (*pObj)[i]->data.gun.ubGunAmmoType = 0; + } + } + + UINT16 usMagIndex = Item[ammoitem].ubClassIndex; + + UINT16 neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.ubShotsLeft; + if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) ) + neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.gun.ubGunShotsLeft; + + while ( neededammo > 0 ) + { + UINT32 poolslot = 0; + if ( GetFittingAmmo_InventoryPool( Magazine[usMagIndex].ubCalibre, Magazine[usMagIndex].ubAmmoType, poolslot ) ) + { + for ( INT16 j = pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1; j >= 0; --j ) + { + UINT32 takeammo = min( neededammo, pInventoryPoolList[poolslot].object[j]->data.ubShotsLeft ); + + neededammo -= takeammo; + + pInventoryPoolList[poolslot].object[j]->data.ubShotsLeft -= takeammo; + + if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) ) + { + (*pObj)[i]->data.gun.usGunAmmoItem = ammoitem; + (*pObj)[i]->data.gun.ubGunAmmoType = Magazine[usMagIndex].ubAmmoType; + (*pObj)[i]->data.gun.ubGunShotsLeft += takeammo; + + usReloadSound = Weapon[pObj->usItem].sReloadSound; + } + else + { + (*pObj)[i]->data.ubShotsLeft += takeammo; + } + + if ( !pInventoryPoolList[poolslot].object[j]->data.ubShotsLeft ) + { + pInventoryPoolList[poolslot].object.RemoveObjectAtIndex( j ); + } + + if ( !neededammo ) + break; + } + } + else + { + break; + } + + if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) ) + neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.gun.ubGunShotsLeft; + else + neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.ubShotsLeft; + } + } + } + } + } + + // Immersion, let's play a reload sound + if ( usReloadSound != 0 ) + PlayJA2Sample( usReloadSound, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); + else if ( attachmentsound ) + PlayJA2Sample( ATTACH_TO_GUN, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); + + // 6. redraw inventory + fTeamPanelDirty = TRUE; + fMapPanelDirty = TRUE; + fInterfacePanelDirty = DIRTYLEVEL2; + fCharacterInfoPanelDirty = TRUE; + + // refresh ammo button in description box if it exists + if ( giItemDescAmmoButton > -1 ) + MarkAButtonDirty( giItemDescAmmoButton ); // Required for tactical screen + } +} diff --git a/Tactical/SkillMenu.cpp b/Tactical/SkillMenu.cpp index 9f8c0b94..0f91c4ee 100644 --- a/Tactical/SkillMenu.cpp +++ b/Tactical/SkillMenu.cpp @@ -12,6 +12,15 @@ #include "worldman.h" #include "Queen Command.h" #include "strategicmap.h" +#include "Map Screen Interface.h" + +extern void GetEquipmentTemplates( ); + +std::vector gTemplateVector; + +extern void ReadEquipmentTable( SOLDIERTYPE* pSoldier, std::string name ); + +extern INT8 bSelectedInfoChar; // sevenfm: need this for correct calculation of traits menu position extern INT16 gsInterfaceLevel; @@ -740,3 +749,141 @@ void TraitsMenu( INT32 usMapPos ) gTraitSelection.Setup(0); } + +/////////////////////////////// Trait Selection //////////////////////////////////////////// +EquipmentSelection gEquipmentSelection; + +void Wrapper_Function_EquipmentSelection(UINT32 aVal) { gEquipmentSelection.Functions(aVal); } +void Wrapper_Setup_EquipmentSelection(UINT32 aVal) { gEquipmentSelection.Setup(aVal); } +void Wrapper_Cancel_EquipmentSelection(UINT32 aVal) { gEquipmentSelection.Cancel(); } +/////////////////////////////// Trait Selection //////////////////////////////////////////// + +/////////////////////////////// Trait Selection //////////////////////////////////////////// +UINT32 gEquipmentSelectionFirstEntryShown = 0; +UINT32 gEquipmentSelectionLastEntryShown = 0; +UINT32 gEquipmentSelectionMaxEntriesShown = 0; + +void +EquipmentSelection::Setup(UINT32 aVal) +{ + Destroy(); + + SetupPopup("EquipmentSelection"); + + INT16 sX = 261 + 406 + xResOffset + 30; + INT16 sY = 0 + 10 + yResOffset + 30; + + if ( sX < 0 ) sX = 0; + if ( sY < 0 ) sY = 0; + + usTraitMenuPosX = sX + 30; + usTraitMenuPosY = sY; + + if ( (usTraitMenuPosX + 400) > SCREEN_WIDTH ) + usTraitMenuPosX = SCREEN_WIDTH - 400; + + if ( (usTraitMenuPosY + 130) > SCREEN_HEIGHT ) + usTraitMenuPosY = SCREEN_HEIGHT - 190; + + POPUP_OPTION *pOption; + + CHAR16 pStr[300]; + + // create entries for the sub-menus for each trait + GetEquipmentTemplates(); + + // we limit the maximum number of entries shown at once - too many entries could theoretically cause the button system to freak out + gEquipmentSelectionMaxEntriesShown = min(40, max( 0, (SCREEN_HEIGHT - 150 - usTraitMenuPosY) / 12 ) ); + + gEquipmentSelectionLastEntryShown = min( gEquipmentSelectionFirstEntryShown + gEquipmentSelectionMaxEntriesShown, gTemplateVector.size( ) ); + + if ( gEquipmentSelectionLastEntryShown < gEquipmentSelectionMaxEntriesShown ) + gEquipmentSelectionFirstEntryShown = 0; + else + gEquipmentSelectionFirstEntryShown = gEquipmentSelectionLastEntryShown - gEquipmentSelectionMaxEntriesShown; + + UINT32 cnt = gEquipmentSelectionFirstEntryShown; + for ( UINT32 i = gEquipmentSelectionFirstEntryShown; i < gEquipmentSelectionLastEntryShown; ++i ) + { + //std::string str8 = (*it); + std::string str8 = gTemplateVector[i]; + + std::size_t found = str8.find_last_of( "." ); + + std::string str82 = str8.substr( 0, found ); + + const char* coca = str82.c_str( ); + + CHAR16 tmpchar[256]; + + int nChars = MultiByteToWideChar( CP_ACP, 0, coca, -1, NULL, 0 ); + MultiByteToWideChar( CP_UTF8, 0, coca, -1, tmpchar, nChars ); + + pOption = new POPUP_OPTION( &std::wstring( tmpchar ), new popupCallbackFunction( &Wrapper_Function_EquipmentSelection, i ) ); + + pOption->color_foreground = FONT_GREEN; + + GetPopup()->addOption(*pOption); + } + + // 'more' option (moves down list) + if ( gEquipmentSelectionMaxEntriesShown < gTemplateVector.size( ) ) + { + swprintf( pStr, pSkillMenuStrings[SKILLMENU_MORE] ); + pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_EquipmentSelection, 99999 ) ); + GetPopup( )->addOption( *pOption ); + } + + // cancel option + swprintf(pStr, pSkillMenuStrings[SKILLMENU_CANCEL]); + pOption = new POPUP_OPTION( &std::wstring( pStr ), new popupCallbackFunction( &Wrapper_Function_EquipmentSelection, 99998 ) ); + GetPopup()->addOption(*pOption); + + SetPos(usTraitMenuPosX, usTraitMenuPosY); +} + +void +EquipmentSelection::Functions(UINT32 aVal) +{ + // this special value allows to 'scroll down' + if ( aVal == 99999 ) + { + gEquipmentSelectionFirstEntryShown += gEquipmentSelectionMaxEntriesShown; + + if ( gEquipmentSelectionFirstEntryShown >= gTemplateVector.size( ) ) + gEquipmentSelectionFirstEntryShown = 0; + + Cancel( ); + + EquipmentListMenu(); + } + else if ( aVal == 99998 ) + { + gEquipmentSelectionFirstEntryShown = 0; + + Cancel( ); + } + else if ( aVal < gTemplateVector.size( ) ) + { + SOLDIERTYPE* pSoldier = &Menptr[gCharactersList[bSelectedInfoChar].usSolID]; + if ( pSoldier ) + { + std::string name = gTemplateVector[aVal]; + + ReadEquipmentTable( pSoldier, name ); + } + + // Note that we don't close menu - this way we don't have to open it every time we want to apply a template + } +} +/////////////////////////////// Trait Selection //////////////////////////////////////////// + +void EquipmentListMenu() +{ + gEquipmentSelection.Setup(0); +} + +void EquipmentListMenuCancel() +{ + gEquipmentSelection.Cancel(); +} diff --git a/Tactical/SkillMenu.h b/Tactical/SkillMenu.h index a788448a..0fdc7f1b 100644 --- a/Tactical/SkillMenu.h +++ b/Tactical/SkillMenu.h @@ -10,7 +10,7 @@ /** * @brief A basic class for popup menus - * Use this to create mensu calling other menus, each having various sub-options + * Use this to create menus calling other menus, each having various sub-options * I tried to make this as orderly as possible. It's ugly, but much more clean than the first solution, which was a hardcoded mess. * You will, however, still need wrapper functions (we use callbacks on member functions), see Implementation. */ @@ -162,4 +162,18 @@ private: */ void TraitsMenu( INT32 usMapPos ); +// C17 +// not skill-related, but while we're here... +class EquipmentSelection : public SkillMenuItem +{ +public: + EquipmentSelection() {} + + void Setup(UINT32 aVal); + void Functions(UINT32 aVal); +}; + +void EquipmentListMenu(); +void EquipmentListMenuCancel(); + #endif \ No newline at end of file diff --git a/Utils/Text.h b/Utils/Text.h index 5613557c..681313d9 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -167,6 +167,7 @@ enum SKILLMENU_CANCEL, SKILLMENU_X_MILITIA, SKILLMENU_ALL_MILITIA, + SKILLMENU_MORE, }; extern STR16 pSkillMenuStrings[]; //Flugente @@ -2992,6 +2993,9 @@ extern STR16 szInteractiveActionText[]; extern STR16 szLaptopStatText[]; +// Flugente: gear templates +extern STR16 szGearTemplateText[]; + #define TACTICAL_INVENTORY_DIALOG_NUM 16 #define TACTICAL_COVER_DIALOG_NUM 16 diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index b7cb65ed..def01ea4 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -2837,8 +2837,9 @@ STR16 pSkillMenuStrings[] = L"其他队伍", L"取消", L"%d 民兵", - L"所有民兵", + + L"More", }; STR16 pSnitchMenuStrings[] = @@ -11251,6 +11252,15 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + // WANNE: Some Chinese specific strings that needs to be in unicode! STR16 ChineseSpecString1 = L"%%"; //defined in _ChineseText.cpp as this file is already unicode STR16 ChineseSpecString2 = L"*%3d%%%%"; //defined in _ChineseText.cpp as this file is already unicode diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 27d3598c..28fe4d2b 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -2836,8 +2836,9 @@ STR16 pSkillMenuStrings[] = // TODO.Translate L"Other Squads", L"Cancel", L"%d Militia", - L"All Militia", + + L"More", }; // TODO.Translate @@ -11269,4 +11270,13 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 35d05120..d6aa1d20 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -2833,8 +2833,9 @@ STR16 pSkillMenuStrings[] = L"Other Squads", L"Cancel", L"%d Militia", - L"All Militia", + + L"More", }; STR16 pSnitchMenuStrings[] = @@ -11250,4 +11251,13 @@ STR16 szLaptopStatText[] = L"Recruit approach", }; +STR16 szGearTemplateText[] = +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 75847802..fa109c06 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -2839,14 +2839,15 @@ STR16 pTraitSkillsDenialStrings[] = L" - aimed gun\n", }; -STR16 pSkillMenuStrings[] = // TODO.Translate +STR16 pSkillMenuStrings[] = { L"Milice", L"Autre escouade", L"Annuler", L"%d miliciens", - L"Tous", + + L"More", // TODO.Translate }; STR16 pSnitchMenuStrings[] = @@ -11251,4 +11252,13 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index e39818f5..7266506a 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -2840,8 +2840,9 @@ STR16 pSkillMenuStrings[] = // TODO.Translate L"Other Squads", L"Cancel", L"%d Militia", - L"All Militia", + + L"More", // TODO.Translate }; // TODO.Translate @@ -11081,4 +11082,13 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 54bf2b11..0253d834 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -2831,8 +2831,9 @@ STR16 pSkillMenuStrings[] = // TODO.Translate L"Other Squads", L"Cancel", L"%d Militia", - L"All Militia", + + L"More", // TODO.Translate }; // TODO.Translate @@ -11260,4 +11261,13 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 0cfbe524..438addda 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -2843,8 +2843,9 @@ STR16 pSkillMenuStrings[] = // TODO.Translate L"Other Squads", L"Cancel", L"%d Militia", - L"All Militia", + + L"More", // TODO.Translate }; STR16 pSnitchMenuStrings[] = @@ -11273,4 +11274,13 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index e8c368a9..c4c7c954 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -2837,8 +2837,9 @@ STR16 pSkillMenuStrings[] = L"Другие отряды", L"Отмена", L"%d ополченцев", - L"Все ополченцы", + + L"More", // TODO.Translate }; STR16 pSnitchMenuStrings[] = @@ -11254,4 +11255,13 @@ STR16 szLaptopStatText[] = // TODO.Translate L"Recruit approach", }; +STR16 szGearTemplateText[] = // TODO.Translate +{ + L"Enter Template Name", + L"Not possible during combat.", + L"Selected mercenary is not in this sector.", + L"%s is not in that sector.", + L"%s could not equip %s.", +}; + #endif //RUSSIAN