Add B_RUN_TRAINER_BATTLE - players can run from Trainer Battles (#6456)

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
psf 2025-04-10 05:59:58 -07:00 committed by GitHub
parent 60a4e191ae
commit 17d851e1f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 100 additions and 6 deletions

View File

@ -10120,3 +10120,15 @@ BattleScript_SleepClausePreventsEnd::
printstring STRINGID_BLOCKEDBYSLEEPCLAUSE
waitmessage B_WAIT_TIME_LONG
end2
BattleScript_QuestionForfeitBattle::
printselectionstring STRINGID_QUESTIONFORFEITBATTLE
forfeityesnobox BS_ATTACKER
endselectionscript
BattleScript_ForfeitBattleGaveMoney::
getmoneyreward
printstring STRINGID_FORFEITBATTLEGAVEMONEY
waitmessage B_WAIT_TIME_LONG
end2

View File

@ -87,6 +87,8 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
void ModifyPersonalityForNature(u32 *personality, u32 newNature);
u32 GeneratePersonalityForGender(u32 gender, u32 species);
void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry);
bool32 CanPlayerForfeitNormalTrainerBattle(void);
bool32 DidPlayerForfeitNormalTrainerBattle(void);
extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE];

View File

@ -856,5 +856,7 @@ extern const u8 BattleScript_EffectSpicyExtract[];
extern const u8 BattleScript_DamageToQuarterTargetHP[];
extern const u8 BattleScript_EffectFickleBeam[];
extern const u8 BattleScript_FickleBeamDoubled[];
extern const u8 BattleScript_QuestionForfeitBattle[];
extern const u8 BattleScript_ForfeitBattleGaveMoney[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -256,6 +256,7 @@
#define B_HIDE_HEALTHBOX_IN_ANIMS TRUE // If set to TRUE, hides healthboxes during move animations.
#define B_WAIT_TIME_MULTIPLIER 16 // This determines how long text pauses in battle last. Vanilla is 16. Lower values result in faster battles.
#define B_QUICK_MOVE_CURSOR_TO_RUN FALSE // If set to TRUE, pushing B in the battle options against a wild encounter will move the cursor to the run option
#define B_RUN_TRAINER_BATTLE TRUE // If set to TRUE, players can run from Trainer battles. This is treated as a whiteout.
#define B_MOVE_DESCRIPTION_BUTTON L_BUTTON // If set to a button other than B_LAST_USED_BALL_BUTTON, pressing this button will open the move description menu
#define B_SHOW_USELESS_Z_MOVE_INFO FALSE // If set to TRUE, Z-moves without additional effects like newer gen status moves will say "no additional effect"
#define B_ANIMATE_MON_AFTER_KO TRUE // If set to TRUE, if a Pokémon on the opposite site faints, the non-fainted Pokemon will display a victory animation.

View File

@ -732,8 +732,10 @@
#define STRINGID_PKMNGIGANTAMAXED 730
#define STRINGID_TIMETODYNAMAX 731
#define STRINGID_TIMETOGIGANTAMAX 732
#define STRINGID_QUESTIONFORFEITBATTLE 733
#define STRINGID_FORFEITBATTLEGAVEMONEY 734
#define BATTLESTRINGS_COUNT 733
#define BATTLESTRINGS_COUNT 735
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@ -2414,6 +2414,8 @@ extern const u8 gText_PM[];
// FRLG whiteout
extern const u8 gText_PlayerScurriedToCenter[];
extern const u8 gText_PlayerScurriedBackHome[];
extern const u8 gText_PlayerRegroupCenter[];
extern const u8 gText_PlayerRegroupHome[];
extern const u8 gText_Relearn[]; // move relearner from summary screen
extern const u8 gText_Rename[]; // change nickname from summary screen

View File

@ -4230,6 +4230,15 @@ static void HandleTurnActionSelectionState(void)
gChosenActionByBattler[battler] = B_ACTION_NOTHING_FAINTED; // Not fainted, but it cannot move, because of the throwing ball.
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else if (B_RUN_TRAINER_BATTLE
&& gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& position == B_POSITION_PLAYER_RIGHT
&& gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] == B_ACTION_RUN
&& gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] != B_ACTION_NOTHING_FAINTED)
{
gChosenActionByBattler[battler] = B_ACTION_USE_MOVE;
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else
{
gBattleStruct->itemPartyIndex[battler] = PARTY_SIZE;
@ -4413,9 +4422,17 @@ static void HandleTurnActionSelectionState(void)
gBattleStruct->stateIdAfterSelScript[battler] = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else if (CanPlayerForfeitNormalTrainerBattle() && gBattleResources->bufferB[battler][1] == B_ACTION_RUN)
{
gSelectionBattleScripts[battler] = BattleScript_QuestionForfeitBattle;
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT_MAY_RUN;
gBattleStruct->selectionScriptFinished[battler] = FALSE;
gBattleStruct->stateIdAfterSelScript[battler] = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
&& gBattleResources->bufferB[battler][1] == B_ACTION_RUN)
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
&& gBattleResources->bufferB[battler][1] == B_ACTION_RUN)
{
BattleScriptExecute(BattleScript_PrintCantRunFromTrainer);
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
@ -5492,6 +5509,11 @@ static void HandleEndTurn_RanFromBattle(void)
gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited;
gBattleOutcome = B_OUTCOME_FORFEITED;
}
else if (CanPlayerForfeitNormalTrainerBattle())
{
gBattlescriptCurrInstr = BattleScript_ForfeitBattleGaveMoney;
gBattleOutcome = B_OUTCOME_FORFEITED;
}
else
{
switch (gProtectStructs[gBattlerAttacker].fleeType)
@ -6114,3 +6136,25 @@ static s32 Factorial(s32 n)
f *= i;
return f;
}
bool32 CanPlayerForfeitNormalTrainerBattle(void)
{
if (!B_RUN_TRAINER_BATTLE)
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_INVALID)
return FALSE;
return (gBattleTypeFlags & BATTLE_TYPE_TRAINER);
}
bool32 DidPlayerForfeitNormalTrainerBattle(void)
{
if (!CanPlayerForfeitNormalTrainerBattle())
return FALSE;
return (gBattleOutcome == B_OUTCOME_FORFEITED);
}

