diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 3994de217..cdec01dbd 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1473,6 +1473,10 @@ .4byte \ptr .endm + .macro handlemegaevo battler + various \battler, VARIOUS_HANDLE_MEGA_EVO + .endm + @ helpful macros .macro setstatchanger stat, stages, down setbyte sSTATCHANGER \stat | \stages << 4 | \down << 7 diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 99c071aac..46719eb3e 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -672,6 +672,7 @@ gBattleAnims_VariousTable:: @ 82C9320 .4byte Anim_Table_x14 .4byte Status_Ingrain .4byte Anim_WishHeal + .4byte General_MegaEvolution .align 2 gBattleAnims_Special:: @ 82C937C @@ -12054,6 +12055,9 @@ Anim_WishHeal: waitforvisualfinish createsprite gUnknown_08597274, 0x2, 1, 3, 10, 0, 0 end + +General_MegaEvolution: + end AnimScript_82D85A3: createvisualtask sub_8172E9C, 0x2 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index c2ea15b0e..0402dc526 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4610,6 +4610,16 @@ BattleScript_FocusPunchSetUp:: printstring STRINGID_PKMNTIGHTENINGFOCUS waitmessage 0x40 end2 + +BattleScript_MegaEvolution:: + printstring STRINGID_MEGAEVOREACTING + waitmessage 0x40 + playanimation BS_ATTACKER, B_ANIM_MEGA_EVOLUTION, NULL + waitanimation + handlemegaevo BS_ATTACKER + printstring STRINGID_MEGAEVOEVOLVED + waitmessage 0x40 + end2 BattleScript_MoveUsedIsAsleep:: printstring STRINGID_PKMNFASTASLEEP diff --git a/include/battle.h b/include/battle.h index c57c7760c..cedba63c0 100644 --- a/include/battle.h +++ b/include/battle.h @@ -598,6 +598,11 @@ struct BattleStruct bool8 ateBoost[MAX_BATTLERS_COUNT]; u32 debugAIFlags; bool8 notfirstTimeAIFlags; + u8 toMegaEvolve; // As flags using gBitTable. + u8 megaEvolvedPartyIds[2]; // As flags using gBitTable; + bool8 alreadyMegaEvolved[4]; // Array id is used for mon position. + u16 speciesToMegaEvolve[MAX_BATTLERS_COUNT]; + u8 megaEvoBattlerId; }; #define GET_MOVE_TYPE(move, typeArg) \ diff --git a/include/battle_controllers.h b/include/battle_controllers.h index f0ec036a9..50e37c917 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -84,6 +84,7 @@ enum // Special return values in gBattleBufferB from Battle Controller functions. #define RET_VALUE_LEVELED_UP 11 +#define RET_MEGA_EVOLUTION 0x80 struct UnusedControllerStruct { diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 43a368a60..f897158e8 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -271,5 +271,6 @@ extern const u8 BattleScript_SelectingNotAllowedMoveHealBlockInPalace[]; extern const u8 BattleScript_ToxicSpikesFree[]; extern const u8 BattleScript_StickyWebFree[]; extern const u8 BattleScript_StealthRockFree[]; +extern const u8 BattleScript_MegaEvolution[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_util.h b/include/battle_util.h index 69df37e66..cf445cfa5 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -88,5 +88,6 @@ u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 bat u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u8 abilityDef); u16 GetTypeModifier(u8 atkType, u8 defType); s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId); +bool32 CanMegaEvolve(u8 battlerId); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 270ac55e0..2b98412a3 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -60,6 +60,7 @@ #define B_ANIM_x14 0x14 #define B_ANIM_INGRAIN_HEAL 0x15 #define B_ANIM_WISH_HEAL 0x16 +#define B_ANIM_MEGA_EVOLUTION 0x17 // special animations table #define B_ANIM_LVL_UP 0x0 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 09f70e131..cdb3bae3f 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -99,6 +99,7 @@ #define VARIOUS_TRY_ELECTRIFY 48 #define VARIOUS_TRY_REFLECT_TYPE 49 #define VARIOUS_TRY_SOAK 50 +#define VARIOUS_HANDLE_MEGA_EVO 51 // atk80, dmg manipulation #define ATK80_DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index cdb0f34b0..aab8dd837 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -502,6 +502,8 @@ #define STRINGID_TERRAINBECOMESELECTRIC 499 #define STRINGID_TERRAINBECOMESPSYCHIC 500 #define STRINGID_TARGETELECTRIFIED 501 +#define STRINGID_MEGAEVOREACTING 502 +#define STRINGID_MEGAEVOEVOLVED 503 #define BATTLESTRINGS_COUNT 516 diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index b2c9fc9e8..df9b9c27d 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -109,6 +109,7 @@ // Gen6 hold effects #define HOLD_EFFECT_FAIRY_POWER 130 +#define HOLD_EFFECT_MEGA_STONE 131 // Gen7 hold effects #define HOLD_EFFECT_PROTECTIVE_PADS 150 diff --git a/include/constants/items.h b/include/constants/items.h index 294499c8b..2121220a2 100644 --- a/include/constants/items.h +++ b/include/constants/items.h @@ -239,7 +239,7 @@ #define ITEM_METAL_POWDER 223 #define ITEM_THICK_CLUB 224 #define ITEM_STICK 225 -#define ITEM_0E2 226 +#define ITEM_MEGA_STONE_TESTING 226 #define ITEM_0E3 227 #define ITEM_0E4 228 #define ITEM_0E5 229 diff --git a/include/pokemon.h b/include/pokemon.h index fa2842d88..0d836cc42 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -422,6 +422,8 @@ enum #define EVO_LEVEL_SHEDINJA 0x000e // Pokémon reaches the specified level (special value for Shedinja) #define EVO_BEAUTY 0x000f // Pokémon levels up with beauty ≥ specified value +#define EVO_MEGA_EVOLUTION 0xffff // Not an actual evolution, used to temporarily mega evolve in battle. + struct Evolution { u16 method; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index bfc709946..4c43c74c5 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1548,16 +1548,15 @@ static void OpponentHandleChooseMove(void) if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)) { - BattleAI_SetupAIData(0xF); chosenMoveId = BattleAI_ChooseMoveOrAction(); switch (chosenMoveId) { - case 5: + case AI_CHOICE_WATCH: BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0); break; - case 4: + case AI_CHOICE_FLEE: BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0); break; case 6: @@ -1572,7 +1571,10 @@ static void OpponentHandleChooseMove(void) if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } - BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); + if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it. + BtlController_EmitTwoReturnValues(1, 10 | RET_MEGA_EVOLUTION, (chosenMoveId) | (gBattlerTarget << 8)); + else + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); break; } OpponentBufferExecCompleted(); diff --git a/src/battle_main.c b/src/battle_main.c index b23930dd9..1f0dc57fe 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -166,6 +166,7 @@ static void SetActionsAndBattlersTurnOrder(void); static void sub_803CDF8(void); static bool8 AllAtActionConfirmed(void); static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void); +static void CheckMegaEvolutionBeforeTurn(void); static void FreeResetData_ReturnToOvOrDoEvolutions(void); static void ReturnFromBattleToOverworld(void); static void TryEvolvePokemon(void); @@ -4375,9 +4376,12 @@ static void HandleTurnActionSelectionState(void) case STATE_WAIT_ACTION_CASE_CHOSEN: if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC)))) { + bool32 shouldMegaEvolve; switch (gChosenActionByBattler[gActiveBattler]) { case B_ACTION_USE_MOVE: + shouldMegaEvolve = gBattleBufferB[gActiveBattler][1] & RET_MEGA_EVOLUTION; + gBattleBufferB[gActiveBattler][1] &= ~(RET_MEGA_EVOLUTION); switch (gBattleBufferB[gActiveBattler][1]) { case 3: @@ -4419,6 +4423,8 @@ static void HandleTurnActionSelectionState(void) *(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleBufferB[gActiveBattler][2]; gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[*(gBattleStruct->chosenMovePositions + gActiveBattler)]; *(gBattleStruct->moveTarget + gActiveBattler) = gBattleBufferB[gActiveBattler][3]; + if (shouldMegaEvolve) + gBattleStruct->toMegaEvolve |= gBitTable[gActiveBattler]; gBattleCommunication[gActiveBattler]++; } break; @@ -4844,8 +4850,8 @@ static void SetActionsAndBattlersTurnOrder(void) turnOrderId++; } } - gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; + gBattleMainFunc = CheckMegaEvolutionBeforeTurn; + gBattleStruct->megaEvoBattlerId = 0; return; } else @@ -4886,8 +4892,8 @@ static void SetActionsAndBattlersTurnOrder(void) } } } - gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; + gBattleMainFunc = CheckMegaEvolutionBeforeTurn; + gBattleStruct->megaEvoBattlerId = 0; } static void TurnValuesCleanUp(bool8 var0) @@ -4939,6 +4945,29 @@ static void SpecialStatusesClear(void) } } +static void CheckMegaEvolutionBeforeTurn(void) +{ + if (!(gHitMarker & HITMARKER_RUN)) + { + while (gBattleStruct->megaEvoBattlerId < gBattlersCount) + { + gActiveBattler = gBattlerAttacker = gBattleStruct->megaEvoBattlerId; + gBattleStruct->megaEvoBattlerId++; + if (gBattleStruct->toMegaEvolve & gBitTable[gActiveBattler] + && !(gProtectStructs[gActiveBattler].noValidMoves)) + { + gBattleStruct->toMegaEvolve &= ~(gBitTable[gActiveBattler]); + gLastUsedItem = gBattleMons[gActiveBattler].item; + BattleScriptExecute(BattleScript_MegaEvolution); + return; + } + } + } + + gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; + gBattleStruct->focusPunchBattlerId = 0; +} + static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) { u32 i; diff --git a/src/battle_message.c b/src/battle_message.c index 238b79b18..cc592b7d6 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -631,8 +631,6 @@ static const u8 sText_NotDoneYet[] = _("This move effect is not done yet!\p"); static const u8 sText_PkmnBlewAwayToxicSpikes[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nTOXIC SPIKES!"); static const u8 sText_PkmnBlewAwayStickyWeb[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nSTICKY WEB!"); static const u8 sText_PkmnBlewAwayStealthRock[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nSTEALTH ROCK!"); - -// To do. static const u8 sText_StickyWebUsed[] = _("A sticky web spreads out on the\nground around your team!"); static const u8 sText_QuashSuccess[] = _("The opposing {B_ATK_NAME_WITH_PREFIX}’s move was postponed!"); static const u8 sText_IonDelugeOn[] = _("A deluge of ions showers\nthe battlefield!"); @@ -642,12 +640,11 @@ static const u8 sText_TerrainBecomesGrassy[] = _("Grass grew to cover\nthe battl static const u8 sText_TerrainBecomesElectric[] = _("An electric current runs across\nthe battlefield!"); static const u8 sText_TerrainBecomesPsychic[] = _("The battlefield got weird!"); static const u8 sText_TargetElectrified[] = _("The opposing {B_ATK_NAME_WITH_PREFIX}’s moves\nhave been electrified!"); - -// New selection strings, they must end with "\p". -// Use {B_LAST_ITEM} and {B_CURRENT_MOVE}. static const u8 sText_AssaultVestDoesntAllow[] = _("The effects of the {B_LAST_ITEM} prevent status\nmoves from being used!\p"); static const u8 sText_GravityPreventsUsage[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} can’t use {B_CURRENT_MOVE}\nbecause of gravity!\p"); static const u8 sText_HealBlockPreventsUsage[] = _("The opposing {B_ATK_NAME_WITH_PREFIX} was\nprevented from healing!\p"); +static const u8 sText_MegaEvoReacting[] = _("{B_ATK_NAME_WITH_PREFIX}’s {B_LAST_ITEM} is reacting\nto {B_ATK_TRAINER_NAME}’s Mega Ring!"); +static const u8 sText_MegaEvoEvolved[] = _("{B_ATK_NAME_WITH_PREFIX} has Mega\nEvolved into Mega {B_BUFF1}!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { @@ -1142,6 +1139,8 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = sText_TerrainBecomesElectric, sText_TerrainBecomesPsychic, sText_TargetElectrified, + sText_MegaEvoReacting, + sText_MegaEvoEvolved, }; const u16 gTerrainStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bbd9715cb..e23342bb6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6119,6 +6119,7 @@ static void HandleTerrainMove(u32 moveEffect) static void atk76_various(void) { + struct Pokemon *mon; u8 side; s32 i, j; u8 data[10]; @@ -6588,6 +6589,28 @@ static void atk76_various(void) gBattlescriptCurrInstr += 7; } return; + case VARIOUS_HANDLE_MEGA_EVO: + if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT) + mon = &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]; + else + mon = &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]; + gBattleMons[gActiveBattler].species = gBattleStruct->speciesToMegaEvolve[gActiveBattler]; + SetMonData(mon, MON_DATA_SPECIES, &gBattleMons[gActiveBattler].species); + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gActiveBattler].species); + + CalculateMonStats(mon); + gBattleMons[gActiveBattler].hp = GetMonData(mon, MON_DATA_HP); + gBattleMons[gActiveBattler].maxHP = GetMonData(mon, MON_DATA_MAX_HP); + gBattleMons[gActiveBattler].attack = GetMonData(mon, MON_DATA_ATK); + gBattleMons[gActiveBattler].defense = GetMonData(mon, MON_DATA_DEF); + gBattleMons[gActiveBattler].speed = GetMonData(mon, MON_DATA_SPEED); + gBattleMons[gActiveBattler].spAttack = GetMonData(mon, MON_DATA_SPATK); + gBattleMons[gActiveBattler].spDefense = GetMonData(mon, MON_DATA_SPDEF); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], mon, HEALTHBOX_ALL); + + gBattleStruct->alreadyMegaEvolved[GetBattlerPosition(gActiveBattler)] = TRUE; + gBattleStruct->megaEvolvedPartyIds[GetBattlerSide(gActiveBattler)] |= gBitTable[gBattlerPartyIndexes[gActiveBattler]]; + break; } gBattlescriptCurrInstr += 3; diff --git a/src/battle_util.c b/src/battle_util.c index 75f4a293b..c6e40ddb8 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5572,3 +5572,60 @@ s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId) return dmg; } + +static bool32 IsPartnerMonFromSameTrainer(u8 battlerId) +{ + if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) + return FALSE; + else if (GetBattlerSide(battlerId) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + return FALSE; + else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + return FALSE; + else + return TRUE; +} + +bool32 CanMegaEvolve(u8 battlerId) +{ + u32 i; + u16 species, itemId; + u8 battlerPosition = GetBattlerPosition(battlerId); + u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId)); + + // Check if trainer already mega evolved a pokemon. + if (gBattleStruct->alreadyMegaEvolved[battlerPosition]) + return FALSE; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + if (IsPartnerMonFromSameTrainer(battlerId) && gBattleStruct->alreadyMegaEvolved[partnerPosition]) + return FALSE; + } + else + { + if (gBattleStruct->alreadyMegaEvolved[partnerPosition]) + return FALSE; + } + + // Check if the pokemon holds an appropriate item, + if (GetBattlerHoldEffect(battlerId, FALSE) != HOLD_EFFECT_MEGA_STONE) + return FALSE; + + // Check if there is an entry in the evolution table. + species = gBattleMons[battlerId].species; + itemId = gBattleMons[battlerId].item; + for (i = 0; i < EVOS_PER_MON; i++) + { + if (gEvolutionTable[species][i].method == EVO_MEGA_EVOLUTION + && gEvolutionTable[species][i].param == itemId) + { + gBattleStruct->speciesToMegaEvolve[battlerId] = gEvolutionTable[species][i].targetSpecies; + break; + } + } + + if (i == EVOS_PER_MON) + return FALSE; + + // All checks passed, the mon CAN mega evolve. + return TRUE; +} diff --git a/src/data/items.h b/src/data/items.h index 49f981faf..13702cda4 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -3649,10 +3649,10 @@ const struct Item gItems[] = .secondaryId = 0, }, { - .name = _("????????"), - .itemId = ITEM_NONE, + .name = _("Mega Stone"), + .itemId = ITEM_MEGA_STONE_TESTING, .price = 0, - .holdEffect = HOLD_EFFECT_NONE, + .holdEffect = HOLD_EFFECT_MEGA_STONE, .holdEffectParam = 0, .description = gDummyItemDescription, .importance = 0, diff --git a/src/data/pokemon/evolution.h b/src/data/pokemon/evolution.h index 8629ac9b1..4bd3df292 100644 --- a/src/data/pokemon/evolution.h +++ b/src/data/pokemon/evolution.h @@ -173,7 +173,8 @@ const struct Evolution gEvolutionTable[NUM_SPECIES][EVOS_PER_MON] = [SPECIES_VIGOROTH] = {{EVO_LEVEL, 36, SPECIES_SLAKING}}, [SPECIES_GULPIN] = {{EVO_LEVEL, 26, SPECIES_SWALOT}}, [SPECIES_WHISMUR] = {{EVO_LEVEL, 20, SPECIES_LOUDRED}}, - [SPECIES_LOUDRED] = {{EVO_LEVEL, 40, SPECIES_EXPLOUD}}, + [SPECIES_LOUDRED] = {{EVO_LEVEL, 40, SPECIES_EXPLOUD}, + {EVO_MEGA_EVOLUTION, ITEM_MEGA_STONE_TESTING, SPECIES_HO_OH}}, [SPECIES_CLAMPERL] = {{EVO_TRADE_ITEM, ITEM_DEEP_SEA_TOOTH, SPECIES_HUNTAIL}, {EVO_TRADE_ITEM, ITEM_DEEP_SEA_SCALE, SPECIES_GOREBYSS}}, [SPECIES_SHUPPET] = {{EVO_LEVEL, 37, SPECIES_BANETTE}},