From 59fba04ff5381330a2ba82565d0343bc1c528d60 Mon Sep 17 00:00:00 2001 From: Sevenfm Date: Thu, 29 Apr 2021 06:28:40 +0000 Subject: [PATCH] More turncoat-related fixes and tweaks (by rftr): - added "recruit turncoat" to the talk menu - recruiting turncoats now costs AP (AP_TALK in APBPConstants) - fixed groups not decrementing soldiers when activating turncoats - fixed a hang when activating a sector with 100% turncoats in realtime git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8988 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- Strategic/Queen Command.cpp | 9 +++-- Tactical/Overhead.cpp | 67 +++++++++++++++++++++++++----------- Tactical/Soldier Control.cpp | 34 ++++++++++-------- Utils/Text.h | 1 + Utils/_ChineseText.cpp | 1 + Utils/_DutchText.cpp | 1 + Utils/_EnglishText.cpp | 1 + Utils/_FrenchText.cpp | 1 + Utils/_GermanText.cpp | 1 + Utils/_ItalianText.cpp | 1 + Utils/_PolishText.cpp | 1 + Utils/_RussianText.cpp | 1 + 12 files changed, 81 insertions(+), 38 deletions(-) diff --git a/Strategic/Queen Command.cpp b/Strategic/Queen Command.cpp index 60830dfe..e28b5667 100644 --- a/Strategic/Queen Command.cpp +++ b/Strategic/Queen Command.cpp @@ -3338,25 +3338,28 @@ bool RemoveOneTurncoat( INT16 sSectorX, INT16 sSectorY, UINT8 aSoldierClass ) switch ( aSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: - if ( pGroup->pEnemyGroup->ubNumAdmins_Turncoat ) + if ( pGroup->pEnemyGroup->ubNumAdmins_Turncoat && pGroup->ubGroupSize && pGroup->pEnemyGroup->ubNumAdmins ) { pGroup->pEnemyGroup->ubNumAdmins_Turncoat--; + pGroup->pEnemyGroup->ubNumAdmins--; pGroup->ubGroupSize--; return true; } break; case SOLDIER_CLASS_ARMY: - if ( pGroup->pEnemyGroup->ubNumTroops_Turncoat ) + if ( pGroup->pEnemyGroup->ubNumTroops_Turncoat && pGroup->ubGroupSize && pGroup->pEnemyGroup->ubNumTroops ) { pGroup->pEnemyGroup->ubNumTroops_Turncoat--; + pGroup->pEnemyGroup->ubNumTroops--; pGroup->ubGroupSize--; return true; } break; case SOLDIER_CLASS_ELITE: - if ( pGroup->pEnemyGroup->ubNumElites_Turncoat ) + if ( pGroup->pEnemyGroup->ubNumElites_Turncoat && pGroup->ubGroupSize && pGroup->pEnemyGroup->ubNumElites ) { pGroup->pEnemyGroup->ubNumElites_Turncoat--; + pGroup->pEnemyGroup->ubNumElites--; pGroup->ubGroupSize--; return true; } diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 985c225d..370d71c3 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -10806,6 +10806,9 @@ void TurnCoatAttemptMessageBoxCallBack( UINT8 ubExitValue ) AddIntel( -intelbribeneeded, TRUE ); } + + // spend AP + DeductPoints( MercPtrs[gusSelectedSoldier], APBPConstants[AP_TALK], 0, UNTRIGGERED_INTERRUPT ); //ReduceAttackBusyCount(); } @@ -10864,7 +10867,7 @@ void HandleTurncoatAttempt( SOLDIERTYPE* pSoldier ) wcscpy( gzUserDefinedButton1, szTurncoatText[8] ); wcscpy( gzUserDefinedButton2, pEpcMenuStrings[4] ); - DoMessageBox( MSG_BOX_BASIC_STYLE, szTurncoatText[7], guiCurrentScreen, ( MSG_BOX_FLAG_GENERIC_TWO_BUTTONS | MSG_BOX_FLAG_DROPDOWN_1 ), + DoMessageBox( MSG_BOX_BASIC_STYLE, szTurncoatText[7], GAME_SCREEN, ( MSG_BOX_FLAG_GENERIC_TWO_BUTTONS | MSG_BOX_FLAG_DROPDOWN_1 ), TurnCoatAttemptMessageBoxCallBack, NULL ); } } @@ -10877,9 +10880,11 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) if ( ubExitValue == 1 ) { + DeductPoints( MercPtrs[gusSelectedSoldier], APBPConstants[AP_TALK], 0, UNTRIGGERED_INTERRUPT ); + if ( !gGameExternalOptions.fEnemyCanSurrender ) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szPrisonerTextStr[ SRT_PRISONER_INI_SETTING_OFF ] ); + StartCivQuote( MercPtrs[prisonerdialoguetargetID] ); return; } @@ -11026,20 +11031,14 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) } } } - // we offered to surrender OURSELVES TO the enemy, or are attempting to turn a soldier into a turncoat + // we offered to surrender OURSELVES TO the enemy else if ( ubExitValue == 2 ) { - // we cannot surrender to bandits, talk instead - if ( MercPtrs[prisonerdialoguetargetID]->bTeam == CREATURE_TEAM ) - { - // normal dialog - StartCivQuote( MercPtrs[prisonerdialoguetargetID] ); - return; - } + DeductPoints( MercPtrs[gusSelectedSoldier], APBPConstants[AP_TALK], 0, UNTRIGGERED_INTERRUPT ); - if ( !gGameExternalOptions.fPlayerCanAsktoSurrender ) + if ( !gGameExternalOptions.fPlayerCanAsktoSurrender || MercPtrs[prisonerdialoguetargetID]->bTeam == CREATURE_TEAM ) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szPrisonerTextStr[ SRT_PRISONER_INI_SETTING_OFF ] ); + StartCivQuote( MercPtrs[prisonerdialoguetargetID] ); return; } @@ -11086,6 +11085,8 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) // we distract the enemy by essentially talking them to death else if ( ubExitValue == 3 ) { + DeductPoints( MercPtrs[gusSelectedSoldier], APBPConstants[AP_TALK], 0, UNTRIGGERED_INTERRUPT ); + // Flugente: if we are disguised and talk to a non-profile NPC, we will continue to 'chat' with the enemy as long as we aren't ordered to do something else. // This way we can easily order our spies to 'distract' enemies if ( GetSoldier( &pSoldier, gusSelectedSoldier ) && @@ -11109,10 +11110,28 @@ void PrisonerSurrenderMessageBoxCallBack( UINT8 ubExitValue ) StartCivQuote( MercPtrs[prisonerdialoguetargetID] ); } } - else - { - // normal dialog - StartCivQuote( MercPtrs[ prisonerdialoguetargetID ] ); + else + { + // rftr: try to recruit a turncoat + if (gSkillTraitValues.fCOTurncoats == TRUE && + GetSoldier( &pSoldier, gusSelectedSoldier ) && + pSoldier->bTeam == gbPlayerNum && + MercPtrs[prisonerdialoguetargetID] && + MercPtrs[prisonerdialoguetargetID]->bTeam == ENEMY_TEAM && + MercPtrs[prisonerdialoguetargetID]->ubProfile == NO_PROFILE && + MercPtrs[prisonerdialoguetargetID]->aiData.bAlertStatus < STATUS_RED && + !MercPtrs[prisonerdialoguetargetID]->RecognizeAsCombatant( gusSelectedSoldier ) ) + { + MSYS_RemoveRegion(&(gMsgBox.BackRegion)); + pSoldier->UseSkill(SKILLS_CREATE_TURNCOAT, MercPtrs[prisonerdialoguetargetID]->sGridNo, MercPtrs[prisonerdialoguetargetID]->ubID); + // AP reduction is handled inside the turncoat attempt flow (TurnCoatAttemptMessageBoxCallBack) + } + else + { + // normal dialog + DeductPoints( MercPtrs[gusSelectedSoldier], APBPConstants[AP_TALK], 0, UNTRIGGERED_INTERRUPT ); + StartCivQuote(MercPtrs[prisonerdialoguetargetID]); + } } ReduceAttackBusyCount( ); @@ -11172,10 +11191,17 @@ void HandleSurrenderOffer( SOLDIERTYPE* pSoldier ) // remember the target's ID prisonerdialoguetargetID = pSoldier->ubID; - // open a dialogue box and see wether we really want to offer this, or just talk - wcscpy( gzUserDefinedButton[0], TacticalStr[ PRISONER_DEMAND_SURRENDER_STR ] ); + if (gGameExternalOptions.fEnemyCanSurrender && pSoldier->CanBeCaptured()) + { + // open a dialogue box and see whether we really want to offer this, or just talk + wcscpy(gzUserDefinedButton[0], TacticalStr[PRISONER_DEMAND_SURRENDER_STR]); + } + else + { + wcscpy(gzUserDefinedButton[0], TacticalStr[PRISONER_TALK_STR]); + } - if ( pSoldier->bTeam != CREATURE_TEAM ) + if (gGameExternalOptions.fPlayerCanAsktoSurrender && pSoldier->bTeam != CREATURE_TEAM) wcscpy( gzUserDefinedButton[1], TacticalStr[ PRISONER_OFFER_SURRENDER_STR ] ); else wcscpy( gzUserDefinedButton[1], TacticalStr[PRISONER_TALK_STR] ); @@ -11186,13 +11212,14 @@ void HandleSurrenderOffer( SOLDIERTYPE* pSoldier ) !pSoldier->RecognizeAsCombatant( gusSelectedSoldier ) ) { wcscpy( gzUserDefinedButton[2], TacticalStr[PRISONER_DISTRACT_STR] ); + wcscpy( gzUserDefinedButton[3], TacticalStr[gSkillTraitValues.fCOTurncoats == TRUE ? PRISONER_RECRUIT_TURNCOAT_STR : PRISONER_TALK_STR] ); } else { wcscpy( gzUserDefinedButton[2], TacticalStr[PRISONER_TALK_STR] ); + wcscpy( gzUserDefinedButton[3], TacticalStr[PRISONER_TALK_STR] ); } - wcscpy( gzUserDefinedButton[3], TacticalStr[ PRISONER_TALK_STR ] ); DoMessageBox( MSG_BOX_BASIC_MEDIUM_BUTTONS, TacticalStr[ PRISONER_OFFER_SURRENDER ], guiCurrentScreen, MSG_BOX_FLAG_GENERIC_FOUR_BUTTONS, PrisonerSurrenderMessageBoxCallBack, NULL ); } diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index bfee34df..bc764c21 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -18117,10 +18117,12 @@ BOOLEAN SOLDIERTYPE::CanUseSkill( INT8 iSkill, BOOLEAN fAPCheck, INT32 sGridNo ) // - a non-profile, not-already-turncoat enemy soldier // - enemy team not aware of us // - valid disguise + // - enough AP to talk { SOLDIERTYPE* pSoldier = SimpleFindSoldier( sGridNo, gsInterfaceLevel ); if ( pSoldier - && InPositionForTurncoatAttempt( pSoldier->ubID ) ) + && InPositionForTurncoatAttempt( pSoldier->ubID ) + && (!fAPCheck || EnoughPoints( this, APBPConstants[AP_TALK], 0, FALSE ) ) ) { canuse = TRUE; } @@ -18421,6 +18423,9 @@ STR16 SOLDIERTYPE::PrintSkillDesc( INT8 iSkill, INT32 sGridNo ) swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_STRATEGIC_SUSPICION] ); wcscat( skilldescarray, atStr ); + swprintf( atStr, pTraitSkillsDenialStrings[TEXT_SKILL_DENIAL_X_AP], APBPConstants[AP_TALK] ); + wcscat( skilldescarray, atStr ); + break; case SKILLS_ACTIVATE_TURNCOATS: @@ -21552,6 +21557,10 @@ void SOLDIERTYPE::OrderAllTurnCoatToSwitchSides() SOLDIERTYPE *pSoldier; INT32 cnt = gTacticalStatus.Team[ENEMY_TEAM].bFirstID; + // rftr: force the player to enter turn-based combat. this function already includes a check to see if we're already in combat, so no harm calling this. + // this also prevents a hang when activating a sector with 100% turncoats + EnterCombatMode(OUR_TEAM); + // run through list for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[ENEMY_TEAM].bLastID; ++cnt, ++pSoldier ) { @@ -23200,8 +23209,6 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid //lal // ATE; Check for normal civs... - BOOLEAN apsDeducted = FALSE; - if ( GetCivType( pTSoldier ) != CIV_TYPE_NA ) { //lal @@ -23212,18 +23219,17 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid } else { - // Deduct points from our guy.... - DeductPoints( this, sAPCost, 0, UNTRIGGERED_INTERRUPT ); - apsDeducted = TRUE; - - // Flugente: if we are talking to an enemy, we have the option to offer them surrender... - if ( (gGameExternalOptions.fEnemyCanSurrender || gGameExternalOptions.fPlayerCanAsktoSurrender) && pTSoldier->CanBeCaptured( ) ) + // Flugente: if we are talking to an enemy, we have the option to offer them surrender... we can also ask them to become a turncoat + if ( (gSkillTraitValues.fCOTurncoats || gGameExternalOptions.fEnemyCanSurrender || gGameExternalOptions.fPlayerCanAsktoSurrender) && pTSoldier->CanBeCaptured( ) ) { + // AP costs are handled inside the callback (PrisonerSurrenderMessageBoxCallBack) HandleSurrenderOffer( pTSoldier ); return(FALSE); } else if ( pTSoldier->sNonNPCTraderID > 0 ) { + DeductPoints( this, sAPCost, 0, UNTRIGGERED_INTERRUPT ); + UINT8 ubTownID = StrategicMap[CALCULATE_STRATEGIC_INDEX( pTSoldier->sSectorX, pTSoldier->sSectorY )].bNameId; // not possible if this guy is hostile towards us @@ -23251,6 +23257,7 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid // if we passed the above check, we can trade else { + DeductPoints( this, sAPCost, 0, UNTRIGGERED_INTERRUPT ); EnterShopKeeperInterfaceScreen_NonNPC( pTSoldier->sNonNPCTraderID, pTSoldier->ubID ); } @@ -23258,6 +23265,8 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid } else { + DeductPoints( this, sAPCost, 0, UNTRIGGERED_INTERRUPT ); + // Flugente: if this guy is a potential volunteer, we might be able to sway him HandleVolunteerRecruitment( this, pTSoldier ); @@ -23267,12 +23276,7 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid } } - // WANNE: This fixes the bug, that APs for talking are not always deducted. - if ( !apsDeducted ) - { - DeductPoints( this, sAPCost, 0, UNTRIGGERED_INTERRUPT ); - apsDeducted = TRUE; - } + DeductPoints( this, sAPCost, 0, UNTRIGGERED_INTERRUPT ); // Are we an EPC that is being escorted? if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) diff --git a/Utils/Text.h b/Utils/Text.h index 38de6b3a..86d3e68d 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -1113,6 +1113,7 @@ enum PRISONER_OFFER_SURRENDER_STR, PRISONER_DISTRACT_STR, PRISONER_TALK_STR, + PRISONER_RECRUIT_TURNCOAT_STR, // sevenfm: new disarm trap dialog, new messages for wrong mines when arming DISARM_DIALOG_DISARM, diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index fe001bb2..88f09abe 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -4060,6 +4060,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"我方缴械投降", //L"Offer surrender", L"转移", //L"Distract", L"交谈", + L"招募被策反的敌军", //L"Recruit turncoat", // TODO: confirm translation. copied from pTraitSkillsMenuStrings // added by sevenfm: disarm messagebox options, messages when arming wrong bomb L"拆除陷阱", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 4effc535..1b934412 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -4048,6 +4048,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Offer surrender", L"Distract", // TODO.Translate L"Talk", + L"Recruit Turncoat", // TODO: translate // TODO.Translate // added by sevenfm: disarm messagebox options, messages when arming wrong bomb diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 36f34d3a..62d605d4 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -4060,6 +4060,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Offer surrender", L"Distract", L"Talk", + L"Recruit Turncoat", // added by sevenfm: disarm messagebox options, messages when arming wrong bomb L"Disarm trap", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 207c92bf..9a04f4ae 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -4053,6 +4053,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Votre reddition", L"Distract", // TODO.Translate L"Parler", + L"Recruit Turncoat", // TODO: translate // added by sevenfm: disarm messagebox options, messages when arming wrong bomb L"Désamorcer le piège", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 95e42db4..a32a3748 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -4081,6 +4081,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Kapitulation anbieten", L"Ablenken", L"Sprechen", + L"Recruit Turncoat", // TODO: translate // added by sevenfm: disarm messagebox options, messages when arming wrong bomb L"Falle entschärfen", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 0bfad05d..a6ea87b6 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -4042,6 +4042,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Offer surrender", L"Distract", // TODO.Translate L"Talk", + L"Recruit Turncoat", // TODO: translate // TODO.Translate // added by sevenfm: disarm messagebox options, messages when arming wrong bomb diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 3a178d13..6c9f55e0 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -4053,6 +4053,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Offer surrender", L"Distract", // TODO.Translate L"Talk", + L"Recruit Turncoat", // TODO: translate // TODO.Translate // added by sevenfm: disarm messagebox options, messages when arming wrong bomb diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index c94028fe..8a4f404e 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -4060,6 +4060,7 @@ CHAR16 TacticalStr[][ MED_STRING_LENGTH ] = L"Предложить сдаться", L"Distract", // TODO.Translate L"Переговоры", + L"Recruit Turncoat", // TODO: translate // added by sevenfm: disarm messagebox options, messages when arming wrong bomb L"Обезвредить",