From f6089a00578ca9e5779f30b4b64f94349f9234e3 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Tue, 7 Mar 2023 22:34:57 +0100 Subject: [PATCH] Mega Evolution after switching and based on speed --- data/battle_scripts_1.s | 8 +-- data/scripts/debug.inc | 1 + include/battle.h | 3 +- include/battle_util.h | 1 - src/battle_main.c | 143 +++++++++++++++++++++---------------- src/battle_util.c | 5 +- src/data/trainer_parties.h | 16 ++--- test/mega_evolution.c | 38 ++++++++++ 8 files changed, 138 insertions(+), 77 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8b93edb78..ef4a739a5 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -561,7 +561,7 @@ BattleScript_ShellTrapSetUp:: playanimation BS_ATTACKER, B_ANIM_SHELL_TRAP_SETUP, NULL printstring STRINGID_PREPARESHELLTRAP waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_EffectShellTrap:: attackcanceler @@ -635,7 +635,7 @@ BattleScript_BeakBlastSetUp:: playanimation BS_ATTACKER, B_ANIM_BEAK_BLAST_SETUP, NULL printstring STRINGID_HEATUPBEAK waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_BeakBlastBurn:: setbyte cMULTISTRING_CHOOSER, 0 @@ -7825,7 +7825,7 @@ BattleScript_FocusPunchSetUp:: playanimation BS_ATTACKER, B_ANIM_FOCUS_PUNCH_SETUP printstring STRINGID_PKMNTIGHTENINGFOCUS waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_MegaEvolution:: printstring STRINGID_MEGAEVOREACTING @@ -7840,7 +7840,7 @@ BattleScript_MegaEvolutionAfeterString: printstring STRINGID_MEGAEVOEVOLVED waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER - end2 + end3 BattleScript_WishMegaEvolution:: printstring STRINGID_FERVENTWISHREACHED diff --git a/data/scripts/debug.inc b/data/scripts/debug.inc index 191fe6932..a6f68b1f7 100644 --- a/data/scripts/debug.inc +++ b/data/scripts/debug.inc @@ -55,6 +55,7 @@ Debug_FlagsNotSetMessage_Text: .string "'include/constants/overworld{UNDERSCORE}config.h'!$" Debug_Script_1:: + multi_fixed_2_vs_2 TRAINER_ROXANNE_2, Debug_FlagsNotSetMessage_Text, TRAINER_ROXANNE_3, Debug_FlagsNotSetMessage_Text, TRAINER_ROXANNE_4, 0 end Debug_Script_2:: diff --git a/include/battle.h b/include/battle.h index 83b552e7e..1ff9ed8ce 100644 --- a/include/battle.h +++ b/include/battle.h @@ -535,7 +535,7 @@ struct BattleStruct u8 dynamicMoveType; u8 wrappedBy[MAX_BATTLERS_COUNT]; u16 assistPossibleMoves[PARTY_SIZE * MAX_MON_MOVES]; // Each of mons can know max 4 moves. - u8 focusPunchBattlerId; + u8 focusPunchBattlers; // as bits u8 battlerPreventingSwitchout; u8 moneyMultiplier:6; u8 moneyMultiplierItem:1; @@ -656,6 +656,7 @@ struct BattleStruct u8 attackerBeforeBounce:2; u8 beatUpSlot:3; bool8 hitSwitchTargetFailed:1; + bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects. u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit. u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching) bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face. diff --git a/include/battle_util.h b/include/battle_util.h index 0661081e4..327ca35ac 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -195,7 +195,6 @@ bool32 IsHealBlockPreventingMove(u32 battler, u32 move); bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId); bool32 IsPartnerMonFromSameTrainer(u8 battlerId); u8 GetSplitBasedOnStats(u8 battlerId); -void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast); bool32 TestSheerForceFlag(u8 battler, u16 move); void TryRestoreStolenItems(void); bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item); diff --git a/src/battle_main.c b/src/battle_main.c index 9ea3b6644..4a9910e70 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4773,9 +4773,6 @@ static void SetActionsAndBattlersTurnOrder(void) turnOrderId++; } } - gBattleMainFunc = CheckMegaEvolutionBeforeTurn; - gBattleStruct->mega.battlerId = 0; - return; } else { @@ -4821,8 +4818,8 @@ static void SetActionsAndBattlersTurnOrder(void) } } } - gBattleMainFunc = CheckMegaEvolutionBeforeTurn; - gBattleStruct->mega.battlerId = 0; + gBattleMainFunc = CheckQuickClaw_CustapBerryActivation; + gBattleStruct->quickClawBattlerId = 0; } static void TurnValuesCleanUp(bool8 var0) @@ -4871,48 +4868,96 @@ void SpecialStatusesClear(void) memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); } -static void CheckMegaEvolutionBeforeTurn(void) +static void PopulateArrayWithBattlers(u8 *battlers) { - if (!(gHitMarker & HITMARKER_RUN)) - { - while (gBattleStruct->mega.battlerId < gBattlersCount) - { - gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId; - gBattleStruct->mega.battlerId++; - if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler] - && !(gProtectStructs[gActiveBattler].noValidMoves)) - { - struct Pokemon *mon = GetBattlerPartyData(gActiveBattler); + u32 i; + for (i = 0; i < gBattlersCount; i++) + battlers[i] = i; +} +static bool32 TryDoMegaEvosBeforeMoves(void) +{ + if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->mega.toEvolve) + { + u32 i; + struct Pokemon *mon; + u8 megaOrder[MAX_BATTLERS_COUNT]; + + PopulateArrayWithBattlers(megaOrder); + SortBattlersBySpeed(megaOrder, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleStruct->mega.toEvolve & gBitTable[megaOrder[i]] + && !(gProtectStructs[megaOrder[i]].noValidMoves)) + { + gActiveBattler = gBattlerAttacker = megaOrder[i]; gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]); gLastUsedItem = gBattleMons[gActiveBattler].item; + mon = GetBattlerPartyData(gActiveBattler); if (GetWishMegaEvolutionSpecies(GetMonData(mon, MON_DATA_SPECIES), GetMonData(mon, MON_DATA_MOVE1), GetMonData(mon, MON_DATA_MOVE2), GetMonData(mon, MON_DATA_MOVE3), GetMonData(mon, MON_DATA_MOVE4))) BattleScriptExecute(BattleScript_WishMegaEvolution); else BattleScriptExecute(BattleScript_MegaEvolution); - return; + return TRUE; } } } - #if B_MEGA_EVO_TURN_ORDER <= GEN_6 - gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; - #else - gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved + #if B_MEGA_EVO_TURN_ORDER >= GEN_7 + TryChangeTurnOrder(); // This will just do nothing if no mon has mega evolved. #endif + return FALSE; +} + +static bool32 TryDoMoveEffectsBeforeMoves(void) +{ + if (!(gHitMarker & HITMARKER_RUN)) + { + u32 i; + struct Pokemon *mon; + u8 battlers[MAX_BATTLERS_COUNT]; + + PopulateArrayWithBattlers(battlers); + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + if (!(gBattleStruct->focusPunchBattlers & gBitTable[battlers[i]]) + && !(gBattleMons[battlers[i]].status1 & STATUS1_SLEEP) + && !(gDisableStructs[battlers[i]].truantCounter) + && !(gProtectStructs[battlers[i]].noValidMoves)) + { + gBattleStruct->focusPunchBattlers |= gBitTable[battlers[i]]; + gActiveBattler = gBattlerAttacker = battlers[i]; + switch (gChosenMoveByBattler[gActiveBattler]) + { + case MOVE_FOCUS_PUNCH: + BattleScriptExecute(BattleScript_FocusPunchSetUp); + return TRUE; + case MOVE_BEAK_BLAST: + BattleScriptExecute(BattleScript_BeakBlastSetUp); + return TRUE; + case MOVE_SHELL_TRAP: + BattleScriptExecute(BattleScript_ShellTrapSetUp); + return TRUE; + } + } + } + } + + return FALSE; } // In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves static void TryChangeTurnOrder(void) { - s32 i, j; + u32 i, j; for (i = 0; i < gBattlersCount - 1; i++) { for (j = i + 1; j < gBattlersCount; j++) { - u8 battler1 = gBattlerByTurnOrder[i]; - u8 battler2 = gBattlerByTurnOrder[j]; + u32 battler1 = gBattlerByTurnOrder[i]; + u32 battler2 = gBattlerByTurnOrder[j]; + if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE) { @@ -4921,42 +4966,6 @@ static void TryChangeTurnOrder(void) } } } - gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; -} - -static void CheckChosenMoveForEffectsBeforeTurnStarts(void) -{ - u32 i; - - if (!(gHitMarker & HITMARKER_RUN)) - { - while (gBattleStruct->focusPunchBattlerId < gBattlersCount) - { - gActiveBattler = gBattlerAttacker = gBattleStruct->focusPunchBattlerId; - gBattleStruct->focusPunchBattlerId++; - if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) - && !(gDisableStructs[gBattlerAttacker].truantCounter) - && !(gProtectStructs[gActiveBattler].noValidMoves)) - { - switch (gChosenMoveByBattler[gActiveBattler]) - { - case MOVE_FOCUS_PUNCH: - BattleScriptExecute(BattleScript_FocusPunchSetUp); - return; - case MOVE_BEAK_BLAST: - BattleScriptExecute(BattleScript_BeakBlastSetUp); - return; - case MOVE_SHELL_TRAP: - BattleScriptExecute(BattleScript_ShellTrapSetUp); - return; - } - } - } - } - - gBattleMainFunc = CheckQuickClaw_CustapBerryActivation; - gBattleStruct->quickClawBattlerId = 0; } static void CheckQuickClaw_CustapBerryActivation(void) @@ -5011,6 +5020,8 @@ static void CheckQuickClaw_CustapBerryActivation(void) gCurrentTurnActionNumber = 0; gCurrentActionFuncId = gActionsByTurnOrder[0]; gBattleStruct->dynamicMoveType = 0; + gBattleStruct->effectsBeforeUsingMoveDone = FALSE; + gBattleStruct->focusPunchBattlers = 0; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattleStruct->ateBoost[i] = FALSE; @@ -5029,6 +5040,16 @@ static void RunTurnActionsFunctions(void) if (gBattleOutcome != 0) gCurrentActionFuncId = B_ACTION_FINISHED; + // Mega Evolve / Focus Punch-like moves after switching, items, running, but before using a move. + if (gCurrentActionFuncId == B_ACTION_USE_MOVE && !gBattleStruct->effectsBeforeUsingMoveDone) + { + if (TryDoMegaEvosBeforeMoves()) + return; + else if (TryDoMoveEffectsBeforeMoves()) + return; + gBattleStruct->effectsBeforeUsingMoveDone = TRUE; + } + *(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber; sTurnActionsFuncsTable[gCurrentActionFuncId](); diff --git a/src/battle_util.c b/src/battle_util.c index 9520e761f..e70973325 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1674,7 +1674,8 @@ void BattleScriptPushCursor(void) void BattleScriptPop(void) { - gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; + if (gBattleResources->battleScriptsStack->size != 0) + gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; } static bool32 IsGravityPreventingMove(u32 move) @@ -10567,7 +10568,7 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) { int i, j, currSpeed, currBattler; - u16 speeds[4] = {0}; + u16 speeds[MAX_BATTLERS_COUNT] = {0}; for (i = 0; i < gBattlersCount; i++) speeds[i] = GetBattlerTotalSpeedStat(battlers[i]); diff --git a/src/data/trainer_parties.h b/src/data/trainer_parties.h index 1759120b7..b4f617579 100644 --- a/src/data/trainer_parties.h +++ b/src/data/trainer_parties.h @@ -10301,9 +10301,9 @@ static const struct TrainerMonNoItemDefaultMoves sParty_MayRustboroTorchic[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne2[] = { { .iv = 255, - .lvl = 32, - .species = SPECIES_GOLEM, - .heldItem = ITEM_NONE, + .lvl = 5, + .species = SPECIES_AMPHAROS, + .heldItem = ITEM_AMPHAROSITE, .moves = {MOVE_PROTECT, MOVE_ROLLOUT, MOVE_MAGNITUDE, MOVE_EXPLOSION} }, { @@ -10333,8 +10333,8 @@ static const struct TrainerMonItemCustomMoves sParty_Roxanne3[] = { { .iv = 255, .lvl = 37, - .species = SPECIES_OMANYTE, - .heldItem = ITEM_NONE, + .species = SPECIES_MANECTRIC, + .heldItem = ITEM_MANECTITE, .moves = {MOVE_PROTECT, MOVE_ICE_BEAM, MOVE_ROCK_SLIDE, MOVE_SURF} }, { @@ -10370,9 +10370,9 @@ static const struct TrainerMonItemCustomMoves sParty_Roxanne3[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne4[] = { { .iv = 255, - .lvl = 42, - .species = SPECIES_OMASTAR, - .heldItem = ITEM_NONE, + .lvl = 100, + .species = SPECIES_GARDEVOIR, + .heldItem = ITEM_GARDEVOIRITE, .moves = {MOVE_PROTECT, MOVE_ICE_BEAM, MOVE_ROCK_SLIDE, MOVE_SURF} }, { diff --git a/test/mega_evolution.c b/test/mega_evolution.c index 13e9cd5b2..49861daaa 100644 --- a/test/mega_evolution.c +++ b/test/mega_evolution.c @@ -17,6 +17,44 @@ SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite") } } +DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - opponent faster") +{ + GIVEN { + PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3);} + OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(3);} + OPPONENT(SPECIES_WOBBUFFET) { Speed(4);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); } + } SCENE { + MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft); + MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!"); + MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft); + MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!"); + } +} + +DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - player faster") +{ + GIVEN { + PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3);} + OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2);} + OPPONENT(SPECIES_WOBBUFFET) { Speed(4);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); } + } SCENE { + MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft); + MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!"); + MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft); + MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!"); + } +} + SINGLE_BATTLE_TEST("Rayquaza can Mega Evolve knowing Dragon Ascent") { GIVEN {