diff --git a/README.md b/README.md index 0cb95f3810..60a1a82c7a 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**: @@ -136,14 +142,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`. @@ -162,7 +168,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! diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index c449c9421e..f6d5dc8fee 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1541,6 +1541,11 @@ callnative BS_TryTrainerSlideDynamaxMsg .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 @@ -1732,11 +1737,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 79c363ec4c..a88d270ef7 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2712,7 +2712,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 1897763799..ddbb8b2eea 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -392,8 +392,9 @@ #define MOVE_EFFECT_SPIKES 76 #define MOVE_EFFECT_TRIPLE_ARROWS 77 #define MOVE_EFFECT_SYRUP_BOMB 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 b98396fe57..5474e3b242 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -131,120 +131,118 @@ #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_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_CUT_1_3_HP_RAISE_STATS 130 -#define VARIOUS_TRY_END_NEUTRALIZING_GAS 131 -#define VARIOUS_JUMP_IF_UNDER_200 132 -#define VARIOUS_SET_SKY_DROP 133 -#define VARIOUS_CLEAR_SKY_DROP 134 -#define VARIOUS_SKY_DROP_YAWN 135 -#define VARIOUS_JUMP_IF_HOLD_EFFECT 136 -#define VARIOUS_CURE_CERTAIN_STATUSES 137 -#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 138 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 139 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 140 -#define VARIOUS_SAVE_BATTLER_ITEM 141 -#define VARIOUS_RESTORE_BATTLER_ITEM 142 -#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 143 -#define VARIOUS_SET_BEAK_BLAST 144 -#define VARIOUS_SWAP_SIDE_STATUSES 145 -#define VARIOUS_SWAP_STATS 146 -#define VARIOUS_TEATIME_INVUL 147 -#define VARIOUS_TEATIME_TARGETS 148 -#define VARIOUS_TRY_WIND_RIDER_POWER 149 -#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 150 -#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 151 -#define VARIOUS_STORE_HEALING_WISH 152 -#define VARIOUS_HIT_SWITCH_TARGET_FAILED 153 -#define VARIOUS_TRY_REVIVAL_BLESSING 154 +#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_JUMP_IF_SPECIES 118 +#define VARIOUS_UPDATE_ABILITY_POPUP 119 +#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 120 +#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 121 +#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 122 +#define VARIOUS_PHOTON_GEYSER_CHECK 123 +#define VARIOUS_SHELL_SIDE_ARM_CHECK 124 +#define VARIOUS_TRY_NO_RETREAT 125 +#define VARIOUS_TRY_TAR_SHOT 126 +#define VARIOUS_CAN_TAR_SHOT_WORK 127 +#define VARIOUS_CHECK_POLTERGEIST 128 +#define VARIOUS_CUT_1_3_HP_RAISE_STATS 129 +#define VARIOUS_TRY_END_NEUTRALIZING_GAS 130 +#define VARIOUS_JUMP_IF_UNDER_200 131 +#define VARIOUS_SET_SKY_DROP 132 +#define VARIOUS_CLEAR_SKY_DROP 133 +#define VARIOUS_SKY_DROP_YAWN 134 +#define VARIOUS_JUMP_IF_HOLD_EFFECT 135 +#define VARIOUS_CURE_CERTAIN_STATUSES 136 +#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 137 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 138 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 139 +#define VARIOUS_SAVE_BATTLER_ITEM 140 +#define VARIOUS_RESTORE_BATTLER_ITEM 141 +#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 142 +#define VARIOUS_SET_BEAK_BLAST 143 +#define VARIOUS_SWAP_SIDE_STATUSES 144 +#define VARIOUS_SWAP_STATS 145 +#define VARIOUS_TEATIME_INVUL 146 +#define VARIOUS_TEATIME_TARGETS 147 +#define VARIOUS_TRY_WIND_RIDER_POWER 148 +#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 149 +#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 150 +#define VARIOUS_STORE_HEALING_WISH 151 +#define VARIOUS_HIT_SWITCH_TARGET_FAILED 152 +#define VARIOUS_TRY_REVIVAL_BLESSING 153 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/random.h b/include/random.h index 8d801ebe74..d4dd359b38 100644 --- a/include/random.h +++ b/include/random.h @@ -97,6 +97,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_script_commands.c b/src/battle_script_commands.c index 6d8769f747..0da9b1e925 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9309,26 +9309,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 = -(GetNonDynamaxMaxHP(battler) * 75 / 100); - else - gBattleMoveDamage = -(GetNonDynamaxMaxHP(battler) / 2); - - if (gBattleMoveDamage == 0) - gBattleMoveDamage = -1; - gBattlescriptCurrInstr = cmd->nextInstr; - } - return; - } case VARIOUS_TRY_QUASH: { VARIOUS_ARGS(const u8 *failInstr); @@ -16431,3 +16411,26 @@ void BS_TryTrainerSlideDynamaxMsg(void) else 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 = -(GetNonDynamaxMaxHP(gBattlerTarget) * 75 / 100); + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && gBattleMoves[gCurrentMove].argument == MOVE_EFFECT_FLORAL_HEALING) + gBattleMoveDamage = -(GetNonDynamaxMaxHP(gBattlerTarget) * 2 / 3); + else + gBattleMoveDamage = -(GetNonDynamaxMaxHP(gBattlerTarget) / 2); + + if (gBattleMoveDamage == 0) + gBattleMoveDamage = -1; + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/battle_util.c b/src/battle_util.c index 475b38c78b..f33898f2a0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6017,7 +6017,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) @@ -6031,15 +6031,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/src/data/battle_moves.h b/src/data/battle_moves.h index a5ef4960a7..f3cf0fa76b 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -10985,6 +10985,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] = .priority = 0, .split = SPLIT_STATUS, .zMoveEffect = Z_EFFECT_RESET_STATS, + .argument = MOVE_EFFECT_FLORAL_HEALING, .mirrorMoveBanned = TRUE, .healBlockBanned = TRUE, .magicCoatAffected = TRUE, 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!"); + } +} 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); + } +} 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); + } +}