diff --git a/README.md b/README.md index facd5f562672..5330e4098138 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,16 @@ 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. +## Using pokeemerald-expansion + 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 1.9.3 https://github.com/rh-hideout/pokeemerald-expansion/ ``` +Please follow the instructions in `INSTALL.md` to get pokeemerald-expansion set up on your machine. + ## What features are included? - ***IMPORTANT*❗❗ Read through these to learn what features you can toggle**: - [Battle configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 6e8a54c62712..07d172cdf88f 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -10093,7 +10093,6 @@ gBattleAnimMove_FloralHealing:: loadspritegfx ANIM_TAG_ORBS @circles loadspritegfx ANIM_TAG_PINK_PETAL @pink particles monbg ANIM_ATTACKER - monbg ANIM_TARGET playsewithpan SE_M_DETECT, SOUND_PAN_ATTACKER call CIRCLES_LEAVES call CIRCLES_LEAVES @@ -10101,6 +10100,7 @@ gBattleAnimMove_FloralHealing:: panse SE_M_COMET_PUNCH, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 0x2, 0x0 playsewithpan SE_M_TWISTER, 0x0 createsprite gSweetScentPetalSpriteTemplate, ANIM_ATTACKER, 2, 0x46, 0x1, 0x40 + clearmonbg ANIM_ATTACKER delay 0x2 createsprite gFloralHealingWindLeavesTemplate, ANIM_ATTACKER, 2, 0x3c, 0x0, 0x40 delay 0x2 @@ -10123,6 +10123,7 @@ gBattleAnimMove_FloralHealing:: createsprite gSweetScentPetalSpriteTemplate, ANIM_ATTACKER, 2, 0x55, 0x0, 0x78 delay 0x2 loopsewithpan SE_M_POISON_POWDER, SOUND_PAN_TARGET, 0x12, 0xa + monbg ANIM_TARGET call FloralHealingSpores call FloralHealingSpores call FloralHealingSpores @@ -10133,7 +10134,6 @@ gBattleAnimMove_FloralHealing:: createsprite gGrantingStarsSpriteTemplate, ANIM_ATTACKER, 16, 0xc, 0xfffb, 0x1, 0x0, 0x20, 0x3c, 0x1 waitforvisualfinish clearmonbg ANIM_TARGET - clearmonbg ANIM_ATTACKER end FloralHealingSpores: createsprite gFloralHealingFlowerTemplate, ANIM_ATTACKER, 2, 0x0, 0xffec, 0x55, 0x50, 0x0 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 76b75075ad15..3d24cbfdf0b0 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2401,7 +2401,6 @@ BattleScript_EffectHealingWish:: storehealingwish BS_ATTACKER .if B_HEALING_WISH_SWITCH <= GEN_4 openpartyscreen BS_ATTACKER, BattleScript_EffectHealingWishEnd - switchoutabilities BS_ATTACKER waitstate switchhandleorder BS_ATTACKER, 2 returnatktoball @@ -5755,7 +5754,6 @@ BattleScript_PrintFullBox:: BattleScript_ActionSwitch:: hpthresholds2 BS_ATTACKER - saveattacker printstring STRINGID_RETURNMON jumpifbattletype BATTLE_TYPE_DOUBLE, BattleScript_PursuitSwitchDmgSetMultihit setmultihit 1 @@ -5773,7 +5771,6 @@ BattleScript_DoSwitchOut:: switchoutabilities BS_ATTACKER updatedynamax waitstate - restoreattacker returnatktoball waitstate drawpartystatussummary BS_ATTACKER @@ -9633,7 +9630,9 @@ BattleScript_EjectButtonActivates:: removeitem BS_SCRIPTING makeinvisible BS_SCRIPTING openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd + copybyte sSAVED_BATTLER, sBATTLER switchoutabilities BS_SCRIPTING + copybyte sBATTLER, sSAVED_BATTLER waitstate switchhandleorder BS_SCRIPTING 0x2 returntoball BS_SCRIPTING, FALSE @@ -9730,6 +9729,7 @@ BattleScript_PastelVeilEnd: end3 BattleScript_NeutralizingGasExits:: + saveattacker savetarget pause B_WAIT_TIME_SHORT printstring STRINGID_NEUTRALIZINGGASOVER @@ -9739,6 +9739,7 @@ BattleScript_NeutralizingGasExitsLoop: switchinabilities BS_TARGET addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_NeutralizingGasExitsLoop + restoreattacker restoretarget return diff --git a/graphics/pokemon/farfetchd/overworld.png b/graphics/pokemon/farfetchd/overworld.png index 46aec0976444..63d29215b729 100644 Binary files a/graphics/pokemon/farfetchd/overworld.png and b/graphics/pokemon/farfetchd/overworld.png differ diff --git a/graphics/pokemon/melmetal/overworld.png b/graphics/pokemon/melmetal/overworld.png index 4be020a5c6a8..e8097644ad16 100644 Binary files a/graphics/pokemon/melmetal/overworld.png and b/graphics/pokemon/melmetal/overworld.png differ diff --git a/graphics/pokemon/melmetal/overworld_normal.pal b/graphics/pokemon/melmetal/overworld_normal.pal index 28e459319267..d9b329362bea 100644 --- a/graphics/pokemon/melmetal/overworld_normal.pal +++ b/graphics/pokemon/melmetal/overworld_normal.pal @@ -1,7 +1,7 @@ JASC-PAL 0100 16 -255 255 255 +120 255 255 254 235 185 220 220 218 235 192 100 @@ -16,4 +16,4 @@ JASC-PAL 64 64 64 45 43 43 8 8 8 -0 0 0 +255 255 255 diff --git a/graphics/pokemon/melmetal/overworld_shiny.pal b/graphics/pokemon/melmetal/overworld_shiny.pal index 6d1c75c9bbf3..4f5321a06227 100644 --- a/graphics/pokemon/melmetal/overworld_shiny.pal +++ b/graphics/pokemon/melmetal/overworld_shiny.pal @@ -16,4 +16,4 @@ JASC-PAL 64 64 64 45 43 43 8 8 8 -0 0 0 +255 255 255 diff --git a/graphics/pokemon/patrat/overworld.png b/graphics/pokemon/patrat/overworld.png index ea2df04dc1ae..8b6a34f19870 100644 Binary files a/graphics/pokemon/patrat/overworld.png and b/graphics/pokemon/patrat/overworld.png differ diff --git a/graphics/pokemon/woobat/overworld.png b/graphics/pokemon/woobat/overworld.png index 1d14a5c43d79..18bff8d5c3b0 100644 Binary files a/graphics/pokemon/woobat/overworld.png and b/graphics/pokemon/woobat/overworld.png differ diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index bfdfceb6207a..050a20d933f7 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -289,9 +289,9 @@ enum MoveEndEffects MOVEEND_RECOIL, MOVEEND_ITEM_EFFECTS_ATTACKER, MOVEEND_MAGICIAN, // Occurs after final multi-hit strike, and after other items/abilities would activate + MOVEEND_RED_CARD, // Red Card triggers before Eject Pack MOVEEND_EJECT_ITEMS, MOVEEND_WHITE_HERB, - MOVEEND_RED_CARD, MOVEEND_LIFEORB_SHELLBELL, // Includes shell bell, throat spray, etc MOVEEND_CHANGED_ITEMS, MOVEEND_PICKPOCKET, diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 74bc16cf2f5c..79a8a4cee7bb 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -134,4 +134,8 @@ // param1: amount of days #define FORM_CHANGE_DAYS_PASSED 23 +// Form change for Aegislash +#define FORM_CHANGE_BATTLE_ATTACK 24 +#define FORM_CHANGE_BATTLE_KINGS_SHIELD 25 + #endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 46d050330709..844381a205c6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1125,7 +1125,6 @@ static bool32 NoTargetPresent(u8 battler, u32 move) return FALSE; } -// TODO: Convert this to a proper FORM_CHANGE type. static bool32 TryAegiFormChange(void) { // Only Aegislash with Stance Change can transform, transformed mons cannot. @@ -1140,12 +1139,12 @@ static bool32 TryAegiFormChange(void) case SPECIES_AEGISLASH_SHIELD: // Shield -> Blade if (IS_MOVE_STATUS(gCurrentMove)) return FALSE; - gBattleMons[gBattlerAttacker].species = SPECIES_AEGISLASH_BLADE; + TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_ATTACK); break; case SPECIES_AEGISLASH_BLADE: // Blade -> Shield if (gCurrentMove != MOVE_KINGS_SHIELD) return FALSE; - gBattleMons[gBattlerAttacker].species = SPECIES_AEGISLASH_SHIELD; + TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_KINGS_SHIELD); break; } @@ -1782,8 +1781,6 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u if (!RandomPercentage(RNG_ACCURACY, accuracy)) { gMoveResultFlags |= MOVE_RESULT_MISSED; - if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY) - gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_DARTS && !recalcDragonDarts // So we don't jump back and forth between targets @@ -2266,6 +2263,7 @@ static void Cmd_adjustdamage(void) } if (gSpecialStatuses[gBattlerAttacker].gemBoost && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) && gBattleMons[gBattlerAttacker].item && gMovesInfo[gCurrentMove].effect != EFFECT_PLEDGE && gCurrentMove != MOVE_STRUGGLE) @@ -2678,6 +2676,13 @@ static void Cmd_resultmessage(void) return; } + if (gMoveResultFlags & MOVE_RESULT_MISSED && !(gMoveResultFlags & (MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED))) + { + if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY + && !IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)) + gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks + } + if (gMoveResultFlags & MOVE_RESULT_MISSED && (!(gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) || gBattleCommunication[MISS_TYPE] > B_MSG_AVOIDED_ATK)) { if (gBattleCommunication[MISS_TYPE] > B_MSG_AVOIDED_ATK) // Wonder Guard or Levitate - show the ability pop-up @@ -4157,6 +4162,15 @@ static void Cmd_tryfaintmon(void) } else { + if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS + && !(gAbsentBattlerFlags & (1u << battler)) + && !IsBattlerAlive(battler)) + { + gBattleMons[battler].ability = ABILITY_NONE; + BattleScriptPush(gBattlescriptCurrInstr); + gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits; + return; + } if (cmd->battler == BS_ATTACKER) { destinyBondBattler = gBattlerTarget; @@ -14788,7 +14802,7 @@ static void Cmd_switchoutabilities(void) MarkBattlerForControllerExec(battler); break; case ABILITY_REGENERATOR: - gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 3; + gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 3; gBattleMoveDamage += gBattleMons[battler].hp; if (gBattleMoveDamage > gBattleMons[battler].maxHP) gBattleMoveDamage = gBattleMons[battler].maxHP; diff --git a/src/battle_util.c b/src/battle_util.c index 336297932388..56bc0e1662de 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6889,7 +6889,7 @@ static u8 TrySetEnigmaBerry(u32 battler) { if (IsBattlerAlive(battler) && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && ((TARGET_TURN_DAMAGED && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements) + && ((BATTLER_TURN_DAMAGED(battler) && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements) && !(gBattleScripting.overrideBerryRequirements && gBattleMons[battler].hp == gBattleMons[battler].maxHP) && (B_HEAL_BLOCKING < GEN_5 || !(gStatuses3[battler] & STATUS3_HEAL_BLOCK))) { @@ -6913,7 +6913,7 @@ static u8 DamagedStatBoostBerryEffect(u32 battler, u8 statId, u8 category) || (!DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && GetBattleMoveCategory(gCurrentMove) == category && battler != gBattlerAttacker - && TARGET_TURN_DAMAGED)) + && BATTLER_TURN_DAMAGED(battler))) ) { BufferStatChange(battler, statId, STRINGID_STATROSE); @@ -10936,6 +10936,10 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) if (GetBattlerTeraType(battler) == formChanges[i].param1) targetSpecies = formChanges[i].targetSpecies; break; + case FORM_CHANGE_BATTLE_ATTACK: + case FORM_CHANGE_BATTLE_KINGS_SHIELD: + targetSpecies = formChanges[i].targetSpecies; + break; } } } diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 05c8391522ce..180d0d74ac60 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -12217,7 +12217,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = { .name = COMPOUND_STRING("Coil"), .description = COMPOUND_STRING( - "Coils up to raise Attack\n" + "Coils up to raise Attack,\n" "Defense and Accuracy."), .effect = EFFECT_COIL, .power = 0, @@ -17335,7 +17335,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .name = COMPOUND_STRING("Octolock"), .description = COMPOUND_STRING( "Traps the foe to lower Def\n" - "and Sp. Def fall each turn."), + "and Sp. Def each turn."), .effect = EFFECT_OCTOLOCK, .power = 0, .type = TYPE_FIGHTING, @@ -20610,6 +20610,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, + .minimizeDoubleDamage = TRUE, .battleAnimScript = gBattleAnimMove_SupercellSlam, }, diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 46dec5354435..4af68ce31825 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -789,9 +789,11 @@ static const struct FormChange sFurfrouFormChangeTable[] = { #if P_FAMILY_HONEDGE static const struct FormChange sAegislashFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_AEGISLASH_SHIELD}, - {FORM_CHANGE_FAINT, SPECIES_AEGISLASH_SHIELD}, - {FORM_CHANGE_END_BATTLE, SPECIES_AEGISLASH_SHIELD}, + {FORM_CHANGE_BATTLE_ATTACK, SPECIES_AEGISLASH_BLADE}, + {FORM_CHANGE_BATTLE_KINGS_SHIELD, SPECIES_AEGISLASH_SHIELD}, + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_AEGISLASH_SHIELD}, + {FORM_CHANGE_FAINT, SPECIES_AEGISLASH_SHIELD}, + {FORM_CHANGE_END_BATTLE, SPECIES_AEGISLASH_SHIELD}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_HONEDGE diff --git a/test/battle/ability/dauntless_shield.c b/test/battle/ability/dauntless_shield.c index ca9125e14f26..ada4ace7866a 100644 --- a/test/battle/ability/dauntless_shield.c +++ b/test/battle/ability/dauntless_shield.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(B_PROTEAN_LIBERO == GEN_9); + ASSUME(B_DAUNTLESS_SHIELD == GEN_9); } SINGLE_BATTLE_TEST("Dauntless Shield raises Defense by one stage") diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index 1278a45866fd..e0f97d5bdaf2 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -246,7 +246,7 @@ DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out thr } } -SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutralizing Gas") +SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutralizing Gas - switching out") { GIVEN { PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } @@ -263,3 +263,91 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutral SEND_IN_MESSAGE("Wobbuffet"); } } + +SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves") +{ + u32 move; + PARAMETRIZE { move = MOVE_U_TURN; } + PARAMETRIZE { move = MOVE_HEALING_WISH; } + PARAMETRIZE { move = MOVE_BATON_PASS; } + GIVEN { + ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE); + ASSUME(gMovesInfo[MOVE_HEALING_WISH].effect == EFFECT_HEALING_WISH); + ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS); + PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { MOVE(player, move); SEND_OUT(player, 1); } + } SCENE { + ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + MESSAGE("Neutralizing gas filled the area!"); + ANIMATION(ANIM_TYPE_MOVE, move, player); + MESSAGE("The effects of the neutralizing gas wore off!"); + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + SEND_IN_MESSAGE("Wobbuffet"); + } THEN { + if (move == MOVE_HEALING_WISH) + EXPECT_EQ(player->hp, player->maxHP); + } +} + +SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - opponent caused switches") +{ + u32 move, item; + PARAMETRIZE { move = MOVE_TACKLE; item = ITEM_EJECT_BUTTON; } + PARAMETRIZE { move = MOVE_GROWL; item = ITEM_EJECT_PACK; } + PARAMETRIZE { move = MOVE_ROAR; item = ITEM_NONE; } + PARAMETRIZE { move = MOVE_DRAGON_TAIL; item = ITEM_NONE; } + GIVEN { + ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON); + ASSUME(gItemsInfo[ITEM_EJECT_PACK].holdEffect == HOLD_EFFECT_EJECT_PACK); + ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN); + ASSUME(gMovesInfo[MOVE_ROAR].effect == EFFECT_ROAR); + ASSUME(gMovesInfo[MOVE_DRAGON_TAIL].effect == EFFECT_HIT_SWITCH_TARGET); + PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); Item(item); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + if (item != ITEM_NONE) { + TURN { MOVE(opponent, move); SEND_OUT(player, 1); } + } else { + TURN { MOVE(opponent, move); } + } + } SCENE { + ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + MESSAGE("Neutralizing gas filled the area!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + if (item != ITEM_NONE) + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("The effects of the neutralizing gas wore off!"); + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + if (item != ITEM_NONE) { + SEND_IN_MESSAGE("Wobbuffet"); + } else { + MESSAGE("Wobbuffet was dragged out!"); + } + } +} + +SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_FELL_STINGER].effect == EFFECT_FELL_STINGER); + PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_FELL_STINGER); SEND_OUT(player, 1); } + } SCENE { + ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + MESSAGE("Neutralizing gas filled the area!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponent); + MESSAGE("The effects of the neutralizing gas wore off!"); + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + MESSAGE("Weezing fainted!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + SEND_IN_MESSAGE("Wobbuffet"); + } +} + diff --git a/test/battle/hold_effect/blunder_policy.c b/test/battle/hold_effect/blunder_policy.c new file mode 100644 index 000000000000..552ad2f6fbd5 --- /dev/null +++ b/test/battle/hold_effect/blunder_policy.c @@ -0,0 +1,67 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gItemsInfo[ITEM_BLUNDER_POLICY].holdEffect == HOLD_EFFECT_BLUNDER_POLICY); +} + +SINGLE_BATTLE_TEST("Blunder Policy raises the users speed by 2 stages if the user misses") +{ + PASSES_RANDOMLY(3, 10, RNG_ACCURACY); + GIVEN { + ASSUME(gMovesInfo[MOVE_FOCUS_BLAST].accuracy == 70); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_BLUNDER_POLICY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FOCUS_BLAST); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_BLAST, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 2); + } +} + + +SINGLE_BATTLE_TEST("Blunder Policy will never trigger if the move fails due to an immunity") +{ + PASSES_RANDOMLY(10, 10, RNG_ACCURACY); + GIVEN { + ASSUME(gMovesInfo[MOVE_FOCUS_BLAST].accuracy == 70); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_BLUNDER_POLICY); } + OPPONENT(SPECIES_GASTLY); + } WHEN { + TURN { MOVE(player, MOVE_FOCUS_BLAST); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_BLAST, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + } + } THEN { + EXPECT(player->item == ITEM_BLUNDER_POLICY); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Blunder Policy will never trigger if the move fails due to Protect") +{ + PASSES_RANDOMLY(10, 10, RNG_ACCURACY); + GIVEN { + ASSUME(gMovesInfo[MOVE_FOCUS_BLAST].accuracy == 70); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_BLUNDER_POLICY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_FOCUS_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_BLAST, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + } + } THEN { + EXPECT(player->item == ITEM_BLUNDER_POLICY); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/hold_effect/enigma_berry.c b/test/battle/hold_effect/enigma_berry.c index 5a058eed1b48..762774f25d25 100644 --- a/test/battle/hold_effect/enigma_berry.c +++ b/test/battle/hold_effect/enigma_berry.c @@ -58,3 +58,19 @@ SINGLE_BATTLE_TEST("Enigma Berry does nothing if Heal Block applies") } } } + +DOUBLE_BATTLE_TEST("Enigma Berry doesn't trigger if partner was hit") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { Item(ITEM_ENIGMA_BERRY); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); + } THEN { + EXPECT(opponentRight->item == ITEM_ENIGMA_BERRY); + } +} diff --git a/test/battle/hold_effect/kee_berry.c b/test/battle/hold_effect/kee_berry.c index 2375e65c2fb6..26cd2152a188 100644 --- a/test/battle/hold_effect/kee_berry.c +++ b/test/battle/hold_effect/kee_berry.c @@ -73,3 +73,19 @@ SINGLE_BATTLE_TEST("Kee Berry doesn't trigger if the item hold user used a physi EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); } } + +DOUBLE_BATTLE_TEST("Kee Berry doesn't trigger if partner was hit") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { Item(ITEM_KEE_BERRY); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); + } THEN { + EXPECT(opponentRight->item == ITEM_KEE_BERRY); + } +} diff --git a/test/battle/hold_effect/maranga_berry.c b/test/battle/hold_effect/maranga_berry.c index 6d12f257a76f..77cdaddf42f5 100644 --- a/test/battle/hold_effect/maranga_berry.c +++ b/test/battle/hold_effect/maranga_berry.c @@ -73,3 +73,19 @@ SINGLE_BATTLE_TEST("Maranga Berry doesn't trigger if the item hold user used a s EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); } } + +DOUBLE_BATTLE_TEST("Maranga Berry doesn't trigger if partner was hit") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { Item(ITEM_MARANGA_BERRY); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); + } THEN { + EXPECT(opponentRight->item == ITEM_MARANGA_BERRY); + } +} diff --git a/test/battle/hold_effect/red_card.c b/test/battle/hold_effect/red_card.c index 8c36400c290f..aa312797b284 100644 --- a/test/battle/hold_effect/red_card.c +++ b/test/battle/hold_effect/red_card.c @@ -468,3 +468,25 @@ SINGLE_BATTLE_TEST("Red Card prevents Emergency Exit activation when triggered") } TO_DO_BATTLE_TEST("Red Card activates but fails if the attacker has Dynamaxed"); + +SINGLE_BATTLE_TEST("Red Card activates before Eject Pack") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffectSelf(MOVE_OVERHEAT, MOVE_EFFECT_SP_ATK_MINUS_2) == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_OVERHEAT); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_OVERHEAT, player); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet is switched out with the Eject Button!"); + } + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("The opposing Wobbuffet held up its Red Card against Wobbuffet!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + } +} diff --git a/test/battle/status2/confusion.c b/test/battle/status2/confusion.c index 96ed2e1362a0..4115123b3c11 100644 --- a/test/battle/status2/confusion.c +++ b/test/battle/status2/confusion.c @@ -26,3 +26,21 @@ SINGLE_BATTLE_TEST("Confusion adds a 50/33% chance to hit self with 40 power") EXPECT_EQ(damage[0], damage[1]); } } + +SINGLE_BATTLE_TEST("Confusion self hit does not consume Gems") +{ + PASSES_RANDOMLY(B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50, 100, RNG_CONFUSION); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_TACKLE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Normal Gem strengthened Wobbuffet's power!"); + } + MESSAGE("It hurt itself in its confusion!"); + } +} diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index b410e810b084..8b939a955f8e 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -1545,6 +1545,7 @@ static void fprint_species(FILE *f, const char *prefix, struct String s) static const unsigned char *male = (unsigned char *)u8"♂"; static const unsigned char *female = (unsigned char *)u8"♀"; static const unsigned char *e_diacritic = (unsigned char *)u8"é"; + static const unsigned char *right_single_quotation_mark = (unsigned char *)u8"’"; for (int i = 0; i < s.string_n; i++) { unsigned char c = s.string[i]; @@ -1562,7 +1563,7 @@ static void fprint_species(FILE *f, const char *prefix, struct String s) underscore = false; fputc(c - 'a' + 'A', f); } - else if (c == '\'' || c == '%') + else if (c == '\'' || c == '%' || is_utf8_character(s, &i, right_single_quotation_mark)) { // Do nothing. }