From 4cb03d4add60b4653edfb04374225ec86a785370 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:45:58 +0100 Subject: [PATCH] Adds Raging Bull and various fixes (#3552) * Adds Raging Bull and various fixes * fix compiling on abgcc * syrup bomb anim fix * sticky syrup anim in link battles * fix sticky syrup in link battles * fix battler index --------- Co-authored-by: Bassoonian --- data/battle_scripts_1.s | 5 +- include/battle_controllers.h | 2 +- include/constants/battle_move_effects.h | 3 +- src/battle_anim_new.c | 10 +- src/battle_controllers.c | 7 +- src/battle_main.c | 7 ++ src/battle_script_commands.c | 13 +- src/battle_util.c | 12 +- src/data/battle_moves.h | 2 +- test/battle/ability/zero_to_hero.c | 40 ++++++ test/battle/move_effect/brick_break.c | 160 ++++++++++++++++++++++++ 11 files changed, 237 insertions(+), 24 deletions(-) create mode 100644 test/battle/move_effect/brick_break.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a6c9d54eed..a61a0e3ede 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -441,6 +441,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectHit @ EFFECT_IVY_CUDGEL .4byte BattleScript_EffectMaxMove @ EFFECT_MAX_MOVE .4byte BattleScript_EffectGlaiveRush @ EFFECT_GLAIVE_RUSH + .4byte BattleScript_EffectBrickBreak @ EFFECT_RAGING_BULL BattleScript_EffectGlaiveRush:: call BattleScript_EffectHit_Ret @@ -450,9 +451,7 @@ BattleScript_EffectGlaiveRush:: BattleScript_EffectSyrupBomb:: setmoveeffect MOVE_EFFECT_SYRUP_BOMB - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - goto BattleScript_MoveEnd + goto BattleScript_EffectHit BattleScript_SyrupBombActivates:: printstring STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP diff --git a/include/battle_controllers.h b/include/battle_controllers.h index ea7e63525f..5f3bd8316e 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -251,7 +251,7 @@ void BtlController_EmitDrawPartyStatusSummary(u32 battler, u32 bufferId, struct void BtlController_EmitHidePartyStatusSummary(u32 battler, u32 bufferId); void BtlController_EmitEndBounceEffect(u32 battler, u32 bufferId); void BtlController_EmitSpriteInvisibility(u32 battler, u32 bufferId, bool8 isInvisible); -void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, u16 argument); +void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, struct DisableStruct* disableStructPtr, u16 argument); void BtlController_EmitLinkStandbyMsg(u32 battler, u32 bufferId, u8 mode, bool32 record); void BtlController_EmitResetActionMoveSelection(u32 battler, u32 bufferId, u8 caseId); void BtlController_EmitEndLinkBattle(u32 battler, u32 bufferId, u8 battleOutcome); diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 4e21557766..1fb300347e 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -418,7 +418,8 @@ #define EFFECT_IVY_CUDGEL 412 #define EFFECT_MAX_MOVE 413 #define EFFECT_GLAIVE_RUSH 414 +#define EFFECT_RAGING_BULL 415 -#define NUM_BATTLE_MOVE_EFFECTS 415 +#define NUM_BATTLE_MOVE_EFFECTS 416 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/battle_anim_new.c b/src/battle_anim_new.c index 99f58d6888..281b3087cc 100644 --- a/src/battle_anim_new.c +++ b/src/battle_anim_new.c @@ -9152,18 +9152,12 @@ void AnimTask_GetWeatherToSet(u8 taskId) void AnimTask_SyrupBomb(u8 taskId) { struct Pokemon *party = GetBattlerParty(gBattleAnimAttacker); - u32 isShiny = IsMonShiny(&party[gBattlerPartyIndexes[gBattleAnimAttacker]]); - - gDisableStructs[gBattleAnimTarget].syrupBombIsShiny = isShiny; - gBattleAnimArgs[0] = isShiny; + gBattleAnimArgs[0] = IsMonShiny(&party[gBattlerPartyIndexes[gBattleAnimAttacker]]); DestroyAnimVisualTask(taskId); } void AnimTask_StickySyrup(u8 taskId) { - if (gDisableStructs[gBattleAnimTarget].syrupBombIsShiny) - gBattleAnimArgs[0] = TRUE; - else - gBattleAnimArgs[0] = FALSE; + gBattleAnimArgs[0] = gAnimDisableStructPtr->syrupBombIsShiny; DestroyAnimVisualTask(taskId); } diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 8479ebb172..885e9394bb 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -1508,13 +1508,14 @@ void BtlController_EmitSpriteInvisibility(u32 battler, u32 bufferId, bool8 isInv PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, u16 argument) +void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, struct DisableStruct* disableStructPtr, u16 argument) { gBattleResources->transferBuffer[0] = CONTROLLER_BATTLEANIMATION; gBattleResources->transferBuffer[1] = animationId; gBattleResources->transferBuffer[2] = argument; gBattleResources->transferBuffer[3] = (argument & 0xFF00) >> 8; - PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); + memcpy(&gBattleResources->transferBuffer[4], disableStructPtr, sizeof(struct DisableStruct)); + PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4 + sizeof(struct DisableStruct)); } // mode is a LINK_STANDBY_* constant @@ -3035,6 +3036,8 @@ void BtlController_HandleBattleAnimation(u32 battler, bool32 ignoreSE, bool32 up u8 animationId = gBattleResources->bufferA[battler][1]; u16 argument = gBattleResources->bufferA[battler][2] | (gBattleResources->bufferA[battler][3] << 8); + gAnimDisableStructPtr = (struct DisableStruct *)&gBattleResources->bufferA[battler][4]; + if (TryHandleLaunchBattleTableAnimation(battler, battler, battler, animationId, argument)) BattleControllerComplete(battler); else diff --git a/src/battle_main.c b/src/battle_main.c index 2823b86bea..a9d1f91add 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5648,6 +5648,13 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) else if (gBattleMons[battlerAtk].type3 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type3 | F_DYNAMIC_TYPE_2; } + else if (gBattleMoves[move].effect == EFFECT_RAGING_BULL + && (gBattleMons[battlerAtk].species == SPECIES_TAUROS_PALDEAN_COMBAT_BREED + || gBattleMons[battlerAtk].species == SPECIES_TAUROS_PALDEAN_BLAZE_BREED + || gBattleMons[battlerAtk].species == SPECIES_TAUROS_PALDEAN_AQUA_BREED)) + { + gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type2 | F_DYNAMIC_TYPE_2; + } else if (gBattleMoves[move].effect == EFFECT_NATURAL_GIFT) { if (ItemId_GetPocket(gBattleMons[battlerAtk].item) == POCKET_BERRIES) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 83795e3582..08081d030d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3619,8 +3619,11 @@ void SetMoveEffect(bool32 primary, u32 certain) case MOVE_EFFECT_SYRUP_BOMB: if (!(gStatuses4[gEffectBattler] & STATUS4_SYRUP_BOMB)) { + struct Pokemon *party = GetBattlerParty(gBattlerAttacker); + gStatuses4[gEffectBattler] |= STATUS4_SYRUP_BOMB; - gDisableStructs[gBattlerTarget].syrupBombTimer = 3; + gDisableStructs[gEffectBattler].syrupBombTimer = 3; + gDisableStructs[gEffectBattler].syrupBombIsShiny = IsMonShiny(&party[gBattlerPartyIndexes[gBattlerAttacker]]); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_SyrupBombActivates; } @@ -4901,7 +4904,7 @@ static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *n || animId == B_ANIM_PRIMAL_REVERSION || animId == B_ANIM_ULTRA_BURST) { - BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, *argPtr); + BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, &gDisableStructs[battler], *argPtr); MarkBattlerForControllerExec(battler); gBattlescriptCurrInstr = nextInstr; } @@ -4916,7 +4919,7 @@ static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *n || animId == B_ANIM_HAIL_CONTINUES || animId == B_ANIM_SNOW_CONTINUES) { - BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, *argPtr); + BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, &gDisableStructs[battler], *argPtr); MarkBattlerForControllerExec(battler); gBattlescriptCurrInstr = nextInstr; } @@ -4926,7 +4929,7 @@ static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *n } else { - BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, *argPtr); + BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, &gDisableStructs[battler], *argPtr); MarkBattlerForControllerExec(battler); gBattlescriptCurrInstr = nextInstr; } @@ -5091,7 +5094,7 @@ static void Cmd_playstatchangeanimation(void) } else if (changeableStatsCount != 0 && !gBattleScripting.statAnimPlayed) { - BtlController_EmitBattleAnimation(battler, BUFFER_A, B_ANIM_STATS_CHANGE, statAnimId); + BtlController_EmitBattleAnimation(battler, BUFFER_A, B_ANIM_STATS_CHANGE, &gDisableStructs[battler], statAnimId); MarkBattlerForControllerExec(battler); if (flags & STAT_CHANGE_MULTIPLE_STATS && changeableStatsCount > 1) gBattleScripting.statAnimPlayed = TRUE; diff --git a/src/battle_util.c b/src/battle_util.c index d32c30b1e6..7d13c521e7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4112,7 +4112,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 { u32 effect = 0; u32 moveType, move; + u32 side; u32 i, j; + struct Pokemon *mon; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return 0; @@ -4700,12 +4702,15 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_ZERO_TO_HERO: + side = GetBattlerSide(battler); + mon = &GetSideParty(side)[gBattlerPartyIndexes[battler]]; + if (!gSpecialStatuses[battler].switchInAbilityDone - && gBattleMons[battler].species == SPECIES_PALAFIN_HERO - && !gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)]) + && GetMonData(mon, MON_DATA_SPECIES) == SPECIES_PALAFIN_HERO + && !gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][side]) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; - gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)] = TRUE; + gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][side] = TRUE; BattleScriptPushCursorAndCallback(BattleScript_ZeroToHeroActivates); effect++; } @@ -5258,6 +5263,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_STANCE_CHANGE: case ABILITY_WONDER_GUARD: case ABILITY_ZEN_MODE: + case ABILITY_ZERO_TO_HERO: break; default: if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_ABILITY_SHIELD) diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 6cfa42e2b7..cc33db08db 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13862,7 +13862,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] = [MOVE_RAGING_BULL] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_RAGING_BULL + .effect = EFFECT_RAGING_BULL, .power = 90, .type = TYPE_NORMAL, .accuracy = 100, diff --git a/test/battle/ability/zero_to_hero.c b/test/battle/ability/zero_to_hero.c index 4b9184ceca..685929a5b7 100644 --- a/test/battle/ability/zero_to_hero.c +++ b/test/battle/ability/zero_to_hero.c @@ -123,6 +123,46 @@ SINGLE_BATTLE_TEST("Role Play, Skill Swap, and Entrainment fail if either Pokém } } +SINGLE_BATTLE_TEST("Transform doesn't apply the heroic transformation message when copying Palafin") +{ + GIVEN { + PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(player, 1); } + TURN { SWITCH(player, 0); MOVE(opponent, MOVE_TRANSFORM); } + } SCENE { + ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO); + MESSAGE("Palafin underwent a heroic transformation!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, opponent); + MESSAGE("Foe Wobbuffet transformed into Palafin!"); + NOT ABILITY_POPUP(opponent, ABILITY_ZERO_TO_HERO); + } THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); } +} + +SINGLE_BATTLE_TEST("Imposter doesn't apply the heroic transformation message when copying Palafin") +{ + GIVEN { + PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(player, 1); SWITCH(opponent, 1); } + TURN { SWITCH(player, 0); SWITCH(opponent, 0); } + } SCENE { + ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO); + MESSAGE("Palafin underwent a heroic transformation!"); + ABILITY_POPUP(opponent, ABILITY_IMPOSTER); + MESSAGE("Foe Ditto transformed into Palafin using Imposter!"); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_ZERO_TO_HERO); + MESSAGE("Foe Ditto underwent a heroic transformation!"); + } + } THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); } +} + // Write Trace test and move this one to that file (including every other ability that can't be copied) SINGLE_BATTLE_TEST("Zero to Hero cannot be copied by Trace") { diff --git a/test/battle/move_effect/brick_break.c b/test/battle/move_effect/brick_break.c new file mode 100644 index 0000000000..8bc05c0eea --- /dev/null +++ b/test/battle/move_effect/brick_break.c @@ -0,0 +1,160 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_BRICK_BREAK].effect == EFFECT_BRICK_BREAK); + ASSUME(gBattleMoves[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE); + ASSUME(gBattleMoves[MOVE_LIGHT_SCREEN].effect == EFFECT_LIGHT_SCREEN); + ASSUME(gBattleMoves[MOVE_REFLECT].effect == EFFECT_REFLECT); + ASSUME(gBattleMoves[MOVE_AURORA_VEIL].effect == EFFECT_AURORA_VEIL); +} + +SINGLE_BATTLE_TEST("Brick Break removes Light Screen, Reflect and Aurora Veil from the target's side of the field") +{ + u16 move; + + PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } + PARAMETRIZE { move = MOVE_REFLECT; } + PARAMETRIZE { move = MOVE_AURORA_VEIL; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SNOWSCAPE); } + TURN { MOVE(opponent, move); MOVE(player, MOVE_BRICK_BREAK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); + MESSAGE("The wall shattered!"); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Brick Break doesn't remove Light Screen, Reflect and Aurora Veil if the target is immune") +{ + u16 move; + + PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } + PARAMETRIZE { move = MOVE_REFLECT; } + PARAMETRIZE { move = MOVE_AURORA_VEIL; } + + KNOWN_FAILING; + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GASTLY); + } WHEN { + TURN { MOVE(player, MOVE_SNOWSCAPE); } + TURN { MOVE(opponent, move); MOVE(player, MOVE_BRICK_BREAK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); + MESSAGE("The wall shattered!"); + HP_BAR(opponent); + } + } +} + +SINGLE_BATTLE_TEST("Brick Break doesn't remove Light Screen, Reflect and Aurora Veil if the target Protected") +{ + u16 move; + + PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } + PARAMETRIZE { move = MOVE_REFLECT; } + PARAMETRIZE { move = MOVE_AURORA_VEIL; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); } + TURN { MOVE(player, MOVE_BRICK_BREAK); MOVE(opponent, MOVE_PROTECT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); + MESSAGE("The wall shattered!"); + HP_BAR(opponent); + } + } +} + +SINGLE_BATTLE_TEST("Brick Break doesn't remove Light Screen, Reflect and Aurora Veil if it misses") +{ + u16 move; + + PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } + PARAMETRIZE { move = MOVE_REFLECT; } + PARAMETRIZE { move = MOVE_AURORA_VEIL; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHT_POWDER); } + } WHEN { + TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); } + TURN { MOVE(player, MOVE_BRICK_BREAK, hit: FALSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, player); + MESSAGE("The wall shattered!"); + HP_BAR(opponent); + } + } +} + +DOUBLE_BATTLE_TEST("Brick Break can remove Light Screen, Reflect and Aurora Veil on users side") +{ + u16 move; + + PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } + PARAMETRIZE { move = MOVE_REFLECT; } + PARAMETRIZE { move = MOVE_AURORA_VEIL; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_SNOWSCAPE); + MOVE(playerLeft, move); + MOVE(playerRight, MOVE_BRICK_BREAK, target: playerLeft); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BRICK_BREAK, playerRight); + MESSAGE("The wall shattered!"); + HP_BAR(playerLeft); + } +} + +SINGLE_BATTLE_TEST("Move Raging Bull changes it's type depending on the Tauros Form") +{ + u16 speciesPlayer; + u16 speciesOpponent; + + PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_COMBAT_BREED; speciesOpponent = SPECIES_CHARIZARD; } + PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_BLAZE_BREED; speciesOpponent = SPECIES_BLASTOISE; } + PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_AQUA_BREED; speciesOpponent = SPECIES_VENUSAUR; } + + GIVEN { + PLAYER(speciesPlayer); + OPPONENT(speciesOpponent); + } WHEN { + TURN { MOVE(player, MOVE_RAGING_BULL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player); + HP_BAR(opponent); + MESSAGE("It's not very effective…"); + } +}