From 913bc6c9d0602dbb61c096520b5424af4599fd89 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 29 Nov 2023 11:05:06 +0100 Subject: [PATCH 1/4] Adds White Herb tests (#3611) --- test/battle/hold_effect/white_herb.c | 176 +++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/test/battle/hold_effect/white_herb.c b/test/battle/hold_effect/white_herb.c index a34bb6a347..734091552b 100644 --- a/test/battle/hold_effect/white_herb.c +++ b/test/battle/hold_effect/white_herb.c @@ -68,3 +68,179 @@ DOUBLE_BATTLE_TEST("White Herb restores stats after Attack was lowered by Intimi EXPECT(opponentRight->statStages[STAT_DEF] = DEFAULT_STAT_STAGE); } } + +SINGLE_BATTLE_TEST("White Herb restores stats after Attack was lowered by Intimidate while switching in") +{ + KNOWN_FAILING; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { SWITCH(opponent, 1); MOVE(player, MOVE_CLOSE_COMBAT); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CLOSE_COMBAT, player); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + } + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT(player->statStages[STAT_DEF] = DEFAULT_STAT_STAGE - 1); + EXPECT(player->statStages[STAT_SPDEF] = DEFAULT_STAT_STAGE - 1); + } +} + + +SINGLE_BATTLE_TEST("White Herb restores stats after all hits of a multi hit move happened") +{ + u16 species; + u16 ability; + + PARAMETRIZE { species = SPECIES_SLIGGOO_HISUIAN; ability = ABILITY_GOOEY; } + PARAMETRIZE { species = SPECIES_DUGTRIO_ALOLAN; ability = ABILITY_TANGLING_HAIR; } + + KNOWN_FAILING; + GIVEN { + ASSUME(gBattleMoves[MOVE_DUAL_WINGBEAT].strikeCount == 2); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_DUAL_WINGBEAT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DUAL_WINGBEAT, player); + ABILITY_POPUP(opponent, ability); + MESSAGE("Wobbuffet's Speed fell!"); + ABILITY_POPUP(opponent, ability); + MESSAGE("Wobbuffet's Speed fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT(player->statStages[STAT_SPEED] = DEFAULT_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("White Herb wont have time to activate if it is knocked off or stolen by Thief") +{ + u16 move; + + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_KNOCK_OFF; } + + KNOWN_FAILING; // Knock off fails, Thief is fine + GIVEN { + ASSUME(gBattleMoves[MOVE_THIEF].effect == EFFECT_THIEF); + ASSUME(gBattleMoves[MOVE_KNOCK_OFF].effect == EFFECT_KNOCK_OFF); + PLAYER(SPECIES_SLUGMA) { Ability(ABILITY_WEAK_ARMOR); Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + if (move == MOVE_THIEF) + MESSAGE("Foe Wobbuffet stole Slugma's White Herb!"); + else + MESSAGE("Foe Wobbuffet knocked off Slugma's White Herb!"); + ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Slugma's Weak Armor lowered its Defense!"); + MESSAGE("Slugma's Weak Armor raised its Speed!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + } + } THEN { + EXPECT(player->statStages[STAT_DEF] = DEFAULT_STAT_STAGE - 1); + EXPECT(player->statStages[STAT_SPEED] = DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("White Herb wont have time to activate if Magician steals it") +{ + KNOWN_FAILING; // White Herb is activated + GIVEN { + ASSUME(P_GEN_6_POKEMON == TRUE); + PLAYER(SPECIES_SLUGMA) { Ability(ABILITY_WEAK_ARMOR); Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_FENNEKIN) { Ability(ABILITY_MAGICIAN); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(opponent, ABILITY_MAGICIAN); + ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Slugma's Weak Armor lowered its Defense!"); + MESSAGE("Slugma's Weak Armor raised its Speed!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + } + } THEN { + EXPECT(player->statStages[STAT_DEF] = DEFAULT_STAT_STAGE - 1); + EXPECT(player->statStages[STAT_SPEED] = DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("White Herb wont have time to activate if Pickpocket steals it") +{ + KNOWN_FAILING; // White Herb is activated + GIVEN { + ASSUME(gBattleMoves[MOVE_LEAF_STORM].effect == EFFECT_OVERHEAT); + PLAYER(SPECIES_SLUGMA) { Ability(ABILITY_WEAK_ARMOR); Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_LEAF_STORM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEAF_STORM, player); + ABILITY_POPUP(player, ABILITY_PICKPOCKET); + ABILITY_POPUP(player, ABILITY_WEAK_ARMOR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Slugma's Weak Armor lowered its Defense!"); + MESSAGE("Slugma's Weak Armor raised its Speed!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + } + } THEN { + EXPECT(player->statStages[STAT_DEF] = DEFAULT_STAT_STAGE - 1); + EXPECT(player->statStages[STAT_SPEED] = DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("White Herb restores stats after Defiant or Competitive were triggered") +{ + u16 species; + u16 ability; + + PARAMETRIZE { species = SPECIES_IGGLYBUFF; ability = ABILITY_COMPETITIVE; } + PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_DEFIANT; } + + KNOWN_FAILING; + GIVEN { + PLAYER(species) { Ability(ability); Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { ; } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ABILITY_POPUP(player, ability); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet's White Herb restored its status!"); + } THEN { + EXPECT(player->item == ITEM_NONE); + if (species == SPECIES_IGGLYBUFF) + { + EXPECT(player->statStages[STAT_ATK] = DEFAULT_STAT_STAGE); + EXPECT(player->statStages[STAT_SPATK] = DEFAULT_STAT_STAGE + 2); + } + else + EXPECT(player->statStages[STAT_ATK] = DEFAULT_STAT_STAGE + 3); + } +} From 4bde378c0d13704ecdc46d26304eaecdc9ba8e23 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:57:29 +0100 Subject: [PATCH 2/4] Fixes Floral Healing + Tests (#3604) --- asm/macros/battle_script.inc | 10 +- data/battle_scripts_1.s | 2 +- include/constants/battle.h | 3 +- include/constants/battle_script_commands.h | 234 ++++++++++----------- src/battle_script_commands.c | 43 ++-- src/data/battle_moves.h | 1 + test/battle/move_effect/heal_pulse.c | 102 +++++++++ 7 files changed, 250 insertions(+), 145 deletions(-) create mode 100644 test/battle/move_effect/heal_pulse.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index c875729b85..8002df68ab 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1461,6 +1461,11 @@ .4byte \failInstr .endm + .macro tryhealpulse failInstr:req + callnative BS_TryHealPulse + .4byte \failInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES @@ -1652,11 +1657,6 @@ various \battler, VARIOUS_SET_LAST_USED_ABILITY .endm - .macro tryhealpulse battler:req, failInstr:req - various \battler, VARIOUS_TRY_HEAL_PULSE - .4byte \failInstr - .endm - .macro tryquash failInstr:req various BS_ATTACKER, VARIOUS_TRY_QUASH .4byte \failInstr diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 07e0bf065a..6124f72fa8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2607,7 +2607,7 @@ BattleScript_EffectHealPulse: jumpifstatus3 BS_TARGET, STATUS3_HEAL_BLOCK, BattleScript_MoveUsedHealBlockPrevents accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON jumpifsubstituteblocks BattleScript_ButItFailed - tryhealpulse BS_TARGET, BattleScript_AlreadyAtFullHp + tryhealpulse BattleScript_AlreadyAtFullHp attackanimation waitanimation healthbarupdate BS_TARGET diff --git a/include/constants/battle.h b/include/constants/battle.h index 5e94f27fe9..f21452310e 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -384,8 +384,9 @@ #define MOVE_EFFECT_STEALTH_ROCK 76 #define MOVE_EFFECT_SPIKES 77 #define MOVE_EFFECT_TRIPLE_ARROWS 78 +#define MOVE_EFFECT_FLORAL_HEALING 79 -#define NUM_MOVE_EFFECTS 79 +#define NUM_MOVE_EFFECTS 80 #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index ea74a4d1e9..e7d0f8f4be 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -131,124 +131,122 @@ #define VARIOUS_SET_SIMPLE_BEAM 39 #define VARIOUS_TRY_ENTRAINMENT 40 #define VARIOUS_SET_LAST_USED_ABILITY 41 -#define VARIOUS_TRY_HEAL_PULSE 42 -#define VARIOUS_TRY_QUASH 43 -#define VARIOUS_INVERT_STAT_STAGES 44 -#define VARIOUS_TRY_ME_FIRST 45 -#define VARIOUS_JUMP_IF_BATTLE_END 46 -#define VARIOUS_TRY_ELECTRIFY 47 -#define VARIOUS_TRY_REFLECT_TYPE 48 -#define VARIOUS_TRY_SOAK 49 -#define VARIOUS_HANDLE_MEGA_EVO 50 -#define VARIOUS_TRY_LAST_RESORT 51 -#define VARIOUS_ARGUMENT_STATUS_EFFECT 52 -#define VARIOUS_TRY_HIT_SWITCH_TARGET 53 -#define VARIOUS_TRY_AUTOTOMIZE 54 -#define VARIOUS_TRY_COPYCAT 55 -#define VARIOUS_ABILITY_POPUP 56 -#define VARIOUS_DEFOG 57 -#define VARIOUS_JUMP_IF_TARGET_ALLY 58 -#define VARIOUS_TRY_SYNCHRONOISE 59 -#define VARIOUS_PSYCHO_SHIFT 60 -#define VARIOUS_CURE_STATUS 61 -#define VARIOUS_POWER_TRICK 62 -#define VARIOUS_AFTER_YOU 63 -#define VARIOUS_BESTOW 64 -#define VARIOUS_ARGUMENT_TO_MOVE_EFFECT 65 -#define VARIOUS_JUMP_IF_NOT_GROUNDED 66 -#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 67 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 68 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 69 -#define VARIOUS_SET_AURORA_VEIL 70 -#define VARIOUS_TRY_THIRD_TYPE 71 -#define VARIOUS_ACUPRESSURE 72 -#define VARIOUS_SET_POWDER 73 -#define VARIOUS_SPECTRAL_THIEF 74 -#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 75 -#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 76 -#define VARIOUS_JUMP_IF_ROAR_FAILS 77 -#define VARIOUS_TRY_INSTRUCT 78 -#define VARIOUS_JUMP_IF_NOT_BERRY 79 -#define VARIOUS_TRACE_ABILITY 80 -#define VARIOUS_UPDATE_NICK 81 -#define VARIOUS_TRY_ILLUSION_OFF 82 -#define VARIOUS_SET_SPRITEIGNORE0HP 83 -#define VARIOUS_HANDLE_FORM_CHANGE 84 -#define VARIOUS_GET_STAT_VALUE 85 -#define VARIOUS_JUMP_IF_FULL_HP 86 -#define VARIOUS_LOSE_TYPE 87 -#define VARIOUS_TRY_ACTIVATE_SOULHEART 88 -#define VARIOUS_TRY_ACTIVATE_RECEIVER 89 -#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 90 -#define VARIOUS_TRY_FRISK 91 -#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 92 -#define VARIOUS_TRY_FAIRY_LOCK 93 -#define VARIOUS_JUMP_IF_NO_ALLY 94 -#define VARIOUS_POISON_TYPE_IMMUNITY 95 -#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 96 -#define VARIOUS_INFATUATE_WITH_BATTLER 97 -#define VARIOUS_SET_LAST_USED_ITEM 98 -#define VARIOUS_PARALYZE_TYPE_IMMUNITY 99 -#define VARIOUS_JUMP_IF_ABSENT 100 -#define VARIOUS_DESTROY_ABILITY_POPUP 101 -#define VARIOUS_TOTEM_BOOST 102 -#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 103 -#define VARIOUS_MOVEEND_ITEM_EFFECTS 104 -#define VARIOUS_TERRAIN_SEED 105 -#define VARIOUS_MAKE_INVISIBLE 106 -#define VARIOUS_ROOM_SERVICE 107 - -#define VARIOUS_EERIE_SPELL_PP_REDUCE 108 -#define VARIOUS_JUMP_IF_TEAM_HEALTHY 109 -#define VARIOUS_TRY_HEAL_QUARTER_HP 110 -#define VARIOUS_REMOVE_TERRAIN 111 -#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 112 -#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 113 -#define VARIOUS_GET_ROTOTILLER_TARGETS 114 -#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 115 -#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 116 -#define VARIOUS_CONSUME_BERRY 117 -#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 118 -#define VARIOUS_APPLY_PLASMA_FISTS 119 -#define VARIOUS_JUMP_IF_SPECIES 120 -#define VARIOUS_UPDATE_ABILITY_POPUP 121 -#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 122 -#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 123 -#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 124 -#define VARIOUS_PHOTON_GEYSER_CHECK 125 -#define VARIOUS_SHELL_SIDE_ARM_CHECK 126 -#define VARIOUS_TRY_NO_RETREAT 127 -#define VARIOUS_TRY_TAR_SHOT 128 -#define VARIOUS_CAN_TAR_SHOT_WORK 129 -#define VARIOUS_CHECK_POLTERGEIST 130 -#define VARIOUS_SET_OCTOLOCK 131 -#define VARIOUS_CUT_1_3_HP_RAISE_STATS 132 -#define VARIOUS_TRY_END_NEUTRALIZING_GAS 133 -#define VARIOUS_JUMP_IF_UNDER_200 134 -#define VARIOUS_SET_SKY_DROP 135 -#define VARIOUS_CLEAR_SKY_DROP 136 -#define VARIOUS_SKY_DROP_YAWN 137 -#define VARIOUS_JUMP_IF_HOLD_EFFECT 138 -#define VARIOUS_CURE_CERTAIN_STATUSES 139 -#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 140 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 141 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 142 -#define VARIOUS_SAVE_BATTLER_ITEM 143 -#define VARIOUS_RESTORE_BATTLER_ITEM 144 -#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 145 -#define VARIOUS_SET_BEAK_BLAST 146 -#define VARIOUS_SWAP_SIDE_STATUSES 147 -#define VARIOUS_SWAP_STATS 148 -#define VARIOUS_TEATIME_INVUL 149 -#define VARIOUS_TEATIME_TARGETS 150 -#define VARIOUS_TRY_WIND_RIDER_POWER 151 -#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 152 -#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 153 -#define VARIOUS_STORE_HEALING_WISH 154 -#define VARIOUS_HIT_SWITCH_TARGET_FAILED 155 -#define VARIOUS_TRY_REVIVAL_BLESSING 156 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 157 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 158 +#define VARIOUS_TRY_QUASH 42 +#define VARIOUS_INVERT_STAT_STAGES 43 +#define VARIOUS_TRY_ME_FIRST 44 +#define VARIOUS_JUMP_IF_BATTLE_END 45 +#define VARIOUS_TRY_ELECTRIFY 46 +#define VARIOUS_TRY_REFLECT_TYPE 47 +#define VARIOUS_TRY_SOAK 48 +#define VARIOUS_HANDLE_MEGA_EVO 49 +#define VARIOUS_TRY_LAST_RESORT 50 +#define VARIOUS_ARGUMENT_STATUS_EFFECT 51 +#define VARIOUS_TRY_HIT_SWITCH_TARGET 52 +#define VARIOUS_TRY_AUTOTOMIZE 53 +#define VARIOUS_TRY_COPYCAT 54 +#define VARIOUS_ABILITY_POPUP 55 +#define VARIOUS_DEFOG 56 +#define VARIOUS_JUMP_IF_TARGET_ALLY 57 +#define VARIOUS_TRY_SYNCHRONOISE 58 +#define VARIOUS_PSYCHO_SHIFT 59 +#define VARIOUS_CURE_STATUS 60 +#define VARIOUS_POWER_TRICK 61 +#define VARIOUS_AFTER_YOU 62 +#define VARIOUS_BESTOW 63 +#define VARIOUS_ARGUMENT_TO_MOVE_EFFECT 64 +#define VARIOUS_JUMP_IF_NOT_GROUNDED 65 +#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 66 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 67 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 68 +#define VARIOUS_SET_AURORA_VEIL 69 +#define VARIOUS_TRY_THIRD_TYPE 70 +#define VARIOUS_ACUPRESSURE 71 +#define VARIOUS_SET_POWDER 72 +#define VARIOUS_SPECTRAL_THIEF 73 +#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 74 +#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 75 +#define VARIOUS_JUMP_IF_ROAR_FAILS 76 +#define VARIOUS_TRY_INSTRUCT 77 +#define VARIOUS_JUMP_IF_NOT_BERRY 78 +#define VARIOUS_TRACE_ABILITY 79 +#define VARIOUS_UPDATE_NICK 80 +#define VARIOUS_TRY_ILLUSION_OFF 81 +#define VARIOUS_SET_SPRITEIGNORE0HP 82 +#define VARIOUS_HANDLE_FORM_CHANGE 83 +#define VARIOUS_GET_STAT_VALUE 84 +#define VARIOUS_JUMP_IF_FULL_HP 85 +#define VARIOUS_LOSE_TYPE 86 +#define VARIOUS_TRY_ACTIVATE_SOULHEART 87 +#define VARIOUS_TRY_ACTIVATE_RECEIVER 88 +#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 89 +#define VARIOUS_TRY_FRISK 90 +#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 91 +#define VARIOUS_TRY_FAIRY_LOCK 92 +#define VARIOUS_JUMP_IF_NO_ALLY 93 +#define VARIOUS_POISON_TYPE_IMMUNITY 94 +#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 95 +#define VARIOUS_INFATUATE_WITH_BATTLER 96 +#define VARIOUS_SET_LAST_USED_ITEM 97 +#define VARIOUS_PARALYZE_TYPE_IMMUNITY 98 +#define VARIOUS_JUMP_IF_ABSENT 99 +#define VARIOUS_DESTROY_ABILITY_POPUP 100 +#define VARIOUS_TOTEM_BOOST 101 +#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 102 +#define VARIOUS_MOVEEND_ITEM_EFFECTS 103 +#define VARIOUS_TERRAIN_SEED 104 +#define VARIOUS_MAKE_INVISIBLE 105 +#define VARIOUS_ROOM_SERVICE 106 +#define VARIOUS_EERIE_SPELL_PP_REDUCE 107 +#define VARIOUS_JUMP_IF_TEAM_HEALTHY 108 +#define VARIOUS_TRY_HEAL_QUARTER_HP 109 +#define VARIOUS_REMOVE_TERRAIN 110 +#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 111 +#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 112 +#define VARIOUS_GET_ROTOTILLER_TARGETS 113 +#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 114 +#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 115 +#define VARIOUS_CONSUME_BERRY 116 +#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 117 +#define VARIOUS_APPLY_PLASMA_FISTS 118 +#define VARIOUS_JUMP_IF_SPECIES 119 +#define VARIOUS_UPDATE_ABILITY_POPUP 120 +#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 121 +#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 122 +#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 123 +#define VARIOUS_PHOTON_GEYSER_CHECK 124 +#define VARIOUS_SHELL_SIDE_ARM_CHECK 125 +#define VARIOUS_TRY_NO_RETREAT 126 +#define VARIOUS_TRY_TAR_SHOT 127 +#define VARIOUS_CAN_TAR_SHOT_WORK 128 +#define VARIOUS_CHECK_POLTERGEIST 129 +#define VARIOUS_SET_OCTOLOCK 130 +#define VARIOUS_CUT_1_3_HP_RAISE_STATS 131 +#define VARIOUS_TRY_END_NEUTRALIZING_GAS 132 +#define VARIOUS_JUMP_IF_UNDER_200 133 +#define VARIOUS_SET_SKY_DROP 134 +#define VARIOUS_CLEAR_SKY_DROP 135 +#define VARIOUS_SKY_DROP_YAWN 136 +#define VARIOUS_JUMP_IF_HOLD_EFFECT 137 +#define VARIOUS_CURE_CERTAIN_STATUSES 138 +#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 139 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 140 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 141 +#define VARIOUS_SAVE_BATTLER_ITEM 142 +#define VARIOUS_RESTORE_BATTLER_ITEM 143 +#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 144 +#define VARIOUS_SET_BEAK_BLAST 145 +#define VARIOUS_SWAP_SIDE_STATUSES 146 +#define VARIOUS_SWAP_STATS 147 +#define VARIOUS_TEATIME_INVUL 148 +#define VARIOUS_TEATIME_TARGETS 149 +#define VARIOUS_TRY_WIND_RIDER_POWER 150 +#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 151 +#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 152 +#define VARIOUS_STORE_HEALING_WISH 153 +#define VARIOUS_HIT_SWITCH_TARGET_FAILED 154 +#define VARIOUS_TRY_REVIVAL_BLESSING 155 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 156 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 157 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 32f61423af..2395a65e1f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9324,26 +9324,6 @@ static void Cmd_various(void) gLastUsedAbility = gBattleMons[battler].ability; break; } - case VARIOUS_TRY_HEAL_PULSE: - { - VARIOUS_ARGS(const u8 *failInstr); - if (BATTLER_MAX_HP(battler)) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - if (GetBattlerAbility(gBattlerAttacker) == ABILITY_MEGA_LAUNCHER && gBattleMoves[gCurrentMove].pulseMove) - gBattleMoveDamage = -(gBattleMons[battler].maxHP * 75 / 100); - else - gBattleMoveDamage = -(gBattleMons[battler].maxHP / 2); - - if (gBattleMoveDamage == 0) - gBattleMoveDamage = -1; - gBattlescriptCurrInstr = cmd->nextInstr; - } - return; - } case VARIOUS_TRY_QUASH: { VARIOUS_ARGS(const u8 *failInstr); @@ -16349,3 +16329,26 @@ void BS_TryReflectType(void) gBattlescriptCurrInstr = cmd->nextInstr; } } + +void BS_TryHealPulse(void) +{ + NATIVE_ARGS(const u8 *failInstr); + + if (BATTLER_MAX_HP(gBattlerTarget)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else + { + if (GetBattlerAbility(gBattlerAttacker) == ABILITY_MEGA_LAUNCHER && gBattleMoves[gCurrentMove].pulseMove) + gBattleMoveDamage = -(gBattleMons[gBattlerTarget].maxHP * 75 / 100); + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && gBattleMoves[gCurrentMove].argument == MOVE_EFFECT_FLORAL_HEALING) + gBattleMoveDamage = -(gBattleMons[gBattlerTarget].maxHP * 2 / 3); + else + gBattleMoveDamage = -(gBattleMons[gBattlerTarget].maxHP / 2); + + if (gBattleMoveDamage == 0) + gBattleMoveDamage = -1; + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 80ac9f9458..fed757cdc5 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -10532,6 +10532,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .priority = 0, .split = SPLIT_STATUS, .zMoveEffect = Z_EFFECT_RESET_STATS, + .argument = MOVE_EFFECT_FLORAL_HEALING, .magicCoatAffected = TRUE, }, diff --git a/test/battle/move_effect/heal_pulse.c b/test/battle/move_effect/heal_pulse.c new file mode 100644 index 0000000000..ecd1cfe294 --- /dev/null +++ b/test/battle/move_effect/heal_pulse.c @@ -0,0 +1,102 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_HEAL_PULSE].effect == EFFECT_HEAL_PULSE); +} + +SINGLE_BATTLE_TEST("Heal Pulse heals the target by 1/2 of it's maxHP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_HEAL_PULSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_PULSE, opponent); + HP_BAR(player, damage: -maxHP / 2); + } +} + +DOUBLE_BATTLE_TEST("Heal Pulse can heal partner") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerRight, MOVE_HEAL_PULSE, target: playerLeft); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_PULSE, playerRight); + HP_BAR(playerLeft, damage: -maxHP / 2); + } +} + +SINGLE_BATTLE_TEST("Heal Pulse is boosted by Mega Launcher") +{ + GIVEN { + ASSUME(P_GEN_6_POKEMON == TRUE); + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); } + OPPONENT(SPECIES_CLAWITZER); + } WHEN { + TURN { MOVE(opponent, MOVE_HEAL_PULSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_PULSE, opponent); + HP_BAR(player, damage: -maxHP * 75 / 100); + } +} + +SINGLE_BATTLE_TEST("Heal Pulse ignores accurace checks") +{ + GIVEN { + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); Item(ITEM_BRIGHT_POWDER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_HEAL_PULSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_PULSE, opponent); + HP_BAR(player, damage: -maxHP / 2); + } +} + +SINGLE_BATTLE_TEST("Heal Pulse is blocked by Substitute") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(50); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_HEAL_PULSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_PULSE, opponent); + HP_BAR(player, damage: -maxHP / 2); + } + } +} + +SINGLE_BATTLE_TEST("Floral Healing heals the target by 2/3rd of it's maxHP if Grassy Terrain is on the field") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_FLORAL_HEALING].argument == MOVE_EFFECT_FLORAL_HEALING); + ASSUME(gBattleMoves[MOVE_GRASSY_TERRAIN].effect == EFFECT_GRASSY_TERRAIN); + PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GRASSY_TERRAIN); MOVE(opponent, MOVE_FLORAL_HEALING); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLORAL_HEALING, opponent); + HP_BAR(player, damage: -maxHP * 2 / 3); + } +} From ff0351ca702cc304b8a97414f8072733aeafc64b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Fri, 1 Dec 2023 12:41:33 -0300 Subject: [PATCH 3/4] Ask users for credits in README.md (#3619) * Ask users for credits in README.md * Consistently refer to the repo as pokeemerald-expansion --------- Co-authored-by: Bassoonian --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 19d4f23f5a..91a8bc12dd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ -# pokeemerald Expansion +# pokeemerald-expansion -## What is the pokeemerald Expansion? +## What is pokeemerald-expansion? -The pokeemerald Expansion is a decomp hack base project based off pret's [pokeemerald](https://github.com/pret/pokeemerald) decompilation project. It's recommended that any new projects that plan on using it, to clone this repository instead of pret's vanilla repository, as we regurlarly incorporate pret's documentation changes. This is ***NOT*** a standalone romhack, and as such, most features will be unavailable and/or unbalanced if played as is. +pokeemerald-expansion is a decomp hack base project based off pret's [pokeemerald](https://github.com/pret/pokeemerald) decompilation project. It's recommended that any new projects that plan on using it, to clone this repository instead of pret's vanilla repository, as we regurlarly incorporate pret's documentation changes. This is ***NOT*** a standalone romhack, and as such, most features will be unavailable and/or unbalanced if played as is. + +If you use pokeemerald-expansion in your hack, please add RHH (Rom Hacking Hideout) to your credits list. Optionally, you can list the version used, so it can help players know what features to expect. +You can phrase it as the following: +``` +Based off RHH's pokeemerald-expansion v1.6.2 https://github.com/rh-hideout/pokeemerald-expansion/ +``` ## What features are included? - ***IMPORTANT*❗❗ Read through these to learn what features you can toggle**: @@ -130,14 +136,14 @@ There are some mechanics, moves and abilities that are missing and being develop ### [Documentation on features can be found here](https://github.com/rh-hideout/pokeemerald-expansion/wiki) -## If I already have a project based on regular pokeemerald, can I use the pokeemerald Expansion +## If I already have a project based on regular pokeemerald, can I use pokeemerald-expansion? Yes! Keep in mind that we keep up with pret's documentation of pokeemerald, which means that if your project a bit old, you might get merge conflicts that you need to solve manually. - If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. - Once you have your remote set up, run the command `git pull RHH master`. -With this, you'll get the latest version of the Expansion, plus a couple of bugfixes that haven't been released into the next patch version :) +With this, you'll get the latest version of pokeemerald-expansion, plus a couple of bugfixes that haven't been released into the next patch version :) -## **How do I update my version of the pokeemerald Expansion?** +## **How do I update my version of pokeemerald-expansion?** - If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. - Once you have your remote set up, run the command `git pull RHH expansion/1.6.2`. @@ -156,7 +162,7 @@ The project was originally started by DizzyEgg alongside other contributors. The project has now gotten larger and DizzyEgg is now maintaining the project as part of the ROM Hacking Hideout community. Some members of this community are taking on larger roles to help maintain the project. -## What is ROM Hacking Hideout? +## What is the ROM Hacking Hideout? A Discord-based ROM hacking community that has many members who hack using the disassembly and decompilation projects for Pokémon. Quite a few contributors to the original feature branches by DizzyEgg were members of ROM Hacking Hideout. You can call it RHH for short! From 6b1ea33be8cf533f343ad938a43edfd13d03a67b Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Sat, 2 Dec 2023 19:09:09 -0300 Subject: [PATCH 4/4] Fixed Trace (#3623) * Applied Alex's fix for ABILITYEFFECT_TRACE * Added Trace tests written by Alex/Rainonline * Updated Trace's target-choosing capabilities and added a double battle test for it Credits to Alex/Rainonline. * Removed 2 of the 3 'Trace on opponent's switch-in' tests * Added another Trace test --- include/random.h | 1 + src/battle_util.c | 12 +----- test/battle/ability/trace.c | 82 +++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 test/battle/ability/trace.c diff --git a/include/random.h b/include/random.h index af3d7c75e5..4500d77403 100644 --- a/include/random.h +++ b/include/random.h @@ -93,6 +93,7 @@ enum RandomTag RNG_TRIPLE_ARROWS_DEFENSE_DOWN, RNG_TRIPLE_ARROWS_FLINCH, RNG_QUICK_DRAW, + RNG_TRACE, }; #define RandomWeighted(tag, ...) \ diff --git a/src/battle_util.c b/src/battle_util.c index bdce8fbc9d..0a89e7e70e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5955,7 +5955,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 { if (!sAbilitiesNotTraced[gBattleMons[target1].ability] && gBattleMons[target1].hp != 0 && !sAbilitiesNotTraced[gBattleMons[target2].ability] && gBattleMons[target2].hp != 0) - chosenTarget = GetBattlerAtPosition(((Random() & 1) * 2) | side), effect++; + chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++; else if (!sAbilitiesNotTraced[gBattleMons[target1].ability] && gBattleMons[target1].hp != 0) chosenTarget = target1, effect++; else if (!sAbilitiesNotTraced[gBattleMons[target2].ability] && gBattleMons[target2].hp != 0) @@ -5969,15 +5969,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (effect != 0) { - if (caseID == ABILITYEFFECT_TRACE1) - { - BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); - } - else - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_TraceActivates; - } + BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_TRACED; gBattleStruct->tracedAbility[i] = gLastUsedAbility = gBattleMons[chosenTarget].ability; RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability diff --git a/test/battle/ability/trace.c b/test/battle/ability/trace.c new file mode 100644 index 0000000000..0e4448424b --- /dev/null +++ b/test/battle/ability/trace.c @@ -0,0 +1,82 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Trace copies opponents ability") +{ + GIVEN { + PLAYER(SPECIES_RALTS) { Ability(ABILITY_TRACE); } + OPPONENT(SPECIES_TORCHIC) { Ability(ABILITY_BLAZE); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(player, ABILITY_TRACE); + MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); + } +} + +SINGLE_BATTLE_TEST("Trace copies opponents ability on switch-in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) + PLAYER(SPECIES_RALTS) { Ability(ABILITY_TRACE); } + OPPONENT(SPECIES_TORCHIC) { Ability(ABILITY_BLAZE); } + } WHEN { + TURN { SWITCH(player, 1); } + } SCENE { + ABILITY_POPUP(player, ABILITY_TRACE); + MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); + } +} + +SINGLE_BATTLE_TEST("Trace copies opponents ability on switch-in even if opponent switched in at the same time") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) + PLAYER(SPECIES_RALTS) { Ability(ABILITY_TRACE); } + OPPONENT(SPECIES_TREECKO) { HP(1); } + OPPONENT(SPECIES_TORCHIC) { Ability(ABILITY_BLAZE); } + } WHEN { + TURN { MOVE(player, MOVE_MISTY_EXPLOSION); SEND_OUT(opponent, 1); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_EXPLOSION); + ABILITY_POPUP(player, ABILITY_TRACE); + MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); + } +} + +DOUBLE_BATTLE_TEST("Trace copies opponents ability randomly") +{ + u16 ability1, ability2; + + PARAMETRIZE { ability1 = ABILITY_SPEED_BOOST; ability2 = ABILITY_BLAZE;} + PARAMETRIZE { ability1 = ABILITY_BLAZE; ability2 = ABILITY_SPEED_BOOST; } + + PASSES_RANDOMLY(1, 2, RNG_TRACE); + GIVEN { + PLAYER(SPECIES_RALTS) { Ability(ABILITY_TRACE); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TORCHIC) { Ability(ability1); } + OPPONENT(SPECIES_TORCHIC) { Ability(ability2); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_TRACE); + MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); + } +} + +SINGLE_BATTLE_TEST("Trace will copy an opponent's ability whenever it has the chance but only once") +{ + GIVEN { + ASSUME(P_GEN_4_POKEMON == TRUE); + PLAYER(SPECIES_RALTS) { Ability(ABILITY_TRACE); } + OPPONENT(SPECIES_CHERRIM) { Ability(ABILITY_FLOWER_GIFT); } + OPPONENT(SPECIES_TORCHIC) { Ability(ABILITY_BLAZE); } + } WHEN { + TURN { SWITCH(opponent, 1); } + } SCENE { + // TURN 2 + ABILITY_POPUP(player, ABILITY_TRACE); + MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); + } +}