From 93043cb4a2895d725f15eb16a8d08af2a4cf893b Mon Sep 17 00:00:00 2001 From: Greysa <52637570+Greysa@users.noreply.github.com> Date: Sat, 21 Mar 2026 22:06:00 +1100 Subject: [PATCH 1/6] Basic implementaion of unjamming on reload hotkey. TODO: text strings check dual wielding --- Tactical/Items.cpp | 55 ++++++++++++++++++++++++++++++++++- Tactical/Turn Based Input.cpp | 22 +++++++++----- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index c8e802e87..a66336aca 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3872,7 +3872,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) { OBJECTTYPE *pObj, *pObj2; INT8 bSlot; - INT16 bAPCost; + INT16 bAPCost; BOOLEAN fRet = FALSE; CHECKF( pSoldier ); @@ -3880,6 +3880,59 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) // Flugente: check for underbarrel weapons and use that object if necessary pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); + // Greysa: Check if weapon is jammed and unjam it first + // Greysa: need to implement and test duel wield jamming and unjamming + if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0) + { + //borrowed from Weapons.cpp + if (EnoughPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM], FALSE)) + { + DeductPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM]); + + INT8 bChanceMod; + + if (Weapon[pSoldier->inv[pSoldier->ubAttackingHand].usItem].EasyUnjam) + bChanceMod = 100; + else + bChanceMod = (INT8)(GetReliability(pObj) * 4); + + int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod); + + // sevenfm: AI always unjams successfully + // Greysa: no AI check required here + if (iResult > 0)// || !(pSoldier->flags.uiStatusFlags & SOLDIER_PC)) + { + // yay! unjammed the gun + (*pObj)[0]->data.gun.bGunAmmoStatus *= -1; + + // MECHANICAL/DEXTERITY GAIN: Unjammed a gun + + if (bChanceMod < 100) // don't give exp for unjamming an easily unjammable gun + { + StatChange(pSoldier, MECHANAMT, 5, FALSE); + StatChange(pSoldier, DEXTAMT, 5, FALSE); + } + + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? + PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + //print unjam message and merc voice feedback + // Greysa: We want to skip reloading if we unjammed + return FALSE; // Greysa: Do we return false or true here to skip rest of reload? I think true, but test this! Also, need to figure out animation + } + else + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + return FALSE; + } + } + else + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + return FALSE; + } + } + // manual recharge if ((*pObj)[0]->data.gun.ubGunShotsLeft && !((*pObj)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) ) { diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 9bdee0c15..866b5e108 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -4309,15 +4309,21 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) case 'r': if( gusSelectedSoldier != NOBODY ) { - if( fAlt ) //reload selected merc's weapon + // Greysa: Commented out for testing purposes. + //if( fAlt ) //reload selected merc's weapon + //{ + // if ( CHEATER_CHEAT_LEVEL( ) ) + // { + // ReloadWeapon( gusSelectedSoldier, gusSelectedSoldier->ubAttackingHand ); + // } + // else + // HandleTBReload(); + //} + // Remove this once testing and above commenting finished with. + if( fAlt ) { - if ( CHEATER_CHEAT_LEVEL( ) ) - { - ReloadWeapon( gusSelectedSoldier, gusSelectedSoldier->ubAttackingHand ); - } - else - HandleTBReload(); - } + HandleTBReload(); + } else if( fCtrl ) { if ( INFORMATION_CHEAT_LEVEL( ) ) From 655bbfe9bafc8f42f516bdf714e852c0dd776e1f Mon Sep 17 00:00:00 2001 From: Greysa <52637570+Greysa@users.noreply.github.com> Date: Sun, 22 Mar 2026 09:49:19 +1100 Subject: [PATCH 2/6] Added dual wielding unjamming, tidied up reload and unjam code a little --- Tactical/Items.cpp | 144 ++++++++++++++++++++++++---------- Tactical/Turn Based Input.cpp | 1 + 2 files changed, 103 insertions(+), 42 deletions(-) diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index a66336aca..104ecda75 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3879,58 +3879,118 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) // Flugente: check for underbarrel weapons and use that object if necessary pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); - + if (pSoldier->IsValidSecondHandShotForReloadingPurposes()) //check for valid second hand weapon for reloading purposes (ie, not a launcher or something that doesn't use ammo) + { + pObj2 = pSoldier->GetUsedWeapon( &(pSoldier->inv[SECONDHANDPOS]) ); + } + else + { + pObj2 = NULL; + } // Greysa: Check if weapon is jammed and unjam it first - // Greysa: need to implement and test duel wield jamming and unjamming - if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0) + // Greysa: need to implement and test duel wield unjamming + if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0 || ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0)) { - //borrowed from Weapons.cpp - if (EnoughPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM], FALSE)) + if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0) { - DeductPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM]); + //borrowed from Weapons.cpp + if (EnoughPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM], FALSE)) + { + DeductPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM]); - INT8 bChanceMod; + INT8 bChanceMod; - if (Weapon[pSoldier->inv[pSoldier->ubAttackingHand].usItem].EasyUnjam) - bChanceMod = 100; - else - bChanceMod = (INT8)(GetReliability(pObj) * 4); + if (Weapon[pObj->usItem].EasyUnjam) + bChanceMod = 100; + else + bChanceMod = (INT8)(GetReliability(pObj) * 4); - int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod); + int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod); - // sevenfm: AI always unjams successfully - // Greysa: no AI check required here - if (iResult > 0)// || !(pSoldier->flags.uiStatusFlags & SOLDIER_PC)) - { - // yay! unjammed the gun - (*pObj)[0]->data.gun.bGunAmmoStatus *= -1; + // sevenfm: AI always unjams successfully + // Greysa: no AI check required here + if (iResult > 0)// || !(pSoldier->flags.uiStatusFlags & SOLDIER_PC)) + { + // yay! unjammed the gun + (*pObj)[0]->data.gun.bGunAmmoStatus *= -1; - // MECHANICAL/DEXTERITY GAIN: Unjammed a gun + // MECHANICAL/DEXTERITY GAIN: Unjammed a gun - if (bChanceMod < 100) // don't give exp for unjamming an easily unjammable gun + if (bChanceMod < 100) // don't give exp for unjamming an easily unjammable gun + { + StatChange(pSoldier, MECHANAMT, 5, FALSE); + StatChange(pSoldier, DEXTAMT, 5, FALSE); + } + + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? + PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + // merc voice feedback? + // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome + //return FALSE; // Greysa: Do we return false or true here to skip rest of reload? I think true, but test this! Also, need to figure out animation + } + else { - StatChange(pSoldier, MECHANAMT, 5, FALSE); - StatChange(pSoldier, DEXTAMT, 5, FALSE); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + //return FALSE; } - - DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? - PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); - //print unjam message and merc voice feedback - // Greysa: We want to skip reloading if we unjammed - return FALSE; // Greysa: Do we return false or true here to skip rest of reload? I think true, but test this! Also, need to figure out animation } else { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); - return FALSE; + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + //return FALSE; } } - else + if ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0) { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); - return FALSE; + if (EnoughPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM], FALSE)) + { + DeductPoints(pSoldier, APBPConstants[AP_UNJAM], APBPConstants[BP_UNJAM]); + + INT8 bChanceMod; + + if (Weapon[pObj2->usItem].EasyUnjam) + bChanceMod = 100; + else + bChanceMod = (INT8)(GetReliability(pObj2) * 4); + + int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod); + + // sevenfm: AI always unjams successfully + // Greysa: no AI check required here + if (iResult > 0)// || !(pSoldier->flags.uiStatusFlags & SOLDIER_PC)) + { + // yay! unjammed the gun + (*pObj2)[0]->data.gun.bGunAmmoStatus *= -1; + + // MECHANICAL/DEXTERITY GAIN: Unjammed a gun + + if (bChanceMod < 100) // don't give exp for unjamming an easily unjammable gun + { + StatChange(pSoldier, MECHANAMT, 5, FALSE); + StatChange(pSoldier, DEXTAMT, 5, FALSE); + } + + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? + PlayJA2Sample(Weapon[Item[pObj2->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj2->usItem]); + // merc voice feedback? + // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome + return FALSE; // Greysa: Do we return false or true here to skip rest of reload? I think true, but test this! Also, need to figure out animation + } + else + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj2->usItem]); + return FALSE; + } + } + else + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj2->usItem]); + return FALSE; + } } + return FALSE; } // manual recharge @@ -3968,9 +4028,9 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) PlayJA2Sample( Weapon[ Item[pObj->usItem].ubClassIndex ].ManualReloadSound, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); - if ( pSoldier->IsValidSecondHandShot( ) ) + if (pObj2 != NULL)//( pSoldier->IsValidSecondHandShot( ) ) { - pObj2 = &(pSoldier->inv[SECONDHANDPOS]); + //pObj2 = &(pSoldier->inv[SECONDHANDPOS]); if ((*pObj2)[0]->data.gun.ubGunShotsLeft && !((*pObj2)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) ) { @@ -3983,9 +4043,9 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) } else { - if ( pSoldier->IsValidSecondHandShot( ) ) + if (pObj2 != NULL)//( pSoldier->IsValidSecondHandShot( ) ) { - pObj2 = &(pSoldier->inv[SECONDHANDPOS]); + //pObj2 = &(pSoldier->inv[SECONDHANDPOS]); if ((*pObj2)[0]->data.gun.ubGunShotsLeft && !((*pObj2)[0]->data.gun.ubGunState & GS_CARTRIDGE_IN_CHAMBER) ) { @@ -4042,21 +4102,21 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) // if we are valid for two-pistol shooting (reloading) and we have enough APs still // then do a reload of both guns! // Flugente: only reload if it's empty, or we really want to - if ( pSoldier->IsValidSecondHandShotForReloadingPurposes() + if ( pObj2 != NULL //pSoldier->IsValidSecondHandShotForReloadingPurposes() && ( aReloadEvenIfNotEmpty || !EnoughAmmo( pSoldier, FALSE, SECONDHANDPOS ) ) ) { // Flugente: check for underbarrel weapons and use that object if necessary - pObj = pSoldier->GetUsedWeapon( &( pSoldier->inv[SECONDHANDPOS] ) ); + //pObj2 = pSoldier->GetUsedWeapon( &( pSoldier->inv[SECONDHANDPOS] ) ); bSlot = FindAmmoToReload( pSoldier, SECONDHANDPOS, NO_SLOT ); if ( bSlot != NO_SLOT ) { // ce would reload using this ammo! - bAPCost = GetAPsToReloadGunWithAmmo( pSoldier, pObj, &( pSoldier->inv[bSlot] ) ); + bAPCost = GetAPsToReloadGunWithAmmo( pSoldier, pObj2, &( pSoldier->inv[bSlot] ) ); if ( EnoughPoints( pSoldier, (INT16)bAPCost, 0, FALSE ) ) { // reload the 2nd gun too - fRet = ReloadGun( pSoldier, pObj, &( pSoldier->inv[bSlot] ) ); + fRet = ReloadGun( pSoldier, pObj2, &( pSoldier->inv[bSlot] ) ); } else { diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 866b5e108..c4f9503e5 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -4319,6 +4319,7 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) // else // HandleTBReload(); //} + // Remove this once testing and above commenting finished with. if( fAlt ) { From 4cc9dd6a951facd0f876df4a53ab42f5b5492e24 Mon Sep 17 00:00:00 2001 From: Greysa <52637570+Greysa@users.noreply.github.com> Date: Sun, 22 Mar 2026 11:19:49 +1100 Subject: [PATCH 3/6] Message strings added to language files. --- Tactical/Items.cpp | 30 ++++++++++-------------------- i18n/_ChineseText.cpp | 5 +++++ i18n/_DutchText.cpp | 5 +++++ i18n/_EnglishText.cpp | 5 +++++ i18n/_FrenchText.cpp | 5 +++++ i18n/_GermanText.cpp | 5 +++++ i18n/_ItalianText.cpp | 5 +++++ i18n/_PolishText.cpp | 5 +++++ i18n/_RussianText.cpp | 5 +++++ i18n/include/Text.h | 4 ++++ 10 files changed, 54 insertions(+), 20 deletions(-) diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 104ecda75..8a29b2662 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3879,7 +3879,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) // Flugente: check for underbarrel weapons and use that object if necessary pObj = pSoldier->GetUsedWeapon( &(pSoldier->inv[HANDPOS]) ); - if (pSoldier->IsValidSecondHandShotForReloadingPurposes()) //check for valid second hand weapon for reloading purposes (ie, not a launcher or something that doesn't use ammo) + if (pSoldier->IsValidSecondHandShotForReloadingPurposes()) //check for valid second hand weapon for reloading purposes (something that doesn't use ammo) { pObj2 = pSoldier->GetUsedWeapon( &(pSoldier->inv[SECONDHANDPOS]) ); } @@ -3888,7 +3888,6 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) pObj2 = NULL; } // Greysa: Check if weapon is jammed and unjam it first - // Greysa: need to implement and test duel wield unjamming if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0 || ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0)) { if ((*pObj)[0]->data.gun.bGunAmmoStatus < 0) @@ -3907,9 +3906,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod); - // sevenfm: AI always unjams successfully - // Greysa: no AI check required here - if (iResult > 0)// || !(pSoldier->flags.uiStatusFlags & SOLDIER_PC)) + if (iResult > 0) { // yay! unjammed the gun (*pObj)[0]->data.gun.bGunAmmoStatus *= -1; @@ -3926,19 +3923,15 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); // merc voice feedback? - // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome - //return FALSE; // Greysa: Do we return false or true here to skip rest of reload? I think true, but test this! Also, need to figure out animation } else { ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); - //return FALSE; } } else { ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); - //return FALSE; } } if ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0) @@ -3956,9 +3949,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) int iResult = SkillCheck(pSoldier, UNJAM_GUN_CHECK, bChanceMod); - // sevenfm: AI always unjams successfully - // Greysa: no AI check required here - if (iResult > 0)// || !(pSoldier->flags.uiStatusFlags & SOLDIER_PC)) + if (iResult > 0) { // yay! unjammed the gun (*pObj2)[0]->data.gun.bGunAmmoStatus *= -1; @@ -3973,24 +3964,23 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? PlayJA2Sample(Weapon[Item[pObj2->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj2->usItem]); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj2->usItem]); // merc voice feedback? - // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome - return FALSE; // Greysa: Do we return false or true here to skip rest of reload? I think true, but test this! Also, need to figure out animation + return FALSE; //do I need this? } else { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj2->usItem]); - return FALSE; + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_FAILED_UNJAM], pSoldier->GetName(), ItemNames[pObj2->usItem]); + return FALSE; //do I need this? } } else { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj2->usItem]); - return FALSE; + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_NO_AP_NO_UNJAM], pSoldier->GetName(), ItemNames[pObj2->usItem]); + return FALSE; //do I need this? } } - return FALSE; + return FALSE; // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome. Return value doesn't seem to matter as there doesn't seem to be any actual checks on the returned value } // manual recharge diff --git a/i18n/_ChineseText.cpp b/i18n/_ChineseText.cpp index 2f951f6d3..1976fbe8d 100644 --- a/i18n/_ChineseText.cpp +++ b/i18n/_ChineseText.cpp @@ -2361,6 +2361,11 @@ CHAR16 Message[][STRING_LENGTH] = L"没有能够训练的民兵。", //L"No militia that can be drilled present.", L"%s 已经完全的探索了 %s。", //L"%s has fully explored %s." + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/_DutchText.cpp b/i18n/_DutchText.cpp index 509040f7d..56283c9b6 100644 --- a/i18n/_DutchText.cpp +++ b/i18n/_DutchText.cpp @@ -2360,6 +2360,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s has fully explored %s.", // TODO.Translate + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/_EnglishText.cpp b/i18n/_EnglishText.cpp index f3284709f..a93dc98c0 100644 --- a/i18n/_EnglishText.cpp +++ b/i18n/_EnglishText.cpp @@ -2361,6 +2361,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s has fully explored %s.", + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/_FrenchText.cpp b/i18n/_FrenchText.cpp index a859908d8..7d47cf68c 100644 --- a/i18n/_FrenchText.cpp +++ b/i18n/_FrenchText.cpp @@ -2369,6 +2369,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s has fully explored %s.", // TODO.Translate + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game // TODO.Translate diff --git a/i18n/_GermanText.cpp b/i18n/_GermanText.cpp index cfd6a7ed7..5fbec512a 100644 --- a/i18n/_GermanText.cpp +++ b/i18n/_GermanText.cpp @@ -2403,6 +2403,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s hat %s vollständig erkundet.", + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/_ItalianText.cpp b/i18n/_ItalianText.cpp index a8da7833b..5411245db 100644 --- a/i18n/_ItalianText.cpp +++ b/i18n/_ItalianText.cpp @@ -2355,6 +2355,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s has fully explored %s.", // TODO.Translate + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/_PolishText.cpp b/i18n/_PolishText.cpp index 526206375..46dd6b536 100644 --- a/i18n/_PolishText.cpp +++ b/i18n/_PolishText.cpp @@ -2367,6 +2367,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s has fully explored %s.", // TODO.Translate + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/_RussianText.cpp b/i18n/_RussianText.cpp index ae91c3121..4a4e8c468 100644 --- a/i18n/_RussianText.cpp +++ b/i18n/_RussianText.cpp @@ -2361,6 +2361,11 @@ CHAR16 Message[][STRING_LENGTH] = L"No militia that can be drilled present.", L"%s has fully explored %s.", // TODO.Translate + + // The first %s is a merc name and the second %s is an item name + L"%s unjammed %s.", + L"%s failed to unjam %s.", + L"%s does not have enough APs to unjam %s." }; // the country and its noun in the game diff --git a/i18n/include/Text.h b/i18n/include/Text.h index 470398476..0c890d6b3 100644 --- a/i18n/include/Text.h +++ b/i18n/include/Text.h @@ -612,6 +612,10 @@ enum STR_ASSIGNMENT_EXPLORATION_DONE, + STR_UNJAMMED, + STR_FAILED_UNJAM, + STR_NO_AP_NO_UNJAM, + TEXT_NUM_STR_MESSAGE, }; From d131138fb5dd9e9afab8681b668a07b25695f917 Mon Sep 17 00:00:00 2001 From: Greysa <52637570+Greysa@users.noreply.github.com> Date: Sun, 22 Mar 2026 11:58:24 +1100 Subject: [PATCH 4/6] A little code cleanup and moved debug reload to shift + alt + r --- Tactical/Items.cpp | 9 +++------ Tactical/Turn Based Input.cpp | 25 +++++++++++-------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 8a29b2662..9507b63af 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3919,7 +3919,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) StatChange(pSoldier, DEXTAMT, 5, FALSE); } - DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this even do? PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); // merc voice feedback? @@ -3962,25 +3962,22 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) StatChange(pSoldier, DEXTAMT, 5, FALSE); } - DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? PlayJA2Sample(Weapon[Item[pObj2->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj2->usItem]); // merc voice feedback? - return FALSE; //do I need this? } else { ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_FAILED_UNJAM], pSoldier->GetName(), ItemNames[pObj2->usItem]); - return FALSE; //do I need this? } } else { ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_NO_AP_NO_UNJAM], pSoldier->GetName(), ItemNames[pObj2->usItem]); - return FALSE; //do I need this? } } - return FALSE; // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome. Return value doesn't seem to matter as there doesn't seem to be any actual checks on the returned value + return FALSE; // Greysa: We want to skip reloading if we attempted to unjam, regardless of outcome. Return value doesn't seem to matter as there doesn't seem to be any actual checks on the returned value. I picked FALSE as actual reload hasn't occurred } // manual recharge diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index c4f9503e5..adad80e86 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -4303,24 +4303,21 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) // Make auto reload with magazines from sector inventory case 'R': - HandleTBReloadAll(); - + if (fAlt && gusSelectedSoldier != NOBODY ) // Greysa: Moved here from 'r' to retain all functionality during testing. + { + if (CHEATER_CHEAT_LEVEL()) + { + ReloadWeapon(gusSelectedSoldier, gusSelectedSoldier->ubAttackingHand); + } + } + else + { + HandleTBReloadAll(); + } break; case 'r': if( gusSelectedSoldier != NOBODY ) { - // Greysa: Commented out for testing purposes. - //if( fAlt ) //reload selected merc's weapon - //{ - // if ( CHEATER_CHEAT_LEVEL( ) ) - // { - // ReloadWeapon( gusSelectedSoldier, gusSelectedSoldier->ubAttackingHand ); - // } - // else - // HandleTBReload(); - //} - - // Remove this once testing and above commenting finished with. if( fAlt ) { HandleTBReload(); From a680d1b696d411938298e23c5338d82880658bdf Mon Sep 17 00:00:00 2001 From: Greysa <52637570+Greysa@users.noreply.github.com> Date: Sun, 22 Mar 2026 16:49:33 +1100 Subject: [PATCH 5/6] fixed some screenmsg strings not changed --- Tactical/Items.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 9507b63af..269b4e772 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3921,17 +3921,17 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this even do? PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s unjammed %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj->usItem]); // merc voice feedback? } else { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s failed to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_FAILED_UNJAM], pSoldier->GetName(), ItemNames[pObj->usItem]); } } else { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not have enough APs to unjam %s.", pSoldier->GetName(), ItemNames[pObj->usItem]); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_NO_AP_NO_UNJAM], pSoldier->GetName(), ItemNames[pObj->usItem]); } } if ((pObj2 != NULL) && (*pObj2)[0]->data.gun.bGunAmmoStatus < 0) From 0241130ccd1d0b8c51959200d06d3c21216536d5 Mon Sep 17 00:00:00 2001 From: Greysa <52637570+Greysa@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:32:05 +1100 Subject: [PATCH 6/6] fix for issue #580 --- Tactical/Interface Items.cpp | 6 ++++-- Tactical/Items.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index 532c288a5..ce693284c 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -6060,7 +6060,8 @@ void ItemDescAmmoCallback(GUI_BUTTON *btn,INT32 reason) gfItemAmmoDown = FALSE; //CHRISL: We dont' want to be able to reload guns using the ammo crate from this function - if((gpItemPointer != NULL && Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubMagType >= AMMO_BOX) || !EnoughPoints(gpItemDescSoldier, APBPConstants[AP_RELOAD_GUN], 0, TRUE))//dnl ch65 040913 + //Greysa: add check for ammo item + if( gpItemPointer != NULL && ( (Item[gpItemPointer->usItem].usItemClass != IC_AMMO || Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubMagType >= AMMO_BOX) ) || !EnoughPoints(gpItemDescSoldier, APBPConstants[AP_RELOAD_GUN], 0, TRUE) )//dnl ch65 040913 { fInterfacePanelDirty = DIRTYLEVEL2; btn->uiFlags &= (~BUTTON_CLICKED_ON ); @@ -6083,7 +6084,8 @@ void ItemDescAmmoCallback(GUI_BUTTON *btn,INT32 reason) else { //holding an item - if(Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubCalibre == Weapon[Item[gpItemDescObject->usItem].ubClassIndex].ubCalibre) + //Greysa: add check for ammo item + if( Item[gpItemPointer->usItem].usItemClass == IC_AMMO && Magazine[Item[gpItemPointer->usItem].ubClassIndex].ubCalibre == Weapon[Item[gpItemDescObject->usItem].ubClassIndex].ubCalibre ) { ReloadGun(gpItemDescSoldier, gpItemDescObject, gpItemPointer, ubStatusIndex); } diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index 269b4e772..890369a38 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -3919,7 +3919,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) StatChange(pSoldier, DEXTAMT, 5, FALSE); } - DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this even do? + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); PlayJA2Sample(Weapon[Item[pObj->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj->usItem]); // merc voice feedback? @@ -3962,7 +3962,7 @@ BOOLEAN AutoReload( SOLDIERTYPE * pSoldier, bool aReloadEvenIfNotEmpty ) StatChange(pSoldier, DEXTAMT, 5, FALSE); } - DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); // Greysa: what does this do? + DirtyMercPanelInterface(pSoldier, DIRTYLEVEL2); PlayJA2Sample(Weapon[Item[pObj2->usItem].ubClassIndex].ManualReloadSound, RATE_11025, SoundVolume(HIGHVOLUME, pSoldier->sGridNo), 1, SoundDir(pSoldier->sGridNo)); ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, Message[STR_UNJAMMED], pSoldier->GetName(), ItemNames[pObj2->usItem]); // merc voice feedback?