diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 849e20bc98..d821b9a7c2 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2249,44 +2249,44 @@ @ Used to active a different Max Move effects. .macro setmaxmoveeffect - various 0, VARIOUS_SET_MAX_MOVE_EFFECT + callnative BS_SetMaxMoveEffect .endm .macro setsteelsurge, failInstr:req - various 0, VARIOUS_SET_STEELSURGE + callnative BS_SetSteelsurge .4byte \failInstr .endm .macro damagenontypes - various 0, VARIOUS_DAMAGE_NON_TYPES + callnative BS_DamageNonTypes .endm .macro trysetstatus1, ptr:req - various 0, VARIOUS_TRY_SET_STATUS1 + callnative BS_TrySetStatus1 .4byte \ptr .endm .macro trysetstatus2, ptr:req - various 0, VARIOUS_TRY_SET_STATUS2 + callnative BS_TrySetStatus2 .4byte \ptr .endm .macro tryhealsixthhealth, ptr:req - various 0, VARIOUS_TRY_HEAL_SIXTH_HP + callnative BS_HealOneSixth .4byte \ptr .endm .macro tryrecycleberry, ptr:req - various 0, VARIOUS_TRY_RECYCLE_BERRY + callnative BS_TryRecycleBerry .4byte \ptr .endm .macro updatedynamax - various 0, VARIOUS_UPDATE_DYNAMAX + callnative BS_UpdateDynamax .endm .macro jumpiftargetdynamaxed, ptr:req - various 0, VARIOUS_JUMP_IF_TARGET_DYNAMAXED + callnative BS_JumpIfDynamaxed .4byte \ptr .endm diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 1fd19a3238..e9f2ae8472 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -73,8 +73,16 @@ u8 GetMaxMovePower(u16 move); bool32 IsMaxMove(u16 move); const u8 *GetMaxMoveName(u16 move); void ChooseDamageNonTypesString(u8 type); -u32 GetMaxMoveStatusEffect(u16 move); -u16 SetMaxMoveEffect(u16 move); + +void BS_UpdateDynamax(void); +void BS_SetMaxMoveEffect(void); +void BS_SetSteelsurge(void); +void BS_TrySetStatus1(void); +void BS_TrySetStatus2(void); +void BS_DamageNonTypes(void); +void BS_HealOneSixth(void); +void BS_TryRecycleBerry(void); +void BS_JumpIfDynamaxed(void); void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId); void CreateDynamaxTriggerSprite(u8, bool8); diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index bbc1cf99c5..2be15be581 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -262,16 +262,6 @@ #define VARIOUS_TRY_REVIVAL_BLESSING 170 #define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 171 #define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 172 -#define VARIOUS_SET_MAX_MOVE_EFFECT 173 -#define VARIOUS_JUMP_IF_NO_VALID_TARGETS 174 -#define VARIOUS_SET_STEELSURGE 175 -#define VARIOUS_DAMAGE_NON_TYPES 176 -#define VARIOUS_TRY_SET_STATUS1 177 -#define VARIOUS_TRY_SET_STATUS2 178 -#define VARIOUS_TRY_HEAL_SIXTH_HP 179 -#define VARIOUS_TRY_RECYCLE_BERRY 180 -#define VARIOUS_UPDATE_DYNAMAX 181 -#define VARIOUS_JUMP_IF_TARGET_DYNAMAXED 182 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index c2bf7949e3..1a2660d9a3 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -514,7 +514,7 @@ void ChooseDamageNonTypesString(u8 type) } // Returns the status effect that should be applied by a G-Max Move. -u32 GetMaxMoveStatusEffect(u16 move) +static u32 GetMaxMoveStatusEffect(u16 move) { u8 maxEffect = gBattleMoves[move].argument; switch (maxEffect) @@ -547,15 +547,48 @@ u32 GetMaxMoveStatusEffect(u16 move) } } -// Activates the secondary effect of a Max Move. -u16 SetMaxMoveEffect(u16 move) +// CALLNATIVE FUNCTIONS +#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; MEMBERS(__VA_ARGS__) const u8 nextInstr[0]; } *const cmd = (const void *)gBattlescriptCurrInstr +#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__) + +#define MEMBERS(...) VARARG_8(MEMBERS_, __VA_ARGS__) +#define MEMBERS_0() +#define MEMBERS_1(a) a; +#define MEMBERS_2(a, b) a; b; +#define MEMBERS_3(a, b, c) a; b; c; +#define MEMBERS_4(a, b, c, d) a; b; c; d; +#define MEMBERS_5(a, b, c, d, e) a; b; c; d; e; +#define MEMBERS_6(a, b, c, d, e, f) a; b; c; d; e; f; +#define MEMBERS_7(a, b, c, d, e, f, g) a; b; c; d; e; f; g; +#define MEMBERS_8(a, b, c, d, e, f, g, h) a; b; c; d; e; f; g; h; + +// Updates Dynamax HP multipliers and healthboxes. +void BS_UpdateDynamax(void) { + NATIVE_ARGS(); + u16 battler = gBattleScripting.battler; + struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]]; + + if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. + RecalcBattlerStats(battler, mon); + + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); + gBattlescriptCurrInstr = cmd->nextInstr; +} + +// Activates the secondary effect of a Max Move. +void BS_SetMaxMoveEffect(void) +{ + NATIVE_ARGS(); u16 effect = 0; - u8 maxEffect = gBattleMoves[move].argument; + u8 maxEffect = gBattleMoves[gCurrentMove].argument; // Don't continue if the move didn't land. if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) - return effect; + { + gBattlescriptCurrInstr = cmd->nextInstr; + return; + } switch (maxEffect) { @@ -859,7 +892,210 @@ u16 SetMaxMoveEffect(u16 move) break; } } - return effect; + + if (!effect) + gBattlescriptCurrInstr = cmd->nextInstr; +} + +// Sets up sharp steel on the target's side. +void BS_SetSteelsurge(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u8 targetSide = GetBattlerSide(gBattlerTarget); + if (gSideStatuses[targetSide] & SIDE_STATUS_STEELSURGE) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else + { + gSideStatuses[targetSide] |= SIDE_STATUS_STEELSURGE; + gSideTimers[targetSide].steelsurgeAmount = 1; + gBattlescriptCurrInstr = cmd->nextInstr; + } +} + +// Applies the status1 effect associated with a given G-Max Move. +// Could be expanded to function for any move. +void BS_TrySetStatus1(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u8 effect = 0; + u32 status1 = GetMaxMoveStatusEffect(gCurrentMove); + switch (status1) + { + case STATUS1_POISON: + if (CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + { + gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + effect++; + } + break; + case STATUS1_PARALYSIS: + if (CanBeParalyzed(gBattlerTarget)) + { + gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS; + gBattleCommunication[MULTISTRING_CHOOSER] = 3; + effect++; + } + break; + case STATUS1_SLEEP: + if (CanSleep(gBattlerTarget)) + { + #if B_SLEEP_TURNS >= GEN_5 + gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2); + #else + gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 4) + 3); + #endif + gBattleCommunication[MULTISTRING_CHOOSER] = 4; + effect++; + } + break; + } + if (effect) + { + gActiveBattler = gEffectBattler = gBattlerTarget; + BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->failInstr; + } +} + +// Applies the status2 effect associated with a given G-Max Move. +void BS_TrySetStatus2(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u8 effect = 0; + u32 status2 = GetMaxMoveStatusEffect(gCurrentMove); + switch (status2) + { + case STATUS2_CONFUSION: + if (CanBeConfused(gBattlerTarget)) + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattleCommunication[MULTIUSE_STATE] = 1; + effect++; + } + break; + case STATUS2_INFATUATION: + { + u8 atkGender = GetGenderFromSpeciesAndPersonality(gBattleMons[gBattlerAttacker].species, gBattleMons[gBattlerAttacker].personality); + u8 defGender = GetGenderFromSpeciesAndPersonality(gBattleMons[gBattlerTarget].species, gBattleMons[gBattlerTarget].personality); + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION) + && gBattleMons[gBattlerTarget].ability != ABILITY_OBLIVIOUS + && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL) + && atkGender != defGender + && atkGender != MON_GENDERLESS + && defGender != MON_GENDERLESS) + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker); + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gBattleCommunication[MULTIUSE_STATE] = 2; + effect++; + } + break; + } + case STATUS2_ESCAPE_PREVENTION: + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION)) + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION; + gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + effect++; + } + break; + case STATUS2_TORMENT: + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT) + && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL)) + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT; + gDisableStructs[gBattlerTarget].tormentTimer = 3; // 3 turns excluding current turn + gBattleCommunication[MULTISTRING_CHOOSER] = 3; + effect++; + } + break; + } + if (effect) + { + gEffectBattler = gBattlerTarget; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->failInstr; + } +} + +// Applies the endturn damage effect associated with the "Damage Non-" G-Max moves. +void BS_DamageNonTypes(void) +{ + NATIVE_ARGS(); + u8 side = GetBattlerSide(gBattlerAttacker); + gBattleMoveDamage = 0; + if (gSideTimers[side].damageNonTypesTimer + && !IS_BATTLER_OF_TYPE(gBattlerAttacker, gSideTimers[side].damageNonTypesType) + && IsBattlerAlive(gBattlerAttacker) + && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) + { + gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 6; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } + gBattlescriptCurrInstr = cmd->nextInstr; +} + +// Heals one-sixth of the target's HP, including for Dynamaxed targets. +void BS_HealOneSixth(void) +{ + NATIVE_ARGS(const u8* failInstr); + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 6; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + + if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) + gBattlescriptCurrInstr = cmd->failInstr; // fail + else + gBattlescriptCurrInstr = cmd->nextInstr; // can heal +} + +// Recycles the target's item if it is specifically holding a berry. +void BS_TryRecycleBerry(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u16* usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerTarget]][GetBattlerSide(gBattlerTarget)]; + if (gBattleMons[gBattlerTarget].item == ITEM_NONE + && gBattleStruct->changedItems[gBattlerTarget] == ITEM_NONE // Will not inherit an item + && ItemId_GetPocket(*usedHeldItem) == POCKET_BERRIES) + { + gLastUsedItem = *usedHeldItem; + *usedHeldItem = ITEM_NONE; + gBattleMons[gBattlerTarget].item = gLastUsedItem; + + BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); + MarkBattlerForControllerExec(gBattlerTarget); + + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->failInstr; + } +} + +// Goes to the jump instruction if the target is Dynamaxed. +void BS_JumpIfDynamaxed(void) +{ + NATIVE_ARGS(const u8 *jumpInstr); + if (IsDynamaxed(gBattlerTarget)) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; } // DYNAMAX TRIGGER: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 72961abaa0..93b151a98e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11304,219 +11304,6 @@ static void Cmd_various(void) } break; } - case VARIOUS_SET_MAX_MOVE_EFFECT: - { - VARIOUS_ARGS(); - if(SetMaxMoveEffect(gCurrentMove)) - return; - break; - } - case VARIOUS_SET_STEELSURGE: - { - VARIOUS_ARGS(const u8 *failInstr); - u8 targetSide = GetBattlerSide(gBattlerTarget); - if (gSideStatuses[targetSide] & SIDE_STATUS_STEELSURGE) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - gSideStatuses[targetSide] |= SIDE_STATUS_STEELSURGE; - gSideTimers[targetSide].steelsurgeAmount = 1; - gBattlescriptCurrInstr = cmd->nextInstr; - } - return; - } - case VARIOUS_DAMAGE_NON_TYPES: - { - VARIOUS_ARGS(); - side = GetBattlerSide(gBattlerAttacker); - gBattleMoveDamage = 0; - if (gSideTimers[side].damageNonTypesTimer - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, gSideTimers[side].damageNonTypesType) - && IsBattlerAlive(gBattlerAttacker) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) - { - gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 6; - if (gBattleMoveDamage == 0) - gBattleMoveDamage = 1; - } - break; - } - case VARIOUS_TRY_SET_STATUS1: - { - VARIOUS_ARGS(const u8 *failInstr); - u8 effect = 0; - u32 status1 = GetMaxMoveStatusEffect(gCurrentMove); - switch (status1) - { - case STATUS1_POISON: - if (CanBePoisoned(gBattlerAttacker, gBattlerTarget)) - { - gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON; - gBattleCommunication[MULTISTRING_CHOOSER] = 0; - effect++; - } - break; - case STATUS1_PARALYSIS: - if (CanBeParalyzed(gBattlerTarget)) - { - gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS; - gBattleCommunication[MULTISTRING_CHOOSER] = 3; - effect++; - } - break; - case STATUS1_SLEEP: - if (CanSleep(gBattlerTarget)) - { - #if B_SLEEP_TURNS >= GEN_5 - gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2); - #else - gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 4) + 3); - #endif - gBattleCommunication[MULTISTRING_CHOOSER] = 4; - effect++; - } - break; - } - if (effect) - { - gActiveBattler = gEffectBattler = gBattlerTarget; - BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); - MarkBattlerForControllerExec(gActiveBattler); - gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; - } - return; - } - case VARIOUS_TRY_SET_STATUS2: - { - VARIOUS_ARGS(const u8 *failInstr); - u8 effect = 0; - u32 status2 = GetMaxMoveStatusEffect(gCurrentMove); - switch (status2) - { - case STATUS2_CONFUSION: - if (CanBeConfused(gBattlerTarget)) - { - gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); - gBattleCommunication[MULTISTRING_CHOOSER] = 0; - gBattleCommunication[MULTIUSE_STATE] = 1; - effect++; - } - break; - case STATUS2_INFATUATION: - { - u8 atkGender = GetGenderFromSpeciesAndPersonality(gBattleMons[gBattlerAttacker].species, gBattleMons[gBattlerAttacker].personality); - u8 defGender = GetGenderFromSpeciesAndPersonality(gBattleMons[gBattlerTarget].species, gBattleMons[gBattlerTarget].personality); - if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION) - && gBattleMons[gBattlerTarget].ability != ABILITY_OBLIVIOUS - && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL) - && atkGender != defGender - && atkGender != MON_GENDERLESS - && defGender != MON_GENDERLESS) - { - gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker); - gBattleCommunication[MULTISTRING_CHOOSER] = 1; - gBattleCommunication[MULTIUSE_STATE] = 2; - effect++; - } - break; - } - case STATUS2_ESCAPE_PREVENTION: - if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION)) - { - gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION; - gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker; - gBattleCommunication[MULTISTRING_CHOOSER] = 2; - effect++; - } - break; - case STATUS2_TORMENT: - if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT) - && !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL)) - { - gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT; - gDisableStructs[gBattlerTarget].tormentTimer = 3; // 3 turns excluding current turn - gBattleCommunication[MULTISTRING_CHOOSER] = 3; - effect++; - } - break; - } - if (effect) - { - gEffectBattler = gBattlerTarget; - gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; - } - return; - } - case VARIOUS_TRY_HEAL_SIXTH_HP: - { - VARIOUS_ARGS(const u8 *failInstr); - // This heals based on Dynamaxed HP! - gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 6; - if (gBattleMoveDamage == 0) - gBattleMoveDamage = 1; - gBattleMoveDamage *= -1; - - if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) - gBattlescriptCurrInstr = cmd->failInstr; // fail - else - gBattlescriptCurrInstr = cmd->nextInstr; // can heal - return; - } - case VARIOUS_TRY_RECYCLE_BERRY: - { - VARIOUS_ARGS(const u8 *failInstr); - u16* usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerTarget]][GetBattlerSide(gBattlerTarget)]; - if (gBattleMons[gBattlerTarget].item == ITEM_NONE - && gBattleStruct->changedItems[gBattlerTarget] == ITEM_NONE // Will not inherit an item - && ItemId_GetPocket(*usedHeldItem) == POCKET_BERRIES) - { - gLastUsedItem = *usedHeldItem; - *usedHeldItem = ITEM_NONE; - gBattleMons[gBattlerTarget].item = gLastUsedItem; - - BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); - MarkBattlerForControllerExec(gBattlerTarget); - - gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; - } - return; - } - case VARIOUS_UPDATE_DYNAMAX: - { - VARIOUS_ARGS(); - u16 battler = gBattleScripting.battler; - if (GetBattlerSide(battler) == B_SIDE_PLAYER) - mon = &gPlayerParty[gBattlerPartyIndexes[battler]]; - else - mon = &gEnemyParty[gBattlerPartyIndexes[battler]]; - if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. - RecalcBattlerStats(battler, mon); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); - break; - } - case VARIOUS_JUMP_IF_TARGET_DYNAMAXED: - { - VARIOUS_ARGS(const u8 *jumpInstr); - if (IsDynamaxed(gBattlerTarget)) - gBattlescriptCurrInstr = cmd->jumpInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; - return; - } } // End of switch (cmd->id) gBattlescriptCurrInstr = cmd->nextInstr;