diff --git a/Allow-Running-From-Trainer-Battles.md b/Allow-Running-From-Trainer-Battles.md new file mode 100644 index 0000000..4c14dd9 --- /dev/null +++ b/Allow-Running-From-Trainer-Battles.md @@ -0,0 +1,227 @@ +By devolov +**Goal:** Allow running from a battle with an NPC trainer. When you run from them, you can still talk to them to battle, they just won't `see` you until you either change maps or walk in front of someone else who can battle you. + +![bar1](https://user-images.githubusercontent.com/36523934/230449381-addb805f-2a54-43c4-b96e-c97f13a3c463.gif) + + +```diff +------------------------------ src/battle_setup.c ------------------------------ +index 03bc0a08e7..e17db26d14 100644 +@@ -113,8 +113,9 @@ EWRAM_DATA static u8 *sTrainerBattleEndScript = NULL; + EWRAM_DATA static u8 *sTrainerABattleScriptRetAddr = NULL; + EWRAM_DATA static u8 *sTrainerBBattleScriptRetAddr = NULL; + EWRAM_DATA static bool8 sShouldCheckTrainerBScript = FALSE; + EWRAM_DATA static u8 sNoOfPossibleTrainerRetScripts = 0; ++EWRAM_DATA static u32 sPrevTrainerSeeing = 0; + + // The first transition is used if the enemy pokemon are lower level than our pokemon. + // Otherwise, the second transition is used. + static const u8 sBattleTransitionTable_Wild[][2] = +@@ -1271,9 +1272,13 @@ void SetUpTwoTrainersBattle(void) + + bool32 GetTrainerFlagFromScriptPointer(const u8 *data) + { + u32 flag = TrainerBattleLoadArg16(data + 2); +- return FlagGet(TRAINER_FLAGS_START + flag); ++ if (flag != sPrevTrainerSeeing){ ++ sPrevTrainerSeeing = flag; ++ FlagClear(FLAG_RAN_FROM_TRAINER); ++ } ++ return (FlagGet(TRAINER_FLAGS_START + flag) || FlagGet(FLAG_RAN_FROM_TRAINER)); + } + + // Set trainer's movement type so they stop and remain facing that direction + // Note: Only for trainers who are spoken to directly +@@ -1298,8 +1303,13 @@ bool8 GetTrainerFlag(void) + else + return FlagGet(GetTrainerAFlag()); + } + ++static void SetBattledTrainersSeeFlag(void) ++{ ++ FlagSet(FLAG_RAN_FROM_TRAINER); ++} ++ + static void SetBattledTrainersFlags(void) + { + if (gTrainerBattleOpponent_B != 0) + FlagSet(GetTrainerBFlag()); +@@ -1412,10 +1422,16 @@ static void CB2_EndTrainerBattle(void) + { + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); + if (!InBattlePyramid() && !InTrainerHillChallenge()) + { +- RegisterTrainerInMatchCall(); +- SetBattledTrainersFlags(); ++ if (gBattleOutcome == B_OUTCOME_RAN){ ++ SetBattledTrainersSeeFlag(); ++ } ++ else ++ { ++ RegisterTrainerInMatchCall(); ++ SetBattledTrainersFlags(); ++ } + } + } + } +``` + +```diff +------------------------------ src/trainer_see.c ------------------------------ +index 7b7533a337..642b6bd0f1 100644 +@@ -421,16 +421,14 @@ static u8 CheckTrainer(u8 objectEventId) + { + if (GetHillTrainerFlag(objectEventId)) + return 0; + } +- else +- { +- if (GetTrainerFlagFromScriptPointer(scriptPtr)) +- return 0; +- } + + approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]); + + if (approachDistance != 0) + { ++ if (GetTrainerFlagFromScriptPointer(scriptPtr)) ++ return 0; + if (scriptPtr[1] == TRAINER_BATTLE_DOUBLE + || scriptPtr[1] == TRAINER_BATTLE_REMATCH_DOUBLE +``` + +```diff +------------------------------- src/event_data.c ------------------------------- +index 50f6f68da4..4a00bcac40 100644 +@@ -44,8 +44,9 @@ void InitEventData(void) + void ClearTempFieldEventData(void) + { + memset(gSaveBlock1Ptr->flags + (TEMP_FLAGS_START / 8), 0, TEMP_FLAGS_SIZE); + memset(gSaveBlock1Ptr->vars + ((TEMP_VARS_START - VARS_START) * 2), 0, TEMP_VARS_SIZE); ++ FlagClear(FLAG_RAN_FROM_TRAINER); + FlagClear(FLAG_SYS_ENC_UP_ITEM); + FlagClear(FLAG_SYS_ENC_DOWN_ITEM); + FlagClear(FLAG_SYS_USE_STRENGTH); + FlagClear(FLAG_SYS_CTRL_OBJ_DELETE); +``` + +```diff +-------------------------- include/constants/flags.h -------------------------- +index 7f36cacc8f..2a25e074f6 100644 +@@ -1241,9 +1241,9 @@ +-#define FLAG_UNUSED_0x4AC 0x4AC // Unused Flag ++#define FLAG_RAN_FROM_TRAINER 0x4AC + #define FLAG_UNUSED_0x4AD 0x4AD // Unused Flag + #define FLAG_UNUSED_0x4AE 0x4AE // Unused Flag + #define FLAG_UNUSED_0x4AF 0x4AF // Unused Flag + #define FLAG_UNUSED_0x4B0 0x4B0 // Unused Flag +``` + +```diff +------------------------------ src/battle_main.c ------------------------------ +@@ -4552,14 +4577,26 @@ static void HandleTurnActionSelectionState(void) + *(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE; + *(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN; + return; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER + && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) + && gBattleBufferB[gActiveBattler][1] == B_ACTION_RUN) + { +- BattleScriptExecute(BattleScript_PrintCantRunFromTrainer); +- gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; ++ gBattleCommunication[gActiveBattler]++; + } + else if (IsRunningFromBattleImpossible() != BATTLE_RUN_SUCCESS + && gBattleBufferB[gActiveBattler][1] == B_ACTION_RUN) + { +``` + +## Make Important Trainer Battles Ones That You Still Can't Run From +Some battles (Battle Frontier, Rival, Elite Four), may be ones that you don't want to run from. The following tweak can be added in `battle_main.c`: + +```diff +static void HandleEndTurn_BattleLost(void); +static void HandleEndTurn_RanFromBattle(void); +static void HandleEndTurn_MonFled(void); +static void HandleEndTurn_FinishBattle(void); +static void SpriteCB_UnusedBattleInit(struct Sprite *sprite); +static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite); ++static bool8 IsTrainerCantRunFrom(void); + +EWRAM_DATA u16 gBattle_BG0_X = 0; +``` + +```diff + return BATTLE_RUN_FORBIDDEN; + } + return BATTLE_RUN_SUCCESS; +} + ++static u8 IsTrainerCantRunFrom(void){ ++ u8 trainerClass; ++ if (FlagGet(FLAG_NUZLOCKE)) ++ return BATTLE_RUN_FORBIDDEN; ++ if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL)) ++ return BATTLE_RUN_FORBIDDEN; ++ trainerClass = gTrainers[gTrainerBattleOpponent_A].trainerClass; ++ switch (trainerClass) ++ { ++ case TRAINER_CLASS_AQUA_LEADER: ++ case TRAINER_CLASS_MAGMA_LEADER: ++ case TRAINER_CLASS_TEAM_AQUA: ++ case TRAINER_CLASS_TEAM_MAGMA: ++ case TRAINER_CLASS_AQUA_ADMIN: ++ case TRAINER_CLASS_MAGMA_ADMIN: ++ case TRAINER_CLASS_LEADER: ++ case TRAINER_CLASS_CHAMPION: ++ case TRAINER_CLASS_RIVAL: ++ case TRAINER_CLASS_ELITE_FOUR: ++ return BATTLE_RUN_FORBIDDEN; ++ default: ++ return BATTLE_RUN_SUCCESS; ++ } ++} ++ +void SwitchPartyOrder(u8 battler) +{ + s32 i; +``` +```diff + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER + && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) + && gBattleBufferB[gActiveBattler][1] == B_ACTION_RUN) + { +- gBattleCommunication[gActiveBattler]++; ++ if (IsTrainerCantRunFrom()) ++ { ++ BattleScriptExecute(BattleScript_PrintCantRunFromTrainer); ++ gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; ++ } ++ else ++ { ++ gBattleCommunication[gActiveBattler]++; ++ } ++ } +``` + +For proper wording, in `battle_messages.c`: +```diff +----------------------------- src/battle_message.c ----------------------------- +index 11f7f8d5f3..8cc21e0fc5 100644 +@@ -332,9 +332,10 @@ static const u8 sText_PlayerLostToTwo[] = _("Player lost to {B_LINK_OPPONENT1_NA + static const u8 sText_PlayerBattledToDrawLinkTrainer[] = _("Player battled to a draw against\n{B_LINK_OPPONENT1_NAME}!"); + static const u8 sText_PlayerBattledToDrawVsTwo[] = _("Player battled to a draw against\n{B_LINK_OPPONENT1_NAME} and {B_LINK_OPPONENT2_NAME}!"); + static const u8 sText_WildFled[] = _("{PLAY_SE SE_FLEE}{B_LINK_OPPONENT1_NAME} fled!"); + static const u8 sText_TwoWildFled[] = _("{PLAY_SE SE_FLEE}{B_LINK_OPPONENT1_NAME} and\n{B_LINK_OPPONENT2_NAME} fled!"); +-static const u8 sText_NoRunningFromTrainers[] = _("No! There's no running\nfrom a TRAINER battle!\p"); ++static const u8 sText_NoRunningFromTrainers[] = _("No! There's no running\nfrom this TRAINER battle!\p"); + static const u8 sText_CantEscape[] = _("Can't escape!\p"); + static const u8 sText_DontLeaveBirch[] = _("PROF. BIRCH: Don't leave me like this!\p"); + static const u8 sText_ButNothingHappened[] = _("But nothing happened!"); + static const u8 sText_ButItFailed[] = _("But it failed!"); +``` + + + +