View File

@ -896,6 +896,8 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_PKMNGIGANTAMAXED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} grew huge\ninto its Gigantamax form!"),
[STRINGID_TIMETODYNAMAX] = COMPOUND_STRING("Time to Dynamax!"),
[STRINGID_TIMETOGIGANTAMAX] = COMPOUND_STRING("Time to Gigantamax!"),
[STRINGID_QUESTIONFORFEITBATTLE] = COMPOUND_STRING("Would you like to give up on this battle and quit now? Quitting the battle is the same as losing the battle."),
[STRINGID_FORFEITBATTLEGAVEMONEY] = COMPOUND_STRING("You gave ¥{B_BUFF1} to the winner…{PAUSE_UNTIL_PRESS}"),
};
const u16 gTrainerUsedItemStringIds[] =

View File

@ -1084,9 +1084,9 @@ void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript)
gSelectedObjectEvent = trainerObjEventId;
gSpecialVar_LastTalked = gObjectEvents[trainerObjEventId].localId;
if (gApproachingTrainerId == 0)
if (gApproachingTrainerId == 0)
TrainerBattleLoadArgs(trainerScript + 1);
else
else
TrainerBattleLoadArgsSecondTrainer(trainerScript + 1);
BattleSetup_ConfigureTrainerBattle(trainerScript + 1);
@ -1264,6 +1264,10 @@ static void CB2_EndTrainerBattle(void)
else
SetMainCallback2(CB2_WhiteOut);
}
else if (DidPlayerForfeitNormalTrainerBattle())
{
SetMainCallback2(CB2_WhiteOut);
}
else
{
SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);

View File

@ -562,6 +562,10 @@ bool32 TryRunFromBattle(u32 battler)
{
effect++;
}
else if (CanPlayerForfeitNormalTrainerBattle())
{
effect++;
}
else
{
u8 runningFromBattler = BATTLE_OPPOSITE(battler);

View File

@ -39,6 +39,7 @@
#include "constants/rgb.h"
#include "trainer_hill.h"
#include "fldeff.h"
#include "battle.h"
static void Task_ExitNonAnimDoor(u8);
static void Task_ExitNonDoor(u8);
@ -1342,6 +1343,21 @@ enum {
FRLG_WHITEOUT_HEAL_SCRIPT,
};
static const u8 *GenerateRecoveryMessage(u8 taskId)
{
bool32 forfeitTrainer = DidPlayerForfeitNormalTrainerBattle();
bool32 destinationIsPlayersHouse = (gTasks[taskId].tIsPlayerHouse == TRUE);
if (forfeitTrainer && destinationIsPlayersHouse)
return gText_PlayerRegroupHome;
else if (forfeitTrainer && !destinationIsPlayersHouse)
return gText_PlayerRegroupCenter;
else if (!forfeitTrainer && destinationIsPlayersHouse)
return gText_PlayerScurriedBackHome;
else
return gText_PlayerScurriedToCenter;
}
static void Task_RushInjuredPokemonToCenter(u8 taskId)
{
u32 windowId;
@ -1361,7 +1377,8 @@ static void Task_RushInjuredPokemonToCenter(u8 taskId)
break;
case FRLG_WHITEOUT_PRINT_MSG:
{
const u8 *recoveryMessage = gTasks[taskId].tIsPlayerHouse == TRUE ? gText_PlayerScurriedBackHome : gText_PlayerScurriedToCenter;
const u8 *recoveryMessage = GenerateRecoveryMessage(taskId);
if (PrintWhiteOutRecoveryMessage(taskId, recoveryMessage, 2, 8))
{
ObjectEventTurn(&gObjectEvents[gPlayerAvatar.objectEventId], DIR_NORTH);

View File

@ -806,6 +806,8 @@ const u8 gText_ThankYou[] = _("THANK YOU");
const u8 gText_ByeBye[] = _("BYE-BYE!");
const u8 gText_PlayerScurriedToCenter[] = _("{PLAYER} scurried to a POKéMON CENTER,\nprotecting the exhausted and fainted\nPOKéMON from further harm…\p");
const u8 gText_PlayerScurriedBackHome[] = _("{PLAYER} scurried back home, protecting\nthe exhausted and fainted POKéMON from\nfurther harm…\p");
const u8 gText_PlayerRegroupCenter[] = _("{PLAYER} went running back to a POKéMON CENTER\nto regroup and reconsider their battle\lstrategy…\p");
const u8 gText_PlayerRegroupHome[] = _("{PLAYER} went running back home to regroup\nand reconsider their battle strategy…\p");
const u8 gText_HatchedFromEgg[] = _("{STR_VAR_1} hatched from the EGG!");
const u8 gText_NicknameHatchPrompt[] = _("Would you like to nickname the newly\nhatched {STR_VAR_1}?");
ALIGNED(4) const u8 gText_ReadyPickBerry[] = _("Are you ready to BERRY-CRUSH?\nPlease pick a BERRY for use.\p");