diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index a06bfb5247..56368ef27c 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -43,9 +43,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using? options: - - 1.13.1 (Latest release) + - 1.13.2 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.13.1 - 1.13.0 - 1.12.3 - 1.12.2 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 505cd5e6c4..06c55598d6 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -43,9 +43,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using? options: - - 1.13.1 (Latest release) + - 1.13.2 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.13.1 - 1.13.0 - 1.12.3 - 1.12.2 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index 4abed5dbb5..56cfecf33a 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -43,9 +43,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using? options: - - 1.13.1 (Latest release) + - 1.13.2 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.13.1 - 1.13.0 - 1.12.3 - 1.12.2 diff --git a/README.md b/README.md index 21de8f622d..8f9088a6e8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you use **`pokeemerald-expansion`**, please credit **RHH (Rom Hacking Hideout)**. Optionally, include the version number for clarity. ``` -Based off RHH's pokeemerald-expansion 1.13.1 https://github.com/rh-hideout/pokeemerald-expansion/ +Based off RHH's pokeemerald-expansion 1.13.2 https://github.com/rh-hideout/pokeemerald-expansion/ ``` Please consider [crediting all contributors](CREDITS.md) involved in the project! diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index c4165c4829..8756e74236 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -199,7 +199,7 @@ BattleScript_EffectDoodle_AfterCopy: printstring STRINGID_PKMNCOPIEDFOE waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER - jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_MoveEnd + jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_EffectDoodleMoveEnd addbyte gBattleCommunication, 1 jumpifnoally BS_ATTACKER, BattleScript_EffectDoodleMoveEnd setallytonextattacker BattleScript_EffectDoodle_CopyAbility @@ -751,13 +751,13 @@ BattleScript_FlingLightBall: goto BattleScript_FlingEnd BattleScript_FlingMentalHerb: curecertainstatuses - savetarget + saveattacker copybyte gBattlerAttacker, gBattlerTarget playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, NULL printfromtable gMentalHerbCureStringIds waitmessage B_WAIT_TIME_LONG updatestatusicon BS_ATTACKER - restoretarget + restoreattacker goto BattleScript_FlingEnd BattleScript_FlingPoisonBarb: seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_POISON @@ -7632,6 +7632,13 @@ BattleScript_AbilityCuredStatus:: updatestatusicon BS_SCRIPTING return +BattleScript_AbilityCuredStatusEnd3:: + call BattleScript_AbilityPopUp + printstring STRINGID_PKMNSXCUREDITSYPROBLEM + waitmessage B_WAIT_TIME_LONG + updatestatusicon BS_SCRIPTING + end3 + BattleScript_BattlerShookOffTaunt:: call BattleScript_AbilityPopUp printstring STRINGID_PKMNSHOOKOFFTHETAUNT diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 8fd400844d..c1e6a78752 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -20,6 +20,7 @@ - [Day/Night System FAQ](tutorials/dns.md) - [Changelog](./CHANGELOG.md) - [1.13.x]() + - [Version 1.13.2](changelogs/1.13.x/1.13.2.md) - [Version 1.13.1](changelogs/1.13.x/1.13.1.md) - [Version 1.13.0](changelogs/1.13.x/1.13.0.md) - [1.12.x]() diff --git a/docs/changelogs/1.13.x/1.13.2.md b/docs/changelogs/1.13.x/1.13.2.md new file mode 100644 index 0000000000..6cd2249378 --- /dev/null +++ b/docs/changelogs/1.13.x/1.13.2.md @@ -0,0 +1,123 @@ +```md +## How to update +- 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.13.2 +`. +``` + + +## 🧬 General 🧬 +### Changed +* Removed superflous palette compression check by @hedara90 in [#7718](https://github.com/rh-hideout/pokeemerald-expansion/pull/7718) +* update of sv.json to consider newest DLC changes by @wiz1989 in [#7672](https://github.com/rh-hideout/pokeemerald-expansion/pull/7672) +* Adjusted line break substring breaking by @hedara90 in [#7789](https://github.com/rh-hideout/pokeemerald-expansion/pull/7789) +* Fixup add-new-trainer-front-pic tutorial. by @GraionDilach in [#7802](https://github.com/rh-hideout/pokeemerald-expansion/pull/7802) +* Adds conversion script for trainers.h by @AlexOn1ine in [#7663](https://github.com/rh-hideout/pokeemerald-expansion/pull/7663) + +### Fixed +* Fix bug with IF_GENDER evolution condition by @FosterProgramming in [#7749](https://github.com/rh-hideout/pokeemerald-expansion/pull/7749) +* Pokemon storage moving items bugfix by @FosterProgramming in [#7763](https://github.com/rh-hideout/pokeemerald-expansion/pull/7763) +* Fix catch bug introduced in #7774 by @FosterProgramming in [#7782](https://github.com/rh-hideout/pokeemerald-expansion/pull/7782) +* Fix Party Menu move select name width by @AsparagusEduardo in [#7820](https://github.com/rh-hideout/pokeemerald-expansion/pull/7820) +* Fix Debug Give Pokémon (Complex) with duplicate moves by @AsparagusEduardo in [#7821](https://github.com/rh-hideout/pokeemerald-expansion/pull/7821) + +## 🗺️ Overworld 🗺️ +### Changed +* Added missing `LOCALID_NONE` by @estellarc in [#7783](https://github.com/rh-hideout/pokeemerald-expansion/pull/7783) + +### Fixed +* Fix TRAINER_TYPE_SEE_ALL_DIRECTIONS by @DizzyEggg in [#7779](https://github.com/rh-hideout/pokeemerald-expansion/pull/7779) + +## 🐉 Pokémon 🐉 +### Fixed +* fix: seen flags for first mon in enemy party by @khbsd in [#7791](https://github.com/rh-hideout/pokeemerald-expansion/pull/7791) + +## ⚔️ Battle General ⚔️ +### Fixed +* Fix most failed tests with `GEN_LATEST` = `GEN_7` by @AsparagusEduardo in [#7688](https://github.com/rh-hideout/pokeemerald-expansion/pull/7688) +* Fixes Sweet Veil not protecting sleep from Yawn status by @AlexOn1ine in [#7704](https://github.com/rh-hideout/pokeemerald-expansion/pull/7704) +* Fix incorrect wrap turn amount by @AlexOn1ine in [#7667](https://github.com/rh-hideout/pokeemerald-expansion/pull/7667) +* Fixes Rage Fist for gen7 Disguise by @AlexOn1ine in [#7692](https://github.com/rh-hideout/pokeemerald-expansion/pull/7692) +* Fixes Intrepid Sword/Dauntless Shield boosting after entering while at max stats by @PhallenTree in [#7716](https://github.com/rh-hideout/pokeemerald-expansion/pull/7716) +* Fixes incorrect ending for some scripts by @AlexOn1ine in [#7691](https://github.com/rh-hideout/pokeemerald-expansion/pull/7691) +* Fixes Uproar not waking up mons by @AlexOn1ine in [#7714](https://github.com/rh-hideout/pokeemerald-expansion/pull/7714) +* Fixes Endure and Eject Pack issues by @AlexOn1ine in [#7687](https://github.com/rh-hideout/pokeemerald-expansion/pull/7687) +* Fix Beak Blast burning fire types by @hedara90 in [#7740](https://github.com/rh-hideout/pokeemerald-expansion/pull/7740) +* Fixes Recharge not actually being removed when recharge turn occurs by @PhallenTree in [#7744](https://github.com/rh-hideout/pokeemerald-expansion/pull/7744) +* Bugfixes Batch by @AlexOn1ine in [#7739](https://github.com/rh-hideout/pokeemerald-expansion/pull/7739) +* Fixes Beat Up incorrect slots usage by @AlexOn1ine in [#7741](https://github.com/rh-hideout/pokeemerald-expansion/pull/7741) +* Fixes Mycelium Might and Lagging Tail adjusting prio incorrectly by @AlexOn1ine in [#7742](https://github.com/rh-hideout/pokeemerald-expansion/pull/7742) +* Wrong argument passed down by @AlexOn1ine in [#7751](https://github.com/rh-hideout/pokeemerald-expansion/pull/7751) +* Fixed Ball Fetch Ability by @bassforte123 in [#7764](https://github.com/rh-hideout/pokeemerald-expansion/pull/7764) +* Fixes Flower Shield affecting semi-invulnerable mons by @AlexOn1ine in [#7766](https://github.com/rh-hideout/pokeemerald-expansion/pull/7766) +* Fixes Helping Hand boosts not stacking with each other by @PhallenTree in [#7775](https://github.com/rh-hideout/pokeemerald-expansion/pull/7775) +* Fixes OHKO moves calculating accuracy twice by @AlexOn1ine in [#7785](https://github.com/rh-hideout/pokeemerald-expansion/pull/7785) +* Fixes Instructed moves looking at the wrong turn order number by @PhallenTree in [#7788](https://github.com/rh-hideout/pokeemerald-expansion/pull/7788) +* Fix Flame Burst timeout if primary target is fainted by @hedara90 in [#7793](https://github.com/rh-hideout/pokeemerald-expansion/pull/7793) +* Fixes Leppa Berry timings by @AlexOn1ine in [#7787](https://github.com/rh-hideout/pokeemerald-expansion/pull/7787) +* Fixes Effects activating when move wasn't successful by @AlexOn1ine in [#7803](https://github.com/rh-hideout/pokeemerald-expansion/pull/7803) +* Fixes Throat Spray being blocked by Sheer Force by @AlexOn1ine in [#7808](https://github.com/rh-hideout/pokeemerald-expansion/pull/7808) +* Fixes inaccurate save / restore in Fling script by @AlexOn1ine in [#7811](https://github.com/rh-hideout/pokeemerald-expansion/pull/7811) +* Fix test exit prints for stored battlers by @AlexOn1ine in [#7807](https://github.com/rh-hideout/pokeemerald-expansion/pull/7807) +* Fixes EndTurn Eject Pack by @AlexOn1ine in [#7813](https://github.com/rh-hideout/pokeemerald-expansion/pull/7813) +* Fix Battle Frontier using Strange Balls by @AsparagusEduardo in [#7823](https://github.com/rh-hideout/pokeemerald-expansion/pull/7823) +* Fix Throat Spray activating multiply times by @AlexOn1ine in [#7818](https://github.com/rh-hideout/pokeemerald-expansion/pull/7818) +* fix (choice lock): Gorilla Tactics interactions with choice item removal by @ghostyboyy97 in [#7824](https://github.com/rh-hideout/pokeemerald-expansion/pull/7824) + - Fixed interactions with choice items and Gorilla Tactics both present when choice item is removed by a thief effect or item swap effect. +* Fixes encore random target for gen5+ by @AlexOn1ine in [#7800](https://github.com/rh-hideout/pokeemerald-expansion/pull/7800) + +## 🤹 Moves 🤹 +### Changed +* Initial Lash Out tests by @grintoul1 in [#7769](https://github.com/rh-hideout/pokeemerald-expansion/pull/7769) + +### Fixed +* Fix Salt Cure in double battles by @Bassoonian in [#7797](https://github.com/rh-hideout/pokeemerald-expansion/pull/7797) + +## 🎭 Abilities 🎭 +### Fixed +* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748) +* Fix Forecast and Flower Gift corruption by @Bassoonian in [#7796](https://github.com/rh-hideout/pokeemerald-expansion/pull/7796) +* Immunity abilities trigger on turn 0 (leads) by @spindrift64 in [#7814](https://github.com/rh-hideout/pokeemerald-expansion/pull/7814) + +## 🤖 Battle AI 🤖 +### Changed +* Tidy up CanTargetFaintAiWithMod and CanTargetMoveFaintAi by @grintoul1 in [#7693](https://github.com/rh-hideout/pokeemerald-expansion/pull/7693) +* Doubles AI: Trick Room timer fix and test for DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE by @grintoul1 in [#7622](https://github.com/rh-hideout/pokeemerald-expansion/pull/7622) + +### Fixed +* Toxic thread uses light screen's scoring.... by @surskitty in [#7674](https://github.com/rh-hideout/pokeemerald-expansion/pull/7674) +* Fix most failed and assume fail tests with `GEN_LATEST` = `GEN_6` by @AsparagusEduardo in [#7696](https://github.com/rh-hideout/pokeemerald-expansion/pull/7696) +* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748) + +## 🧹 Other Cleanup 🧹 +* Tidy up CanTargetFaintAiWithMod and CanTargetMoveFaintAi by @grintoul1 in [#7693](https://github.com/rh-hideout/pokeemerald-expansion/pull/7693) +* Removed superflous palette compression check by @hedara90 in [#7718](https://github.com/rh-hideout/pokeemerald-expansion/pull/7718) +* Fix failing test for B_PREFERRED_ICE_WEATHER = B_ICE_WEATHER_SNOW by @phexmiau in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755) +* Adjusted line break substring breaking by @hedara90 in [#7789](https://github.com/rh-hideout/pokeemerald-expansion/pull/7789) +* Added missing `LOCALID_NONE` by @estellarc in [#7783](https://github.com/rh-hideout/pokeemerald-expansion/pull/7783) + +## 🧪 Test Runner 🧪 +### Changed +* Add tests for Filter, Solid Rock and Prism Armor by @hedara90 in [#7734](https://github.com/rh-hideout/pokeemerald-expansion/pull/7734) +* Fix failing test for B_PREFERRED_ICE_WEATHER = B_ICE_WEATHER_SNOW by @phexmiau in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755) +* Initial Lash Out tests by @grintoul1 in [#7769](https://github.com/rh-hideout/pokeemerald-expansion/pull/7769) +* Improve how test involving ball throw work by @FosterProgramming in [#7774](https://github.com/rh-hideout/pokeemerald-expansion/pull/7774) + +### Fixed +* Fix EWRAM_INIT in tests and add a default state to test runner main loop by @hedara90 in [#7699](https://github.com/rh-hideout/pokeemerald-expansion/pull/7699) +* Fix most failed and assume fail tests with `GEN_LATEST` = `GEN_6` by @AsparagusEduardo in [#7696](https://github.com/rh-hideout/pokeemerald-expansion/pull/7696) +* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748) +* Fix Big Root tests by @hedara90 in [#7817](https://github.com/rh-hideout/pokeemerald-expansion/pull/7817) + +## 📚 Documentation 📚 +* Fixup add-new-trainer-front-pic tutorial. by @GraionDilach in [#7802](https://github.com/rh-hideout/pokeemerald-expansion/pull/7802) + +## New Contributors +* @phexmiau made their first contribution in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755) +* @ghostyboyy97 made their first contribution in [#7824](https://github.com/rh-hideout/pokeemerald-expansion/pull/7824) + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.13.1...expansion/1.13.2 + + + + diff --git a/include/battle.h b/include/battle.h index 99f5b6ba8e..8f6498061b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -598,7 +598,8 @@ struct PartyState u32 supersweetSyrup:1; u32 timesGotHit:5; u32 changedSpecies:11; // For forms when multiple mons can change into the same pokemon. - u32 padding:10; + u32 sentOut:1; + u32 padding:9; }; // Cleared at the beginning of the battle. Fields need to be cleared when needed manually otherwise. diff --git a/include/battle_scripts.h b/include/battle_scripts.h index de01888a6d..28c24f6eff 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -198,6 +198,7 @@ extern const u8 BattleScript_AbilityStatusEffect[]; extern const u8 BattleScript_SynchronizeActivates[]; extern const u8 BattleScript_NoItemSteal[]; extern const u8 BattleScript_AbilityCuredStatus[]; +extern const u8 BattleScript_AbilityCuredStatusEnd3[]; extern const u8 BattleScript_IgnoresWhileAsleep[]; extern const u8 BattleScript_IgnoresAndUsesRandomMove[]; extern const u8 BattleScript_MoveUsedLoafingAround[]; diff --git a/include/battle_util.h b/include/battle_util.h index f31df51ccc..47c5238f19 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -58,6 +58,7 @@ enum { ABILITYEFFECT_OPPORTUNIST, ABILITYEFFECT_OPPORTUNIST_FIRST_TURN, ABILITYEFFECT_SWITCH_IN_STATUSES, + ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, }; // For the first argument of ItemBattleEffects, to deteremine which block of item effects to try @@ -329,6 +330,7 @@ struct Pokemon *GetIllusionMonPtr(u32 battler); void ClearIllusionMon(u32 battler); u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler); bool32 SetIllusionMon(struct Pokemon *mon, u32 battler); +u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID); bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); enum DamageCategory GetBattleMoveCategory(u32 move); void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move); @@ -409,6 +411,7 @@ bool32 IsPursuitTargetSet(void); void ClearPursuitValuesIfSet(u32 battler); void ClearPursuitValues(void); bool32 HasWeatherEffect(void); +bool32 IsAnyTargetAffected(u32 battlerAtk); bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move); bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); void UpdateStallMons(void); diff --git a/include/config/battle.h b/include/config/battle.h index 8533c339a9..20fcc85109 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -317,6 +317,7 @@ #define B_TOXIC_REVERSAL GEN_LATEST // In Gen5+, bad poison will change to regular poison at the end of battles. #define B_TRY_CATCH_TRAINER_BALL GEN_LATEST // In Gen4+, trying to catch a Trainer's Pokémon does not consume the Poké Ball. #define B_SLEEP_CLAUSE FALSE // Enables Sleep Clause all the time in every case, overriding B_FLAG_SLEEP_CLAUSE. Use that for modularity. +#define B_PARTNER_MONS_MARKED_SEEN FALSE // If TRUE, if your double battle partner sends out a Pokémon you haven't encountered yet, it will be marked as SEEN in your Pokédex. #define NUM_BEEPS_GEN_LATEST 4 // Loops 4 times #define NUM_BEEPS_GEN_3 -1 // Loops infinitely diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index d00b8331de..49b83b3a19 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -153,6 +153,7 @@ enum MoveEndEffects MOVEEND_MULTIHIT_MOVE, MOVEEND_MOVE_BLOCK, MOVEEND_ITEM_EFFECTS_ATTACKER, + MOVEEND_ITEM_THROAT_SPRAY, MOVEEND_ABILITY_BLOCK, MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist MOVEEND_COLOR_CHANGE, // Color Change / Berserk / Anger Shell diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 908f474058..3a4dd2e1fd 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -1,7 +1,7 @@ #ifndef GUARD_CONSTANTS_EXPANSION_H #define GUARD_CONSTANTS_EXPANSION_H -// Last version: 1.13.1 +// Last version: 1.13.2 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 14 #define EXPANSION_VERSION_PATCH 0 diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index b365a6ea8f..7a9aed85c3 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -41,6 +41,10 @@ enum GenConfigTag GEN_CONFIG_PRANKSTER_DARK_TYPES, GEN_CONFIG_DESTINY_BOND_FAIL, GEN_CONFIG_POWDER_RAIN, + GEN_CONFIG_POWDER_GRASS, + GEN_CONFIG_OBLIVIOUS_TAUNT, + GEN_CONFIG_TOXIC_NEVER_MISS, + GEN_CONFIG_PARALYZE_ELECTRIC, GEN_CONFIG_COUNT }; diff --git a/include/generational_changes.h b/include/generational_changes.h index a45325caab..30739a14f5 100644 --- a/include/generational_changes.h +++ b/include/generational_changes.h @@ -44,6 +44,10 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] = [GEN_CONFIG_PRANKSTER_DARK_TYPES] = B_PRANKSTER_DARK_TYPES, [GEN_CONFIG_DESTINY_BOND_FAIL] = B_DESTINY_BOND_FAIL, [GEN_CONFIG_POWDER_RAIN] = B_POWDER_RAIN, + [GEN_CONFIG_POWDER_GRASS] = B_POWDER_GRASS, + [GEN_CONFIG_OBLIVIOUS_TAUNT] = B_OBLIVIOUS_TAUNT, + [GEN_CONFIG_TOXIC_NEVER_MISS] = B_TOXIC_NEVER_MISS, + [GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC, }; #if TESTING diff --git a/include/pokemon.h b/include/pokemon.h index 04016af4c8..0d3add4941 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -853,6 +853,7 @@ u8 GetOpposingLinkMultiBattlerId(bool8 rightSide, u8 multiplayerId); u16 FacilityClassToPicIndex(u16 facilityClass); u16 PlayerGenderToFrontTrainerPicId(u8 playerGender); void HandleSetPokedexFlag(enum NationalDexOrder nationalNum, u8 caseId, u32 personality); +void HandleSetPokedexFlagFromMon(struct Pokemon *mon, u32 caseId); bool8 HasTwoFramesAnimation(u16 species); struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 managerId, u8 mode); void DestroyMonSpritesGfxManager(u8 managerId); diff --git a/migration_scripts/1.13/convert_trainers.py b/migration_scripts/1.13/convert_trainers.py new file mode 100644 index 0000000000..ba48f5465e --- /dev/null +++ b/migration_scripts/1.13/convert_trainers.py @@ -0,0 +1,401 @@ +import re +import sys +import os + +is_blank = re.compile(r'^[ \t]*(//.*)?$') + +begin_party_definition = re.compile(r'struct TrainerMon (\w+)\[\] =') +end_party_definition = re.compile(r'^ },') +begin_pokemon_definition = re.compile(r'^ { *$') +end_pokemon_definition = re.compile(r'^ },? *$') +level_definition = re.compile(r'\.lvl = (\d+)') +species_definition = re.compile(r'\.species = SPECIES_(\w+)') +gender_definition = re.compile(r'\.gender = TRAINER_MON_(\w+)') +nickname_definition = re.compile(r'\.nickname = COMPOUND_STRING\("([^"]+)"\)') +item_definition = re.compile(r'\.heldItem = ITEM_(\w+)') +ball_definition = re.compile(r'\.ball = ITEM_(\w+)') +ability_definition = re.compile(r'\.ability = ABILITY_(\w+)') +friendship_definition = re.compile(r'\.friendship = (\d+)') +shiny_definition = re.compile(r'\.isShiny = (\w+)') +ivs_definition = re.compile(r'\.iv = TRAINER_PARTY_IVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)') +evs_definition = re.compile(r'\.ev = TRAINER_PARTY_EVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)') +moves_definition = re.compile(r'\.moves = \{([^}]+)\}') +move_definition = re.compile(r'MOVE_(\w+)') +nature_definition = re.compile(r'NATURE_(\w+)') + +is_trainer_skip = re.compile(r'(const struct Trainer gTrainers\[\] = \{)|(^ \{$)|(\.partySize =)|(\.party = NULL)|(\.mugshotEnabled = TRUE)|(\};)') + +trainer_normal_definition = re.compile(r' \[DIFFICULTY_NORMAL\]\[(TRAINER_\w+)\] =') +trainer_easy_definition = re.compile(r' \[DIFFICULTY_EASY\]\[(TRAINER_\w+)\] =') +trainer_hard_definition = re.compile(r' \[DIFFICULTY_HARD\]\[(TRAINER_\w+)\] =') +end_pokemon_definition = re.compile(r' },') +trainer_class_definition = re.compile(r'\.trainerClass = TRAINER_CLASS_(\w+)') +encounter_music_gender_definition = re.compile(r'\.encounterMusic_gender = (F_TRAINER_FEMALE \| )?TRAINER_ENCOUNTER_MUSIC_(\w+)') +encounter_music_definition = re.compile(r'TRAINER_ENCOUNTER_MUSIC_(\w+)') +trainer_pic_definition = re.compile(r'\.trainerPic = TRAINER_PIC_(\w+)') +trainer_name_definition = re.compile(r'\.trainerName = _\("([^"]*)"\)') +trainer_items_definition = re.compile(r'\.items = \{([^}]*)\}') +trainer_item_definition = re.compile(r'ITEM_(\w+)') +trainer_double_battle_definition = re.compile(r'\.battleType = (\w+)') +trainer_ai_flags_definition = re.compile(r'\.aiFlags = (.*)') +trainer_ai_flag_definition = re.compile(r'AI_FLAG_(\w+)') +trainer_party_definition = re.compile(r'\.party = ') +trainer_mugshot_definition = re.compile(r'\.mugshotColor = MUGSHOT_COLOR_(\w+)') +trainer_starting_status_definition = re.compile(r'\.startingStatus = STARTING_STATUS_(\w+)') + +# NOTE: These are just for aesthetics, the Pokemon would still compile +# without them. +species_replacements = { + "CHIEN_PAO": "Chien-Pao", + "CHI_YU": "Chi-Yu", + "HAKAMO_O": "Hakamo-o", + "HO_OH": "Ho-Oh", + "JANGMO_O": "Jangmo-o", + "KOMMO_O": "Kommo-o", + "PORYGON_Z": "Porygon-Z", + "ROTOM_": "Rotom-", + "TING_LU": "Ting-Lu", + "TYPE_NULL": "Type: Null", + "WO_CHIEN": "Wo-Chien", + + "_ALOLAN": "-Alola", + "_AQUA_BREED": "-Aqua", + "_BATTLE_BOND": "-Bond", + "_BLAZE_BREED": "-Blaze", + "_CAP": "", + "_CLOAK": "", + "_COMBAT_BREED": "-Combat", + "_CROWED_SHIELD": "-Crowned", + "_CROWED_SWORD": "-Crowned", + "_DRIVE": "", + "_EAST_SEA": "-East", + "_FAMILY_OF_FOUR": "-Four", + "_FEMALE": "-F", + "_FLOWER": "", + "_GALARIAN": "-Galar", + "_GIGANTAMAX": "-Gmax", + "_HISUIAN": "-Hisui", + "_ICE_RIDER": "-Ice", + "_NOICE_FACE": "-Noice", + "_ORIGIN": "-Origin", + "_ORIGINAL_COLOR": "-Original", + "_PALDEAN": "-Paldea", + "_PLUMAGE": "", + "_POKE_BALL": "-Pokeball", + "_SHADOW_RIDER": "-Shadow", + "_STRIKE_STYLE": "-Style", + "_TOTEM": "-Totem", + "_ZEN_MODE": "-Zen", +} + +class_fixups = { + "Rs": "RS", +} + +pic_fixups = { + "Rs": "RS", +} + +pokemon_attribute_order = ['Level', 'Ability', 'IVs', 'EVs', 'Happiness', 'Shiny', 'Ball'] + +class Pokemon: + def __init__(self): + self.nickname = None + self.species = None + self.gender = None + self.item = None + self.nature = None + self.attributes = {} + self.attributes['IVs'] = "0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe" + self.moves = [] + + +class Trainer: + def __init__(self, id_): + self.id = id_ + self.class_ = None + self.encounter_music = None + self.gender = None + self.pic = None + self.name = None + self.items = [] + self.double_battle = None + self.ai_flags = None + self.mugshot = None + self.starting_status = None + self.party = None + self.difficulty = None + + +def write_tutorial(output): + output.write('/*\n') + output.write('Trainers and their parties defined with Competetive Syntax.\n') + output.write('Compatible with Pokemon Showdown exports.\n') + output.write('https://github.com/smogon/pokemon-showdown/blob/master/sim/TEAMS.md\n') + output.write('\n') + output.write('A trainer specification starts with "=== TRAINER_XXXX ==="\n') + output.write('and includes everything until the next line that starts with "==="\n') + output.write('or the file ends.\n') + output.write('\n') + output.write('A blank line is required between the trainer and their Pokemon\n') + output.write('and between their Pokemon.\n') + output.write('TRAINER_XXXX is how the trainer is referred to within code.\n') + output.write('Fields with description and/or example of usage\n') + output.write('Required fields for trainers:\n') + output.write(' - Name\n') + output.write(' - Pic\n') + output.write('Optional (but still recommended) fields for trainers:\n') + output.write(' - Class (if not specified, PkMn Trainer will be used)\n') + output.write(' - Gender (Male/Female, affects random gender weights of party if not specified)\n') + output.write(' - Music\n') + output.write(' - Items (Some Item / Another Item / Third Item)\n') + output.write(' (Can also be specified with ITEM_SOME_ITEM)\n') + output.write(' - Battle Type (Singles / Doubles, defaults to Singles)\n') + output.write(' - AI (Ai Flag / Another Flag / Third Flag / ...\n') + output.write(' see "constants/battle_ai.h" for all flags)\n') + output.write(' - Mugshot (enable Mugshots during battle transition\n') + output.write(' set to one of Purple, Green, Pink, Blue or Yellow)\n') + output.write(' - Starting Status (see include/constants/battle.h for values)\n') + output.write('\n') + output.write('Pokemon are then specified using the Showdown Export format.\n') + output.write("If a field is not specified, it will use it's default value.\n") + output.write("\n") + output.write('Required fields for Pokemon:\n') + output.write(' - Species (Either as SPECIES_ABRA or Abra)\n') + output.write(' This line also specifies Gender, Nickname and Held item.\n') + output.write(' Alfred (Abra) (M) @ Eviolite\n') + output.write(' Roberta (SPECIES_ABRA) (F) @ ITEM_CHOICE_SPECS\n') + output.write(' Both lines are valid. Gender (M) or (F) must use a capital letter.\n') + output.write(' Nickname length is limited to 10 characters using standard letters.\n') + output.write(" With narrow font it's increased to 12. Longer strings will be silently shortened.\n") + output.write('\n') + output.write('Optional fields for Pokemon:\n') + output.write(' - Level (Number between 1 and 100, defaults to 100)\n') + output.write(' - Ability (Ability Name or ABILITY_ABILITY_NAME)\n') + output.write(' - IVs (0 HP / 1 Atk / 2 Def / 3 SpA / 4 SpD / 5 Spe, defaults to all 31)\n') + output.write(' (Order does not matter)\n') + output.write(' - EVs (252 HP / 128 Spe / 48 Def, defaults to all 0, is not capped at 512 total)\n') + output.write(' (Order does not matter)\n') + output.write(' - Ball (Poke Ball or ITEM_POKE_BALL, defaults to Poke Ball)\n') + output.write(' - Happiness (Number between 1 and 255)\n') + output.write(' - Nature (Rash or NATURE_RASH, defaults to Hardy)\n') + output.write(' - Shiny (Yes/No, defaults to No)\n') + output.write(' - Dynamax Level (Number between 0 and 10, default 10, also sets "shouldDynamax" to True)\n') + output.write(' - Gigantamax (Yes/No, sets to Gigantamax factor)\n') + output.write(' (doesn\'t do anything to Pokemon without a Gigantamax form, also sets "shouldDynamax" to True)\n') + output.write(' - Tera Type (Set to a Type, either Fire or TYPE_FIRE, also sets "shouldTerastal" to True)\n') + output.write('Moves are defined with a - (dash) followed by a single space, then the move name.\n') + output.write('Either "- Tackle" or "- MOVE_TACKLE" works. One move per line.\n') + output.write('Moves have to be the last lines of a Pokemon.\n') + output.write('If no moves are specified, the Pokemon will use the last 4 moves it learns\n') + output.write('through levelup at its level.\n') + output.write("\n") + output.write('Default IVs and Level can be changed in the "main" function of tools/trainerproc/main.c\n') + output.write("\n") + output.write('This file is processed with a custom preprocessor.\n') + output.write('*/\n') + output.write("\n") + output.write('/*\n') + output.write('Comments can be added as C comment blocks\n') + output.write('// cannot be used as comments\n') + output.write('*/\n') + output.write("\n") + output.write('/*Comments can also be on a single line*/\n') + output.write("\n") + output.write("\n") + + + +def write_to_file(trainer, output): + output.write(f'=== {trainer.id} ===\n') + output.write(f'Name: {trainer.name}\n') + output.write(f'Class: {trainer.class_}\n') + output.write(f'Pic: {trainer.pic}\n') + output.write(f'Gender: {trainer.gender}\n') + output.write(f'Music: {trainer.encounter_music}\n') + if len(trainer.items) > 0: + output.write(f'Items: {trainer.items}\n') + output.write(f'Battle Type: {trainer.double_battle}\n') + if trainer.ai_flags is not None: + output.write(f'AI: {trainer.ai_flags}\n') + if trainer.mugshot: + output.write(f'Mugshot: {trainer.mugshot}\n') + if trainer.difficulty is not None: + output.write(f'Difficulty: {trainer.difficulty}\n') + + output.write(f'\n') + + for pokemon in trainer.party: + if pokemon.species is None: + continue + if pokemon.item is not None: + output.write(f'{pokemon.species} @ {pokemon.item}\n') + else: + output.write(f'{pokemon.species}\n') + # for key in pokemon.attributes: + for key in pokemon_attribute_order: + if key in pokemon.attributes: + output.write(f'{key}: {pokemon.attributes[key]}\n') + if pokemon.nature: + output.write(f'Nature: {pokemon.nature}\n') + for move in pokemon.moves: + output.write(f'- {move}\n') + output.write(f'\n') + + +def parse_trainers(content, output): + newlines = 0 + trainer = None + pokemon = None + party = [] + moves = [] + + write_tutorial(output) + + for line_no, line in enumerate(content, 1): + try: + line = line[:-1] + + # Trainer defition + if m := trainer_normal_definition.search(line): + if trainer is not None: + trainer.party = party + write_to_file(trainer, output) + trainer = None + party = [] + [id_] = m.groups() + trainer = Trainer(id_) + trainer.difficulty = None + trainer.gender = 'Male' + elif m := trainer_easy_definition.search(line): + if trainer is not None: + trainer.party = party + write_to_file(trainer, output) + trainer = None + party = [] + [id_] = m.groups() + trainer = Trainer(id_) + trainer.difficulty = "Easy" + trainer.gender = 'Male' + elif m := trainer_hard_definition.search(line): + if trainer is not None: + trainer.party = party + write_to_file(trainer, output) + trainer = None + party = [] + [id_] = m.groups() + trainer = Trainer(id_) + trainer.difficulty = "Hard" + trainer.gender = 'Male' + elif m := trainer_class_definition.search(line): + [class_] = m.groups() + class_ = class_.replace("_", " ").title() + for match, replacement in class_fixups.items(): + class_ = class_.replace(match, replacement) + trainer.class_ = class_ + elif m := encounter_music_gender_definition.search(line): + [is_female, music] = m.groups() + trainer.gender = 'Female' if is_female else 'Male' + trainer.encounter_music = music.replace("_", " ").title() + elif m := encounter_music_definition.search(line): + [music] = m.groups() + trainer.encounter_music = music.replace("_", " ").title() + elif "F_TRAINER_FEMALE" in line: + trainer.gender = 'Female' + elif m := trainer_pic_definition.search(line): + [pic] = m.groups() + pic = pic.replace("_", " ").title() + for match, replacement in pic_fixups.items(): + pic = pic.replace(match, replacement) + trainer.pic = pic + elif m := trainer_name_definition.search(line): + [name] = m.groups() + trainer.name = name + elif m := trainer_items_definition.search(line): + [items] = m.groups() + trainer.items = " / ".join(item.replace("_", " ").title() for item in trainer_item_definition.findall(items) if item != "NONE") + elif m := trainer_double_battle_definition.search(line): + [double_battle] = m.groups() + if double_battle == 'TRAINER_BATTLE_TYPE_DOUBLES': + trainer.double_battle = "Doubles" + elif double_battle == 'TRAINER_BATTLE_TYPE_SINGLES': + trainer.double_battle = "Singles" + elif m := trainer_ai_flags_definition.search(line): + [ai_flags] = m.groups() + trainer.ai_flags = " / ".join(ai_flag.replace("_", " ").title() for ai_flag in trainer_ai_flag_definition.findall(ai_flags)) + elif m := trainer_mugshot_definition.search(line): + [color] = m.groups() + trainer.mugshot = color.title() + elif m := trainer_starting_status_definition.search(line): + [starting_status] = m.groups() + trainer.starting_status = starting_status.replace("_", " ").title() + elif m := trainer_party_definition.search(line): + pokemon = Pokemon() + + # Party mons + elif end_pokemon_definition.search(line): + party.append(pokemon) + pokemon = Pokemon() + elif m := level_definition.search(line): + [level] = m.groups() + pokemon.attributes['Level'] = level + elif m := species_definition.search(line): + [species_] = m.groups() + for match, replacement in species_replacements.items(): + species_ = species_.replace(match, replacement) + pokemon.species = species_.replace("_", " ").title() + elif m := gender_definition.search(line): + [gender_] = m.groups() + if gender_ == 'MALE': + pokemon.gender = 'M' + elif gender_ == 'FEMALE': + pokemon.gender = 'F' + elif m := nickname_definition.search(line): + [nickname] = m.groups() + pokemon.nickname = nickname + elif m := item_definition.search(line): + [item_] = m.groups() + pokemon.item = item_.replace("_", " ").title() + elif m := ball_definition.search(line): + [ball] = m.groups() + pokemon.attributes['Ball'] = ball.replace("_", " ").title() + elif m := ability_definition.search(line): + [ability] = m.groups() + pokemon.attributes['Ability'] = ability.replace("_", " ").title() + elif m := friendship_definition.search(line): + [friendship] = m.groups() + pokemon.attributes['Happiness'] = friendship + elif m := shiny_definition.search(line): + [shiny] = m.groups() + if shiny == 'TRUE': + pokemon.attributes['Shiny'] = 'Yes' + elif shiny == 'FALSE': + pokemon.attributes['Shiny'] = 'No' + elif m := ivs_definition.search(line): + [hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()] + stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed} + pokemon.attributes['IVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items()) + elif m := evs_definition.search(line): + [hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()] + stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed} + pokemon.attributes['EVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items() if value != '0') + elif m := move_definition.search(line): + [move] = m.groups() + pokemon.moves.append(move.replace("_", " ").title()) + elif m := nature_definition.search(line): + [nature] = m.groups() + pokemon.nature = nature.replace("_", " ").title() + + except Exception as e: + print(f"{line_no}: {e}") + +if __name__ == '__main__': + try: + [argv0, trainers_in_path, out_path] = sys.argv + except: + print(f"usage: python3 {sys.argv[0]} ") + print("trainers.h path: src/data/trainers.h") + print("trainers.party output path: src/data/trainers.party") + else: + with open(trainers_in_path, "r") as source, open(out_path, 'w') as output: + parse_trainers(source, output) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 120d6f910d..11a54290cf 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1518,7 +1518,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva return bestMonId; } -static u32 GetFirstNonIvalidMon(u32 firstId, u32 lastId, u32 invalidMons, u32 battlerIn1, u32 battlerIn2) +static u32 GetFirstNonInvalidMon(u32 firstId, u32 lastId, u32 invalidMons, u32 battlerIn1, u32 battlerIn2) { if (!IsDoubleBattle()) return PARTY_SIZE; @@ -2301,7 +2301,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, return aceMonId; // Fallback - u32 bestMonId = GetFirstNonIvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2); + u32 bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2); if (bestMonId != PARTY_SIZE) return bestMonId; @@ -2423,7 +2423,7 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType) return aceMonId; // Fallback - bestMonId = GetFirstNonIvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2); + bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2); if (bestMonId != PARTY_SIZE) return bestMonId; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index d8d8e3a5c2..3d9457cbde 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -545,7 +545,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler) bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect) { if (ability == ABILITY_OVERCOAT - || (B_POWDER_GRASS >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS)) + || (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS)) || holdEffect == HOLD_EFFECT_SAFETY_GOGGLES) return FALSE; return TRUE; @@ -1700,7 +1700,10 @@ enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId) enum ItemHoldEffect AI_DecideHoldEffectForTurn(u32 battlerId) { - enum ItemHoldEffect holdEffect; + enum ItemHoldEffect holdEffect = HOLD_EFFECT_NONE; + + if (gBattleMons[battlerId].item == ITEM_NONE) // Failsafe for when user recorded an item but it was consumed + return holdEffect; if (!IsAiBattlerAware(battlerId)) holdEffect = gAiPartyData->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 84b3d3c0a8..8c469733c1 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -567,12 +567,14 @@ static void OpponentHandleChoosePokemon(u32 battler) } } gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; + GetBattlerPartyState(battler)->sentOut = TRUE; } else { chosenMonId = gBattleStruct->AI_monToSwitchIntoId[battler]; gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; + GetBattlerPartyState(battler)->sentOut = TRUE; } #if TESTING TestRunner_Battle_CheckSwitch(battler, chosenMonId); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 5426f0239f..58911f1518 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -285,7 +285,6 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler) else if (gBattleStruct->monToSwitchIntoId[battler] >= PARTY_SIZE || !IsValidForBattle(&gPlayerParty[gBattleStruct->monToSwitchIntoId[battler]])) { chosenMonId = GetMostSuitableMonToSwitchInto(battler, SWITCH_AFTER_KO); - if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon { s32 firstId = (IsAiVsAiBattle()) ? 0 : (PARTY_SIZE / 2); @@ -303,12 +302,14 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler) } } gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; + GetBattlerPartyState(battler)->sentOut = TRUE; } else // Mon to switch out has been already chosen. { chosenMonId = gBattleStruct->monToSwitchIntoId[battler]; gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; + GetBattlerPartyState(battler)->sentOut = TRUE; } BtlController_EmitChosenMonReturnValue(battler, B_COMM_TO_ENGINE, chosenMonId, NULL); BtlController_Complete(battler); diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 3b2c0f8cf7..2041f9ecca 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -61,8 +61,8 @@ enum EndTurnResolutionOrder ENDTURN_TERRAIN, ENDTURN_THIRD_EVENT_BLOCK, ENDTURN_EMERGENCY_EXIT_4, - ENDTURN_ABILITIES, - ENDTURN_FOURTH_EVENT_BLOCK, + ENDTURN_FORM_CHANGE_ABILITIES, + ENDTURN_EJECT_PACK, ENDTURN_DYNAMAX, ENDTURN_COUNT, }; @@ -101,13 +101,6 @@ enum ThirdEventBlock THIRD_EVENT_BLOCK_ITEMS, }; -// Form changing abilities and Eject Pack -enum FourthEventBlock -{ - FOURTH_EVENT_BLOCK_HUNGER_SWITCH, - FOURTH_EVENT_BLOCK_EJECT_PACK, -}; - static u32 GetBattlerSideForMessage(u32 side) { u32 battler = 0; @@ -1456,7 +1449,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler) return effect; } -static bool32 HandleEndTurnAbilities(u32 battler) +static bool32 HandleEndTurnFormChangeAbilities(u32 battler) { bool32 effect = FALSE; @@ -1470,6 +1463,7 @@ static bool32 HandleEndTurnAbilities(u32 battler) case ABILITY_SCHOOLING: case ABILITY_SHIELDS_DOWN: case ABILITY_ZEN_MODE: + case ABILITY_HUNGER_SWITCH: if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) effect = TRUE; default: @@ -1479,38 +1473,10 @@ static bool32 HandleEndTurnAbilities(u32 battler) return effect; } -static bool32 HandleEndTurnFourthEventBlock(u32 battler) +static bool32 HandleEndTurnEjectPack(u32 battler) { - bool32 effect = FALSE; - - switch (gBattleStruct->eventBlockCounter) - { - case FOURTH_EVENT_BLOCK_HUNGER_SWITCH: - { - enum Ability ability = GetBattlerAbility(battler); - if (ability == ABILITY_HUNGER_SWITCH) - { - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) - effect = TRUE; - } - gBattleStruct->eventBlockCounter++; - break; - } - case FOURTH_EVENT_BLOCK_EJECT_PACK: - { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); - if (holdEffect == HOLD_EFFECT_EJECT_PACK) - { - if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler)) - effect = TRUE; - } - gBattleStruct->eventBlockCounter = 0; - gBattleStruct->turnEffectsBattlerId++; - break; - } - } - - return effect; + gBattleStruct->turnEffectsBattlerId++; + return TrySwitchInEjectPack(ITEMEFFECT_NORMAL); } static bool32 HandleEndTurnDynamax(u32 battler) @@ -1590,8 +1556,8 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) = [ENDTURN_TERRAIN] = HandleEndTurnTerrain, [ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock, [ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit, - [ENDTURN_ABILITIES] = HandleEndTurnAbilities, - [ENDTURN_FOURTH_EVENT_BLOCK] = HandleEndTurnFourthEventBlock, + [ENDTURN_FORM_CHANGE_ABILITIES] = HandleEndTurnFormChangeAbilities, + [ENDTURN_EJECT_PACK] = HandleEndTurnEjectPack, [ENDTURN_DYNAMAX] = HandleEndTurnDynamax, }; diff --git a/src/battle_main.c b/src/battle_main.c index f20ae5afaf..5e64ca8cb8 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3735,6 +3735,10 @@ static void DoBattleIntro(void) gBattleStruct->overworldWeatherDone = FALSE; Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. + // mark all battlers as sent out + for (battler = 0; battler < gBattlersCount; battler++) + GetBattlerPartyState(battler)->sentOut = TRUE; + // Try to set a status to start the battle with gBattleStruct->startingStatus = 0; if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentB)) @@ -3856,6 +3860,8 @@ static void TryDoEventsBeforeFirstTurn(void) i = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, i, gBattleMons[i].ability, 0, 0) != 0) return; + if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, i, 0, 0, 0) != 0) + return; } gBattleStruct->switchInBattlerCounter = 0; gBattleStruct->eventsBeforeFirstTurnState++; @@ -5163,6 +5169,7 @@ static void TurnValuesCleanUp(bool8 var0) gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF; gBattleStruct->battlerState[i].usedEjectItem = FALSE; gProtectStructs[i].lashOutAffected = FALSE; + gDisableStructs[i].endured = FALSE; } gSideTimers[B_SIDE_PLAYER].followmeTimer = 0; @@ -5587,14 +5594,31 @@ static void HandleEndTurn_FinishBattle(void) GetMonData(GetBattlerMon(battler), MON_DATA_NICKNAME, gBattleResults.playerMon2Name); } } - else if (!IsOnPlayerSide(battler)) - { - HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[battler].species), FLAG_SET_SEEN, gBattleMons[battler].personality); - } } TryPutPokemonTodayOnAir(); } + if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK + | BATTLE_TYPE_EREADER_TRAINER + | BATTLE_TYPE_RECORDED_LINK + | BATTLE_TYPE_TRAINER_HILL + | BATTLE_TYPE_FRONTIER))) + { + for (u32 side = 0; side < NUM_BATTLE_SIDES; side++) + { + struct Pokemon *party = GetSideParty(side); + + if (side == B_SIDE_PLAYER && !B_PARTNER_MONS_MARKED_SEEN) + continue; + + for (u32 partySlot = 0; partySlot < PARTY_SIZE; partySlot++) + { + if (gBattleStruct->partyState[side][partySlot].sentOut) + HandleSetPokedexFlagFromMon(&party[partySlot], FLAG_SET_SEEN); + } + } + } + if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER @@ -5720,20 +5744,24 @@ static void TryEvolvePokemon(void) if (!(sTriedEvolving & (1u << i))) { bool32 canStopEvo = TRUE; - u32 species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_SPECIAL, i, NULL, &canStopEvo, CHECK_EVO); + enum EvolutionMode mode = EVO_MODE_BATTLE_SPECIAL; + u32 evolutionItemArg = i; + u32 species = GetEvolutionTargetSpecies(&gPlayerParty[i], mode, evolutionItemArg, NULL, &canStopEvo, CHECK_EVO); sTriedEvolving |= 1u << i; if (species == SPECIES_NONE && (gLeveledUpInBattle & (1u << i))) { gLeveledUpInBattle &= ~(1u << i); - species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL, &canStopEvo, CHECK_EVO); + mode = EVO_MODE_BATTLE_ONLY; + evolutionItemArg = gLeveledUpInBattle; + species = GetEvolutionTargetSpecies(&gPlayerParty[i], mode, evolutionItemArg, NULL, &canStopEvo, CHECK_EVO); } if (species != SPECIES_NONE) { FreeAllWindowBuffers(); gBattleMainFunc = WaitForEvoSceneToFinish; - GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL, &canStopEvo, DO_EVO); + GetEvolutionTargetSpecies(&gPlayerParty[i], mode, evolutionItemArg, NULL, &canStopEvo, DO_EVO); EvolutionScene(&gPlayerParty[i], species, canStopEvo, i); return; } diff --git a/src/battle_pike.c b/src/battle_pike.c index b86ba6d4c4..fa6bcc50cc 100644 --- a/src/battle_pike.c +++ b/src/battle_pike.c @@ -862,7 +862,7 @@ static bool8 DoesTypePreventStatus(u16 species, u32 status) break; case STATUS1_PARALYSIS: if (GetSpeciesType(species, 0) == TYPE_GROUND || GetSpeciesType(species, 1) == TYPE_GROUND - || (B_PARALYZE_ELECTRIC >= GEN_6 && (GetSpeciesType(species, 0) == TYPE_ELECTRIC || GetSpeciesType(species, 1) == TYPE_ELECTRIC))) + || (GetGenConfig(GEN_CONFIG_PARALYZE_ELECTRIC) >= GEN_6 && (GetSpeciesType(species, 0) == TYPE_ELECTRIC || GetSpeciesType(species, 1) == TYPE_ELECTRIC))) ret = TRUE; break; case STATUS1_BURN: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1636af9aee..c8de9426ba 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -966,19 +966,17 @@ static void ValidateSavedBattlerCounts(void) { if (gBattleStruct->savedAttackerCount > 0) { - // #if TESTING - // Test_ExitWithResult(TEST_RESULT_ERROR, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!"); - // #else - DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!"); - // #endif + if (TESTING) + Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!", __FILE__, __LINE__); + else + DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!"); } if (gBattleStruct->savedTargetCount > 0) { - // #if TESTING - // Test_ExitWithResult(TEST_RESULT_ERROR, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!"); - // #else - DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!"); - // #endif + if (TESTING) + Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!", __FILE__, __LINE__); + else + DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!"); } } @@ -1066,7 +1064,7 @@ bool32 IsMovePowderBlocked(struct BattleContext *ctx) if (IsPowderMove(ctx->currentMove) && (ctx->battlerAtk != ctx->battlerDef)) { - if (B_POWDER_GRASS >= GEN_6 + if (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6 && (IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GRASS) || ctx->ability[ctx->battlerDef] == ABILITY_OVERCOAT)) { gBattlerAbility = ctx->battlerDef; @@ -2809,7 +2807,8 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem) BtlController_EmitSetMonData(battlerItem, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item MarkBattlerForControllerExec(battlerItem); - gBattleStruct->choicedMove[battlerItem] = 0; + if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS) + gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; TrySaveExchangedItem(battlerItem, gLastUsedItem); } @@ -5668,7 +5667,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) gLastUsedItem = gBattleMons[gBattlerTarget].item; gBattleMons[gBattlerTarget].item = 0; if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS) - gBattleStruct->choicedMove[gBattlerTarget] = 0; + gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; CheckSetUnburden(gBattlerTarget); // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable. @@ -6084,9 +6083,12 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities - if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0)) - effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers - else + for (u16 battler = 0; battler < gBattlersCount; battler++) + { + if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0)) + effect = TRUE; + } + if(!effect) gBattleScripting.moveendState++; break; case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize @@ -6455,10 +6457,30 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_ITEM_EFFECTS_ATTACKER: + // ITEMEFFECT_MOVE_END loops over all battlers, not just attacker. + // It will executre only the first mon with an applicable item. + // So presumably it is a bug if (ItemBattleEffects(ITEMEFFECT_MOVE_END, gBattlerAttacker)) effect = TRUE; gBattleScripting.moveendState++; break; + case MOVEEND_ITEM_THROAT_SPRAY: + if (IsSoundMove(gCurrentMove) + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_THROAT_SPRAY + && IsBattlerAlive(gBattlerAttacker) + && IsAnyTargetAffected(gBattlerAttacker) + && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && !NoAliveMonsForEitherParty()) // Don't activate if battle will end + { + gLastUsedItem = gBattleMons[gBattlerAttacker].item; + gBattleScripting.battler = gBattlerAttacker; + SET_STATCHANGER(STAT_SPATK, 1, FALSE); + effect = TRUE; + BattleScriptCall(BattleScript_AttackerItemStatRaise); + } + gBattleScripting.moveendState++; + break; case MOVEEND_ABILITY_BLOCK: effect = HandleMoveEndAbilityBlock(gBattlerAttacker, gBattlerTarget, gCurrentMove); gBattleScripting.moveendState++; @@ -7148,13 +7170,7 @@ static void Cmd_switchinanim(void) battler = GetBattlerForBattleScript(cmd->battler); - if (!IsOnPlayerSide(battler) - && !(gBattleTypeFlags & (BATTLE_TYPE_LINK - | BATTLE_TYPE_EREADER_TRAINER - | BATTLE_TYPE_RECORDED_LINK - | BATTLE_TYPE_TRAINER_HILL - | BATTLE_TYPE_FRONTIER))) - HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[battler].species), FLAG_SET_SEEN, gBattleMons[battler].personality); + GetBattlerPartyState(battler)->sentOut = TRUE; gAbsentBattlerFlags &= ~(1u << battler); @@ -12516,7 +12532,7 @@ static void Cmd_settaunt(void) { CMD_ARGS(const u8 *failInstr); - if (B_OBLIVIOUS_TAUNT >= GEN_6 && GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS) + if (GetGenConfig(GEN_CONFIG_OBLIVIOUS_TAUNT) >= GEN_6 && GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS) { gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp; gLastUsedAbility = ABILITY_OBLIVIOUS; @@ -12640,8 +12656,10 @@ static void Cmd_tryswapitems(void) BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); MarkBattlerForControllerExec(gBattlerTarget); - gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; - gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE; + if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS) + gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; + if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS) + gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE; gBattlescriptCurrInstr = cmd->nextInstr; @@ -16905,6 +16923,7 @@ void BS_SwitchinAbilities(void) AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0); + AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0); if (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect()) AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0); diff --git a/src/battle_tower.c b/src/battle_tower.c index 950ff10f5f..3cf63ba080 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1649,8 +1649,8 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 SetMonData(dst, MON_DATA_TERA_TYPE, &data); } - - SetMonData(dst, MON_DATA_POKEBALL, &ball); + if (ball != BALL_STRANGE) + SetMonData(dst, MON_DATA_POKEBALL, &ball); CalculateMonStats(dst); } diff --git a/src/battle_util.c b/src/battle_util.c index 5d9f1e469b..8abb613adc 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -66,7 +66,6 @@ static u32 GetFlingPowerFromItemId(u32 itemId); static void SetRandomMultiHitCounter(); static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); static bool32 CanBeInfinitelyConfused(u32 battler); -static bool32 IsAnyTargetAffected(u32 battlerAtk); static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, enum Ability abilityAffected, const u8 *battleScript, enum FunctionCallOption option); static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option); @@ -420,7 +419,8 @@ void HandleAction_UseMove(void) { gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove; gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos; - gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); + if (GetGenConfig(GEN_CONFIG_ENCORE_TARGET) < GEN_5) + gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); } // check if the encored move wasn't overwritten else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE @@ -4998,7 +4998,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, enum Ability ability, u32 spec case ABILITY_EFFECT_SPORE: { enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker); - if ((!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) || B_POWDER_GRASS < GEN_6) + if ((GetGenConfig(GEN_CONFIG_POWDER_GRASS) < GEN_6 || !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS)) && abilityAtk != ABILITY_OVERCOAT && GetBattlerHoldEffect(gBattlerAttacker) != HOLD_EFFECT_SAFETY_GOGGLES) { @@ -5386,96 +5386,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, enum Ability ability, u32 spec } break; case ABILITYEFFECT_IMMUNITY: - for (battler = 0; battler < gBattlersCount; battler++) - { - switch (GetBattlerAbilityIgnoreMoldBreaker(battler)) - { - case ABILITY_IMMUNITY: - case ABILITY_PASTEL_VEIL: - if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER)) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); - effect = 1; - } - break; - case ABILITY_OWN_TEMPO: - if (gBattleMons[battler].volatiles.confusionTurns > 0) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); - effect = 2; - } - break; - case ABILITY_LIMBER: - if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); - effect = 1; - } - break; - case ABILITY_INSOMNIA: - case ABILITY_VITAL_SPIRIT: - if (gBattleMons[battler].status1 & STATUS1_SLEEP) - { - TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); - gBattleMons[battler].volatiles.nightmare = FALSE; - StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); - effect = 1; - } - break; - case ABILITY_WATER_VEIL: - case ABILITY_WATER_BUBBLE: - case ABILITY_THERMAL_EXCHANGE: - if (gBattleMons[battler].status1 & STATUS1_BURN) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); - effect = 1; - } - break; - case ABILITY_MAGMA_ARMOR: - if (gBattleMons[battler].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE)) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); - effect = 1; - } - break; - case ABILITY_OBLIVIOUS: - if (gBattleMons[battler].volatiles.infatuation) - effect = 3; - else if (gDisableStructs[battler].tauntTimer != 0) - effect = 4; - break; - default: - break; - } - - if (effect != 0) - { - switch (effect) - { - case 1: // status cleared - gBattleMons[battler].status1 = 0; - BattleScriptCall(BattleScript_AbilityCuredStatus); - break; - case 2: // get rid of confusion - RemoveConfusionStatus(battler); - BattleScriptCall(BattleScript_AbilityCuredStatus); - break; - case 3: // get rid of infatuation - gBattleMons[battler].volatiles.infatuation = 0; - BattleScriptCall(BattleScript_BattlerGotOverItsInfatuation); - break; - case 4: // get rid of taunt - gDisableStructs[battler].tauntTimer = 0; - BattleScriptCall(BattleScript_BattlerShookOffTaunt); - break; - } - - gBattleScripting.battler = gBattlerAbility = battler; - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - return effect; - } - } + effect = TryImmunityAbilityHealStatus(battler, caseID); + if (effect) + return effect; + break; + case ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES: + effect = TryImmunityAbilityHealStatus(battler, caseID); break; case ABILITYEFFECT_SYNCHRONIZE: if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE) @@ -5994,7 +5910,7 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abil { battleScript = BattleScript_AlreadyParalyzed; } - else if (B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ELECTRIC)) + else if (GetGenConfig(GEN_CONFIG_PARALYZE_ELECTRIC) >= GEN_6 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ELECTRIC)) { battleScript = BattleScript_NotAffected; } @@ -6801,6 +6717,9 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect) BattleScriptCall(BattleScript_AttackerItemStatRaise); } break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(battler, ITEMEFFECT_NONE); + break; default: break; } @@ -10771,6 +10690,110 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler) return FALSE; } +u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID) +{ + u32 effect = 0; + switch (GetBattlerAbilityIgnoreMoldBreaker(battler)) + { + case ABILITY_IMMUNITY: + case ABILITY_PASTEL_VEIL: + if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER)) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); + effect = 1; + } + break; + case ABILITY_OWN_TEMPO: + if (gBattleMons[battler].volatiles.confusionTurns > 0) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); + effect = 2; + } + break; + case ABILITY_LIMBER: + if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); + effect = 1; + } + break; + case ABILITY_INSOMNIA: + case ABILITY_VITAL_SPIRIT: + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); + gBattleMons[battler].volatiles.nightmare = FALSE; + StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); + effect = 1; + } + break; + case ABILITY_WATER_VEIL: + case ABILITY_WATER_BUBBLE: + case ABILITY_THERMAL_EXCHANGE: + if (gBattleMons[battler].status1 & STATUS1_BURN) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); + effect = 1; + } + break; + case ABILITY_MAGMA_ARMOR: + if (gBattleMons[battler].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE)) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); + effect = 1; + } + break; + case ABILITY_OBLIVIOUS: + if (gBattleMons[battler].volatiles.infatuation) + effect = 3; + else if (GetGenConfig(GEN_CONFIG_OBLIVIOUS_TAUNT) >= GEN_6 && gDisableStructs[battler].tauntTimer != 0) + effect = 4; + break; + } + + if (effect != 0) + { + switch (effect) + { + case 1: // status cleared + gBattleMons[battler].status1 = 0; + if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) + BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); + else + BattleScriptCall(BattleScript_AbilityCuredStatus); + break; + case 2: // get rid of confusion + RemoveConfusionStatus(battler); + if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) + BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); + else + BattleScriptCall(BattleScript_AbilityCuredStatus); + break; + case 3: // get rid of infatuation + gBattleMons[battler].volatiles.infatuation = 0; + if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) + BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); + else + BattleScriptCall(BattleScript_AbilityCuredStatus); + break; + case 4: // get rid of taunt + gDisableStructs[battler].tauntTimer = 0; + if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) + BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); + else + BattleScriptCall(BattleScript_AbilityCuredStatus); + break; + } + + gBattleScripting.battler = gBattlerAbility = battler; + BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); + MarkBattlerForControllerExec(battler); + return effect; + } + + return 0; +} + bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler) { if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0) @@ -11738,7 +11761,7 @@ bool32 HasWeatherEffect(void) return TRUE; } -static bool32 IsAnyTargetAffected(u32 battlerAtk) +bool32 IsAnyTargetAffected(u32 battlerAtk) { for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { @@ -11825,14 +11848,11 @@ bool32 TrySwitchInEjectPack(enum ItemCaseId caseID) gBattleScripting.battler = battler; gLastUsedItem = gBattleMons[battler].item; if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) - { BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3); - } + else if (caseID == ITEMEFFECT_NORMAL) + BattleScriptExecute(BattleScript_EjectPackActivate_End2); else - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret; - } + BattleScriptCall(BattleScript_EjectPackActivate_Ret); gAiLogicData->ejectPackSwitch = TRUE; return TRUE; } @@ -11985,7 +12005,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abil u32 nonVolatileStatus = GetMoveNonVolatileStatus(move); if ((gBattleMons[battlerDef].volatiles.lockOn && gDisableStructs[battlerDef].battlerWithSureHit == battlerAtk) - || (B_TOXIC_NEVER_MISS >= GEN_6 && nonVolatileStatus == MOVE_EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON)) + || (GetGenConfig(GEN_CONFIG_TOXIC_NEVER_MISS) >= GEN_6 && nonVolatileStatus == MOVE_EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON)) || gBattleMons[battlerDef].volatiles.glaiveRush) { effect = TRUE; diff --git a/src/clock.c b/src/clock.c index f0a632c9ff..142e62ebf2 100644 --- a/src/clock.c +++ b/src/clock.c @@ -1,16 +1,16 @@ #include "global.h" +#include "berry.h" #include "clock.h" +#include "dewford_trend.h" #include "event_data.h" +#include "field_specials.h" +#include "field_weather.h" +#include "main.h" +#include "lottery_corner.h" +#include "overworld.h" #include "rtc.h" #include "time_events.h" -#include "field_specials.h" -#include "lottery_corner.h" -#include "dewford_trend.h" #include "tv.h" -#include "field_weather.h" -#include "berry.h" -#include "main.h" -#include "overworld.h" #include "wallclock.h" #include "constants/form_change_types.h" diff --git a/src/debug.c b/src/debug.c index bd43d9621d..d6632810ca 100644 --- a/src/debug.c +++ b/src/debug.c @@ -2889,6 +2889,10 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu //Moves for (i = 0; i < MAX_MON_MOVES; i++) { + // Non-default moveset chosen. Reset moves before setting the chosen moves. + if (moves[0] != MOVE_NONE) + SetMonMoveSlot(&mon, MOVE_NONE, i); + if (moves[i] == MOVE_NONE || moves[i] >= MOVES_COUNT) continue; diff --git a/src/easy_chat.c b/src/easy_chat.c index 7c0871cb78..63808597a2 100644 --- a/src/easy_chat.c +++ b/src/easy_chat.c @@ -5851,16 +5851,23 @@ static u8 IsEasyChatWordUnlocked(u16 easyChatWord) void InitializeEasyChatWordArray(u16 *words, u16 length) { u16 i; - for (i = length - 1; i != EC_EMPTY_WORD; i--) - *(words++) = EC_EMPTY_WORD; + if (words != NULL) + { + for (i = length - 1; i != EC_EMPTY_WORD; i--) + *(words++) = EC_EMPTY_WORD; + } } void InitQuestionnaireWords(void) { int i; u16 *words = GetQuestionnaireWordsPtr(); - for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++) - words[i] = EC_EMPTY_WORD; + + if (words != NULL) + { + for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++) + words[i] = EC_EMPTY_WORD; + } } bool32 IsEasyChatAnswerUnlocked(int easyChatWord) diff --git a/src/party_menu.c b/src/party_menu.c index 63517a110d..e6568c34bf 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -5230,13 +5230,13 @@ static void ShowMoveSelectWindow(u8 slot) { u8 i; u8 moveCount = 0; - u8 fontId = FONT_NORMAL; u8 windowId = DisplaySelectionWindow(SELECTWINDOW_MOVES); u16 move; for (i = 0; i < MAX_MON_MOVES; i++) { move = GetMonData(&gPlayerParty[slot], MON_DATA_MOVE1 + i); + u8 fontId = GetFontIdToFit(GetMoveName(move), FONT_NORMAL, 0, 72); AddTextPrinterParameterized(windowId, fontId, GetMoveName(move), 8, (i * 16) + 1, TEXT_SKIP_DRAW, NULL); if (move != MOVE_NONE) moveCount++; @@ -8025,4 +8025,3 @@ static void FieldCallback_RockClimb(void) gFieldEffectArguments[0] = GetCursorSelectionMonId(); FieldEffectStart(FLDEFF_USE_ROCK_CLIMB); } - diff --git a/src/pokemon.c b/src/pokemon.c index 32fe01812b..635624ba0e 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -4844,7 +4844,7 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16 case EVO_MODE_ITEM_CHECK: for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++) { - bool32 conditionMet = FALSE; + bool32 conditionsMet = FALSE; if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE) continue; @@ -4852,11 +4852,11 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16 { case EVO_ITEM: if (evolutions[i].param == evolutionItem) - conditionMet = TRUE; + conditionsMet = TRUE; break; } - if (conditionMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, PARTY_SIZE, canStopEvo, evoState)) + if (conditionsMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, PARTY_SIZE, canStopEvo, evoState)) { // All checks passed, so stop checking the rest of the evolutions. // This is different from vanilla where the loop continues. @@ -4878,9 +4878,9 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16 switch (evolutions[i].method) { - case EVO_BATTLE_END: - conditionsMet = TRUE; - break; + case EVO_BATTLE_END: + conditionsMet = TRUE; + break; } if (conditionsMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, evolutionItem, canStopEvo, evoState)) @@ -4906,7 +4906,6 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16 case EVO_SPIN: if (gSpecialVar_0x8000 == evolutions[i].param) conditionsMet = TRUE; - break; } @@ -6357,6 +6356,14 @@ void HandleSetPokedexFlag(enum NationalDexOrder nationalNum, u8 caseId, u32 pers } } +void HandleSetPokedexFlagFromMon(struct Pokemon *mon, u32 caseId) +{ + u32 personality = GetMonData(mon, MON_DATA_PERSONALITY); + enum NationalDexOrder nationalNum = SpeciesToNationalPokedexNum(GetMonData(mon, MON_DATA_SPECIES)); + + HandleSetPokedexFlag(nationalNum, caseId, personality); +} + bool8 HasTwoFramesAnimation(u16 species) { return P_TWO_FRAME_FRONT_SPRITES diff --git a/test/battle/ability/immunity.c b/test/battle/ability/immunity.c index c02a37c69a..9199638ad5 100644 --- a/test/battle/ability/immunity.c +++ b/test/battle/ability/immunity.c @@ -63,3 +63,19 @@ SINGLE_BATTLE_TEST("Immunity doesn't prevent Pokémon from being poisoned by Tox NOT HP_BAR(player); } } + +SINGLE_BATTLE_TEST("Immunity cures existing poison on turn 0") +{ + GIVEN { + PLAYER(SPECIES_ZANGOOSE) { + Ability(ABILITY_IMMUNITY); + Status1(STATUS1_POISON); + } + OPPONENT(SPECIES_WOBBUFFET); + } SCENE { + ABILITY_POPUP(player, ABILITY_IMMUNITY); + TURN { MOVE(player, MOVE_SPLASH); } + } THEN { + EXPECT_EQ(player->status1, STATUS1_NONE); + } +} diff --git a/test/battle/ability/oblivious.c b/test/battle/ability/oblivious.c index 40506e1967..c160ab423e 100644 --- a/test/battle/ability/oblivious.c +++ b/test/battle/ability/oblivious.c @@ -31,22 +31,35 @@ SINGLE_BATTLE_TEST("Oblivious prevents Captivate") } } -SINGLE_BATTLE_TEST("Oblivious prevents Taunt") +SINGLE_BATTLE_TEST("Oblivious prevents Taunt (Gen6+)") { + u32 gen = 0; + PARAMETRIZE { gen = GEN_5; } + PARAMETRIZE { gen = GEN_6; } GIVEN { + WITH_CONFIG(GEN_CONFIG_OBLIVIOUS_TAUNT, gen); ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT); - ASSUME(B_OBLIVIOUS_TAUNT >= GEN_6); PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_TAUNT); } - TURN { MOVE(player, MOVE_SPORE); } + TURN { MOVE(player, MOVE_SPORE, allowed: gen == GEN_6); } } SCENE { - ABILITY_POPUP(player, ABILITY_OBLIVIOUS); - NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); } - MESSAGE("It doesn't affect Slowpoke…"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + if (gen == GEN_6) { + NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); } + ABILITY_POPUP(player, ABILITY_OBLIVIOUS); + MESSAGE("It doesn't affect Slowpoke…"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); + NONE_OF { + ABILITY_POPUP(player, ABILITY_OBLIVIOUS); + MESSAGE("It doesn't affect Slowpoke…"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + } + } } } diff --git a/test/battle/ability/overcoat.c b/test/battle/ability/overcoat.c index c722d7ac55..6d7b5f2a8d 100644 --- a/test/battle/ability/overcoat.c +++ b/test/battle/ability/overcoat.c @@ -1,25 +1,37 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves") +SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves (Gen6+)") { + u32 gen = 0; + PARAMETRIZE { gen = GEN_5; } + PARAMETRIZE { gen = GEN_6; } GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen); ASSUME(IsPowderMove(MOVE_STUN_SPORE)); PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); } } WHEN { TURN { MOVE(player, MOVE_STUN_SPORE); } } SCENE { - ABILITY_POPUP(opponent, ABILITY_OVERCOAT); - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); - MESSAGE("It doesn't affect the opposing Pineco…"); + if (gen == GEN_6) { + ABILITY_POPUP(opponent, ABILITY_OVERCOAT); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("It doesn't affect the opposing Pineco…"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_OVERCOAT); + MESSAGE("It doesn't affect the opposing Pineco…"); + } + } } } DOUBLE_BATTLE_TEST("Overcoat blocks damage from sandstorm") { GIVEN { - PLAYER(SPECIES_WYNAUT) { Speed(50); } + PLAYER(SPECIES_WYNAUT) { Speed(50); } PLAYER(SPECIES_HELIOLISK) { Speed(40); Ability(ABILITY_SAND_VEIL); } OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); } OPPONENT(SPECIES_STARLY) { Speed(20); } @@ -41,7 +53,7 @@ DOUBLE_BATTLE_TEST("Overcoat blocks damage from hail") { GIVEN { ASSUME(GetMoveEffect(MOVE_HAIL) == EFFECT_HAIL); - PLAYER(SPECIES_WYNAUT) { Speed(50); Ability(ABILITY_SNOW_CLOAK); } + PLAYER(SPECIES_WYNAUT) { Speed(50); Ability(ABILITY_SNOW_CLOAK); } PLAYER(SPECIES_SOLOSIS) { Speed(40); Ability(ABILITY_RUN_AWAY); } OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); } OPPONENT(SPECIES_SNORUNT) { Speed(20); } @@ -73,4 +85,3 @@ SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect") EXPECT_EQ(player->status1, 0); } } - diff --git a/test/battle/ability/parental_bond.c b/test/battle/ability/parental_bond.c index c684963f0d..79accaf0a2 100644 --- a/test/battle/ability/parental_bond.c +++ b/test/battle/ability/parental_bond.c @@ -292,10 +292,10 @@ SINGLE_BATTLE_TEST("Parental Bond Snore strikes twice while asleep") HP_BAR(opponent, captureDamage: &damage[1]); MESSAGE("The Pokémon was hit 2 time(s)!"); } THEN { - if (B_PARENTAL_BOND_DMG == GEN_6) - EXPECT_MUL_EQ(damage[0], Q_4_12(0.5), damage[1]); - else + if (B_PARENTAL_BOND_DMG >= GEN_7) EXPECT_MUL_EQ(damage[0], Q_4_12(0.25), damage[1]); + else + EXPECT_MUL_EQ(damage[0], Q_4_12(0.5), damage[1]); } } diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index c45f416024..4dd16f0c45 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -59,6 +59,7 @@ SINGLE_BATTLE_TEST("Protosynthesis ability pop up activates only once during the u16 turns; GIVEN { + WITH_CONFIG(GEN_CONFIG_ABILITY_WEATHER, GEN_6); PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); } OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); }; } WHEN { diff --git a/test/battle/ability/supersweet_syrup.c b/test/battle/ability/supersweet_syrup.c index 4ff8c462ba..cd819eda72 100644 --- a/test/battle/ability/supersweet_syrup.c +++ b/test/battle/ability/supersweet_syrup.c @@ -56,11 +56,21 @@ SINGLE_BATTLE_TEST("Supersweet Syrup can not further lower opponents evasion if TURN { MOVE(opponent, MOVE_SWEET_SCENT); } TURN { MOVE(opponent, MOVE_SWEET_SCENT); } TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + if (GetMoveEffect(MOVE_SWEET_SCENT) == EFFECT_EVASION_DOWN) { + TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + } TURN { SWITCH(opponent, 1); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + if (GetMoveEffect(MOVE_SWEET_SCENT) == EFFECT_EVASION_DOWN) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + } ABILITY_POPUP(opponent, ABILITY_SUPERSWEET_SYRUP); NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); diff --git a/test/battle/ai/ai_flag_predict_switch.c b/test/battle/ai/ai_flag_predict_switch.c index b64dea488c..8530c49675 100644 --- a/test/battle/ai/ai_flag_predict_switch.c +++ b/test/battle/ai/ai_flag_predict_switch.c @@ -59,7 +59,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: Considers ShouldSwitch and GetMos // Switching in trapper is an advanced feature of ShouldSwitch that requires GetMostSuitableMonToSwitchInto to also return a specific mon; this passing means the AI can use both in prediction PASSES_RANDOMLY(5, 10, RNG_AI_PREDICT_SWITCH); GIVEN { - ASSUME(B_POWDER_GRASS >= GEN_6); + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PREDICT_SWITCH | AI_FLAG_PREDICT_INCOMING_MON); PLAYER(SPECIES_SKARMORY) { Moves(MOVE_SCRATCH); } PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_ACROBATICS); } diff --git a/test/battle/damage_formula.c b/test/battle/damage_formula.c index f41f6a2145..4a7e4ec1ef 100644 --- a/test/battle/damage_formula.c +++ b/test/battle/damage_formula.c @@ -101,6 +101,7 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Marshadow vs Mawile)") PARAMETRIZE { expectedDamage = 123; } GIVEN { ASSUME(GetMoveCategory(MOVE_SPECTRAL_THIEF) == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(B_UPDATED_TYPE_MATCHUPS >= GEN_6); // Steel resists Ghost in Gen2-5 PLAYER(SPECIES_MARSHADOW) { Level(100); Attack(286); } OPPONENT(SPECIES_MAWILE) { Level(100); Defense(226); HP(241); } } WHEN { @@ -196,6 +197,7 @@ SINGLE_BATTLE_TEST("Gem boosted Damage calculation") { s16 dmg; s16 expectedDamage; +#if I_GEM_BOOST_POWER >= GEN_6 PARAMETRIZE { expectedDamage = 240; } PARAMETRIZE { expectedDamage = 237; } PARAMETRIZE { expectedDamage = 234; } @@ -212,6 +214,25 @@ SINGLE_BATTLE_TEST("Gem boosted Damage calculation") PARAMETRIZE { expectedDamage = 208; } PARAMETRIZE { expectedDamage = 205; } PARAMETRIZE { expectedDamage = 204; } +#else + KNOWN_FAILING; + PARAMETRIZE { expectedDamage = 273; } + PARAMETRIZE { expectedDamage = 270; } + PARAMETRIZE { expectedDamage = 267; } + PARAMETRIZE { expectedDamage = 264; } + PARAMETRIZE { expectedDamage = 261; } + PARAMETRIZE { expectedDamage = 258; } + PARAMETRIZE { expectedDamage = 256; } + PARAMETRIZE { expectedDamage = 253; } + PARAMETRIZE { expectedDamage = 250; } + PARAMETRIZE { expectedDamage = 247; } + PARAMETRIZE { expectedDamage = 244; } + PARAMETRIZE { expectedDamage = 241; } + PARAMETRIZE { expectedDamage = 240; } + PARAMETRIZE { expectedDamage = 237; } + PARAMETRIZE { expectedDamage = 234; } + PARAMETRIZE { expectedDamage = 231; } +#endif GIVEN { PLAYER(SPECIES_MAKUHITA) { Item(ITEM_FIGHTING_GEM); } OPPONENT(SPECIES_MAKUHITA); diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index a80f644d50..1adf565c3e 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -1464,7 +1464,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Chi Strike boosts allies' crit chance by 1 st { u32 j; GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_6); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, GEN_6); ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_CHI_STRIKE, MOVE_EFFECT_CRIT_PLUS_SIDE)); PLAYER(SPECIES_MACHAMP) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_MACHOP); diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 56416663ff..37a6d515b3 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -606,14 +606,14 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves s16 damage[4]; GIVEN { ASSUME(GetMovePower(MOVE_MEGA_DRAIN) == 40); - ASSUME(GetMovePower(MOVE_BUBBLE) == 40); + ASSUME(GetMovePower(MOVE_WATER_GUN) == 40); PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_MEGA_DRAIN); } TURN { MOVE(player, MOVE_MEGA_DRAIN, gimmick: GIMMICK_TERA); } TURN { MOVE(player, MOVE_MEGA_DRAIN); } - TURN { MOVE(player, MOVE_BUBBLE); } + TURN { MOVE(player, MOVE_WATER_GUN); } } SCENE { // turn 1 MESSAGE("Wobbuffet used Mega Drain!"); @@ -628,8 +628,8 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); HP_BAR(opponent, captureDamage: &damage[2]); // turn 4 - MESSAGE("Wobbuffet used Bubble!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player); + MESSAGE("Wobbuffet used Water Gun!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player); HP_BAR(opponent, captureDamage: &damage[3]); } THEN { // The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost. diff --git a/test/battle/hold_effect/big_root.c b/test/battle/hold_effect/big_root.c index 41dd09adc0..3d2256ea34 100644 --- a/test/battle/hold_effect/big_root.c +++ b/test/battle/hold_effect/big_root.c @@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Big Root increases healing from absorbing moves", s16 damage PARAMETRIZE { item = ITEM_BIG_ROOT; } GIVEN { - PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); } + PLAYER(SPECIES_XURKITREE) { HP(200); Item(item); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_ABSORB); } @@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("Big Root increases healing from absorbing moves", s16 damage HP_BAR(player, captureDamage: &results[i].heal); } FINALLY { EXPECT_EQ(results[0].damage, results[1].damage); // Damage is unaffected - EXPECT_MUL_EQ(results[1].heal, Q_4_12(5234 / 4096), results[0].heal); + EXPECT_MUL_EQ(results[0].heal, Q_4_12(1.3), results[1].heal); } } @@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Big Root increases damage from absorbing Liquid Ooze", s16 d PARAMETRIZE { item = ITEM_BIG_ROOT; } GIVEN { - PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); } + PLAYER(SPECIES_XURKITREE) { HP(200); Item(item); } OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); } } WHEN { TURN { MOVE(player, MOVE_ABSORB); } @@ -73,6 +73,6 @@ SINGLE_BATTLE_TEST("Big Root increases damage from absorbing Liquid Ooze", s16 d ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player); HP_BAR(player, captureDamage: &results[i].damage); } FINALLY { - EXPECT_MUL_EQ(results[1].damage, Q_4_12(5234 / 4096), results[0].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), results[1].damage); } } diff --git a/test/battle/hold_effect/booster_energy.c b/test/battle/hold_effect/booster_energy.c index 93e79e4962..2cfd3a13c3 100644 --- a/test/battle/hold_effect/booster_energy.c +++ b/test/battle/hold_effect/booster_energy.c @@ -38,6 +38,7 @@ SINGLE_BATTLE_TEST("Booster Energy will activate Quark Drive after Electric Terr SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunlight ends") { GIVEN { + WITH_CONFIG(GEN_CONFIG_ABILITY_WEATHER, GEN_6); PLAYER(SPECIES_RAGING_BOLT) { Attack(100); Defense(100); Speed(100); SpAttack(110); SpDefense(100); Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); } OPPONENT(SPECIES_TORKOAL) { Speed(100); Ability(ABILITY_DROUGHT); }; } WHEN { diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index 600af04515..836f3d0fd1 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -338,3 +338,26 @@ SINGLE_BATTLE_TEST("Eject Pack does not activate if mon is switched in due to Ej NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); } } + +DOUBLE_BATTLE_TEST("Eject Pack will trigger on the fastest mon at the end of the turn") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_SYRUP_BOMB, MOVE_EFFECT_SYRUP_BOMB) == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Speed(1); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT) { Speed(10); Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_SYRUP_BOMB, target: playerLeft); + MOVE(opponentRight, MOVE_SYRUP_BOMB, target: playerRight); + SEND_OUT(playerRight, 2); + } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SYRUP_BOMB_SPEED_DROP, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SYRUP_BOMB_SPEED_DROP, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + } +} diff --git a/test/battle/hold_effect/gems.c b/test/battle/hold_effect/gems.c index 52c85d7fb5..b2d843e1d5 100644 --- a/test/battle/hold_effect/gems.c +++ b/test/battle/hold_effect/gems.c @@ -32,7 +32,6 @@ SINGLE_BATTLE_TEST("Gem boost is only applied once") s16 normalHit; GIVEN { - ASSUME(I_GEM_BOOST_POWER >= GEN_6); PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -46,7 +45,10 @@ SINGLE_BATTLE_TEST("Gem boost is only applied once") ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); HP_BAR(opponent, captureDamage: &normalHit); } THEN { - EXPECT_MUL_EQ(normalHit, Q_4_12(1.3), boostedHit); + if (I_GEM_BOOST_POWER >= GEN_6) + EXPECT_MUL_EQ(normalHit, Q_4_12(1.3), boostedHit); + else + EXPECT_MUL_EQ(normalHit, Q_4_12(1.5), boostedHit); } } diff --git a/test/battle/move_effect/acrobatics.c b/test/battle/move_effect/acrobatics.c index 809b77f948..1229d4e2c5 100644 --- a/test/battle/move_effect/acrobatics.c +++ b/test/battle/move_effect/acrobatics.c @@ -30,7 +30,6 @@ SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consume PARAMETRIZE { heldItem = ITEM_NONE; } PARAMETRIZE { heldItem = ITEM_FLYING_GEM; } GIVEN { - ASSUME(I_GEM_BOOST_POWER >= GEN_6); ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS); ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING); PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/dragon_darts.c b/test/battle/move_effect/dragon_darts.c index a06c61ec78..97694909fe 100644 --- a/test/battle/move_effect/dragon_darts.c +++ b/test/battle/move_effect/dragon_darts.c @@ -4,7 +4,6 @@ ASSUMPTIONS { ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS); - ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY); } SINGLE_BATTLE_TEST("Dragon Darts strikes twice") @@ -72,13 +71,13 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes an opponent twice if the other one is F struct BattlePokemon *chosenTarget = NULL; struct BattlePokemon *finalTarget = NULL; u32 speciesLeft, speciesRight; - PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; } - PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; } - PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } - PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } + PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentRight; speciesLeft = SPECIES_FIDOUGH; speciesRight = SPECIES_WOBBUFFET; } + PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_FIDOUGH; speciesRight = SPECIES_WOBBUFFET; } + PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_FIDOUGH; } + PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_FIDOUGH; } GIVEN { - ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY); + ASSUME(GetSpeciesType(SPECIES_FIDOUGH, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_FIDOUGH, 1) == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET); OPPONENT(speciesLeft); @@ -257,7 +256,6 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes right ally twice if one strike misses") DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me user") { GIVEN { - ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -273,14 +271,14 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me us } } -DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a fairy type follow me user") +DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a Fairy-type follow me user") { GIVEN { - ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY); + ASSUME(GetSpeciesType(SPECIES_FIDOUGH, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_FIDOUGH, 1) == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_CLEFAIRY); + OPPONENT(SPECIES_FIDOUGH); } WHEN { TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } } SCENE { @@ -295,7 +293,6 @@ DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a fairy typ DOUBLE_BATTLE_TEST("Dragon Darts fails to strike the second target if first target fainted and follow me was active") { GIVEN { - ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/endure.c b/test/battle/move_effect/endure.c index f332fc28de..2d66bc41ef 100644 --- a/test/battle/move_effect/endure.c +++ b/test/battle/move_effect/endure.c @@ -1,13 +1,17 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_ENDURE) == EFFECT_ENDURE); +} + TO_DO_BATTLE_TEST("Endure allows the user to survive any attack with 1 HP left"); SINGLE_BATTLE_TEST("Endure does not prevent multiple hits and stat changes occur at the end of the turn") { GIVEN { ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT); - ASSUME(GetMoveEffect(MOVE_ENDURE) == EFFECT_ENDURE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { HP(1); } } WHEN { @@ -54,6 +58,23 @@ DOUBLE_BATTLE_TEST("Endure is not transferred to a mon that is switched in due t } } +SINGLE_BATTLE_TEST("Endure only lasts for one turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + } WHEN { + TURN { MOVE(opponent, MOVE_ENDURE); MOVE(player, MOVE_POUND); } + TURN { MOVE(player, MOVE_POUND); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player); + MESSAGE("The opposing Wobbuffet endured the hit!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player); + NOT MESSAGE("The opposing Wobbuffet endured the hit!"); + } +} + TO_DO_BATTLE_TEST("Endure's success rate decreases for every consecutively used turn"); TO_DO_BATTLE_TEST("Endure uses the same counter as Protect"); TO_DO_BATTLE_TEST("Endure doesn't trigger effects that require damage to be done to the Pokémon (Gen 2-4)"); // Eg. Rough Skin diff --git a/test/battle/move_effect/fickle_beam.c b/test/battle/move_effect/fickle_beam.c index 0313823aa9..8554a29dac 100644 --- a/test/battle/move_effect/fickle_beam.c +++ b/test/battle/move_effect/fickle_beam.c @@ -12,15 +12,14 @@ SINGLE_BATTLE_TEST("Fickle Beam deals double damage 30% of the time") PASSES_RANDOMLY(30, 100, RNG_FICKLE_BEAM); GIVEN { - ASSUME(GetMovePower(MOVE_POWER_GEM) == 80); - ASSUME(GetMovePower(MOVE_FICKLE_BEAM) == 80); + ASSUME(GetMovePower(MOVE_DAZZLING_GLEAM) == GetMovePower(MOVE_FICKLE_BEAM)); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_POWER_GEM); } + TURN { MOVE(player, MOVE_DAZZLING_GLEAM); } TURN { MOVE(player, MOVE_FICKLE_BEAM); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_GEM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DAZZLING_GLEAM, player); HP_BAR(opponent, captureDamage: &damage[0]); ANIMATION(ANIM_TYPE_MOVE, MOVE_FICKLE_BEAM, player); HP_BAR(opponent, captureDamage: &damage[1]); diff --git a/test/battle/move_effect/future_sight.c b/test/battle/move_effect/future_sight.c index 246c384a07..ee424c9bd9 100644 --- a/test/battle/move_effect/future_sight.c +++ b/test/battle/move_effect/future_sight.c @@ -1,10 +1,18 @@ #include "global.h" #include "test/battle.h" +#if B_UPDATED_MOVE_DATA >= GEN_6 + #define FUTURE_SIGHT_EQUIVALENT MOVE_SEED_FLARE /* 120 power */ +#elif B_UPDATED_MOVE_DATA >= GEN_5 + #define FUTURE_SIGHT_EQUIVALENT MOVE_DYNAMAX_CANNON /* 100 power */ +#else + #define FUTURE_SIGHT_EQUIVALENT MOVE_EXTRASENSORY /* 80 power */ +#endif + ASSUMPTIONS { - ASSUME(GetMovePower(MOVE_SEED_FLARE) == GetMovePower(MOVE_FUTURE_SIGHT)); - ASSUME(GetMoveCategory(MOVE_SEED_FLARE) == GetMoveCategory(MOVE_FUTURE_SIGHT)); + ASSUME(GetMovePower(FUTURE_SIGHT_EQUIVALENT) == GetMovePower(MOVE_FUTURE_SIGHT)); + ASSUME(GetMoveCategory(FUTURE_SIGHT_EQUIVALENT) == GetMoveCategory(MOVE_FUTURE_SIGHT)); ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT); ASSUME(GetMovePower(MOVE_FUTURE_SIGHT) > 0); } @@ -23,13 +31,13 @@ SINGLE_BATTLE_TEST("Future Sight uses Sp. Atk stat of the original user without PLAYER(SPECIES_RAICHU) { Item(item); } OPPONENT(SPECIES_REGICE); } WHEN { - TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } + TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } TURN { MOVE(player, MOVE_FUTURE_SIGHT); } TURN { SWITCH(player, 1); } TURN { } TURN { } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); + ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player); HP_BAR(opponent, captureDamage: &seedFlareDmg); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); MESSAGE("The opposing Regice took the Future Sight attack!"); @@ -49,13 +57,13 @@ SINGLE_BATTLE_TEST("Future Sight is not boosted by Life Orb is original user if PLAYER(SPECIES_RAICHU) { Item(ITEM_LIFE_ORB); } OPPONENT(SPECIES_REGICE); } WHEN { - TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } + TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } TURN { MOVE(player, MOVE_FUTURE_SIGHT); } TURN { SWITCH(player, 1); } TURN { } TURN { } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); + ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player); HP_BAR(opponent, captureDamage: &seedFlareDmg); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); MESSAGE("The opposing Regice took the Future Sight attack!"); @@ -77,13 +85,13 @@ SINGLE_BATTLE_TEST("Future Sight receives STAB from party mon (Gen 5+)") PLAYER(SPECIES_RAICHU); OPPONENT(SPECIES_REGICE); } WHEN { - TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } + TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } TURN { MOVE(player, MOVE_FUTURE_SIGHT); } TURN { SWITCH(player, 1); } TURN { } TURN { } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); + ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player); HP_BAR(opponent, captureDamage: &seedFlareDmg); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); HP_BAR(opponent, captureDamage: &futureSightDmg); @@ -100,13 +108,13 @@ SINGLE_BATTLE_TEST("Future Sight is affected by type effectiveness (Gen 5+)") PLAYER(SPECIES_RAICHU); OPPONENT(SPECIES_HOUNDOOM); } WHEN { - TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } + TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); } TURN { MOVE(player, MOVE_FUTURE_SIGHT); } TURN { SWITCH(player, 1); } TURN { } TURN { } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); + ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player); HP_BAR(opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); MESSAGE("The opposing Houndoom took the Future Sight attack!"); diff --git a/test/battle/move_effect/instruct.c b/test/battle/move_effect/instruct.c index 03a39fe154..070b6ba31d 100644 --- a/test/battle/move_effect/instruct.c +++ b/test/battle/move_effect/instruct.c @@ -280,6 +280,7 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after inst PARAMETRIZE { moveTarget = opponentLeft; } PARAMETRIZE { moveTarget = opponentRight; } GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6); ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME); ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE); ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK); diff --git a/test/battle/move_effect/multi_hit.c b/test/battle/move_effect/multi_hit.c index 7052a52cbf..291c78efdb 100644 --- a/test/battle/move_effect/multi_hit.c +++ b/test/battle/move_effect/multi_hit.c @@ -177,14 +177,14 @@ SINGLE_BATTLE_TEST("Scale Shot is immune to Fairy types and will end the move co GIVEN { ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT); ASSUME(GetMoveType(MOVE_SCALE_SHOT) == TYPE_DRAGON); - ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY); + ASSUME(GetSpeciesType(SPECIES_FIDOUGH, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_FIDOUGH, 1) == TYPE_FAIRY); PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_CLEFAIRY) { HP(1); } + OPPONENT(SPECIES_FIDOUGH) { HP(1); } } WHEN { TURN { MOVE(player, MOVE_SCALE_SHOT); } } SCENE { NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALE_SHOT, player); - MESSAGE("It doesn't affect the opposing Clefairy…"); + MESSAGE("It doesn't affect the opposing Fidough…"); } } diff --git a/test/battle/move_effect/powder.c b/test/battle/move_effect/powder.c index c4e053db87..da8eba4caf 100644 --- a/test/battle/move_effect/powder.c +++ b/test/battle/move_effect/powder.c @@ -149,9 +149,10 @@ DOUBLE_BATTLE_TEST("Powder fails if target is already affected by Powder") } } -SINGLE_BATTLE_TEST("Powder fails if the target is Grass type") +SINGLE_BATTLE_TEST("Powder fails if the target is Grass type (Gen6+)") { GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6); ASSUME(GetSpeciesType(SPECIES_VENUSAUR, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_VENUSAUR, 1) == TYPE_GRASS); PLAYER(SPECIES_VENUSAUR); OPPONENT(SPECIES_VIVILLON); @@ -164,9 +165,10 @@ SINGLE_BATTLE_TEST("Powder fails if the target is Grass type") } } -SINGLE_BATTLE_TEST("Powder fails if the target has Overcoat") +SINGLE_BATTLE_TEST("Powder fails if the target has Overcoat (Gen6+)") { GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6); PLAYER(SPECIES_FORRETRESS) { Ability(ABILITY_OVERCOAT); } OPPONENT(SPECIES_VIVILLON); } WHEN { diff --git a/test/battle/move_effect/toxic.c b/test/battle/move_effect/toxic.c index 52c5aa53cc..045772db2b 100644 --- a/test/battle/move_effect/toxic.c +++ b/test/battle/move_effect/toxic.c @@ -43,14 +43,16 @@ SINGLE_BATTLE_TEST("Toxic can't bad poison a poison or steel type") } } -SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type") +SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type (Gen6+)") { - u32 species; + u32 species, gen; bool32 hit; - PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; } - PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = TRUE; } + PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; gen = GEN_5; } + PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = FALSE; gen = GEN_5; } + PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; gen = GEN_6; } + PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = TRUE; gen = GEN_6; } GIVEN { - ASSUME(B_TOXIC_NEVER_MISS >= GEN_6); + WITH_CONFIG(GEN_CONFIG_TOXIC_NEVER_MISS, gen); ASSUME(GetSpeciesType(SPECIES_NIDORAN_M, 0) == TYPE_POISON); PLAYER(species); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect_secondary/dire_claw.c b/test/battle/move_effect_secondary/dire_claw.c index 44cfd8ba1c..17352fa70d 100644 --- a/test/battle/move_effect_secondary/dire_claw.c +++ b/test/battle/move_effect_secondary/dire_claw.c @@ -38,11 +38,10 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respe u8 statusAnim; u16 species; u32 rng; - #if B_PARALYZE_ELECTRIC >= GEN_6 PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; } - #endif // B_PARALYZE_ELECTRIC PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = MOVE_EFFECT_POISON; species = SPECIES_ARBOK; } GIVEN { + WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6); PLAYER(SPECIES_WOBBUFFET); OPPONENT(species); } WHEN { diff --git a/test/battle/move_effect_secondary/paralysis.c b/test/battle/move_effect_secondary/paralysis.c index baa2183064..723309f01a 100644 --- a/test/battle/move_effect_secondary/paralysis.c +++ b/test/battle/move_effect_secondary/paralysis.c @@ -21,19 +21,27 @@ SINGLE_BATTLE_TEST("Thunder Shock inflicts paralysis") } } -SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type") +SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type (Gen6+)") { + u32 gen = 0; + PARAMETRIZE { gen = GEN_5; } + PARAMETRIZE { gen = GEN_6; } GIVEN { - ASSUME(B_PARALYZE_ELECTRIC >= GEN_6); + WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, gen); ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_PIKACHU); } WHEN { - TURN { MOVE(player, MOVE_THUNDER_SHOCK); } + TURN { MOVE(player, MOVE_THUNDER_SHOCK, secondaryEffect: TRUE); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, player); HP_BAR(opponent); - NONE_OF { + if (gen == GEN_6) { + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); + STATUS_ICON(opponent, paralysis: TRUE); + } + } else { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); STATUS_ICON(opponent, paralysis: TRUE); } diff --git a/test/battle/move_effect_secondary/tri_attack.c b/test/battle/move_effect_secondary/tri_attack.c index 860a84aa82..4ac69915e6 100644 --- a/test/battle/move_effect_secondary/tri_attack.c +++ b/test/battle/move_effect_secondary/tri_attack.c @@ -46,12 +46,11 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ u8 statusAnim; u16 species; u32 rng; - #if B_PARALYZE_ELECTRIC >= GEN_6 PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; } - #endif // B_PARALYZE_ELECTRIC PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_ARCANINE; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; } GIVEN { + WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6); PLAYER(SPECIES_WOBBUFFET); OPPONENT(species); } WHEN { diff --git a/test/battle/move_flags/powder.c b/test/battle/move_flags/powder.c index bb0b1e3e4f..989cfb6426 100644 --- a/test/battle/move_flags/powder.c +++ b/test/battle/move_flags/powder.c @@ -1,9 +1,13 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon") +SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon (Gen6+)") { + u32 gen = 0; + PARAMETRIZE { gen = GEN_5; } + PARAMETRIZE { gen = GEN_6; } GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen); ASSUME(IsPowderMove(MOVE_STUN_SPORE)); ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS); PLAYER(SPECIES_WYNAUT); @@ -11,7 +15,12 @@ SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon") } WHEN { TURN { MOVE(player, MOVE_STUN_SPORE); } } SCENE { - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); - MESSAGE("It doesn't affect the opposing Oddish…"); + if (gen == GEN_6) { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("It doesn't affect the opposing Oddish…"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + NOT MESSAGE("It doesn't affect the opposing Oddish…"); + } } } diff --git a/test/battle/sleep_clause.c b/test/battle/sleep_clause.c index 7c41538365..e45945a29f 100644 --- a/test/battle/sleep_clause.c +++ b/test/battle/sleep_clause.c @@ -1075,6 +1075,7 @@ SINGLE_BATTLE_TEST("Sleep Clause: Sleep clause is deactivated when a sleeping mo enum Ability ability; PARAMETRIZE { ability = ABILITY_VITAL_SPIRIT; } PARAMETRIZE { ability = ABILITY_INSOMNIA; } + GIVEN { FLAG_SET(B_FLAG_SLEEP_CLAUSE); ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS); diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c index a93d350ebd..0be8cf1ef9 100644 --- a/test/battle/status1/paralysis.c +++ b/test/battle/status1/paralysis.c @@ -56,6 +56,7 @@ AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target") PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; } GIVEN { + WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); PLAYER(species) { Ability(ability); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } @@ -64,18 +65,30 @@ AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target") } } -SINGLE_BATTLE_TEST("Thunder Wave doesn't affect Electric types in Gen6+") +SINGLE_BATTLE_TEST("Thunder Wave doesn't affect Electric types (Gen6+)") { + u32 gen = 0; + PARAMETRIZE { gen = GEN_5; } + PARAMETRIZE { gen = GEN_6; } GIVEN { + WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, gen); ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC); - ASSUME(B_PARALYZE_ELECTRIC >= GEN_6); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_PIKACHU); } WHEN { TURN { MOVE(player, MOVE_THUNDER_WAVE); } } SCENE { MESSAGE("Wobbuffet used Thunder Wave!"); - MESSAGE("It doesn't affect the opposing Pikachu…"); + if (gen == GEN_6) { + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); + STATUS_ICON(opponent, paralysis: TRUE); + } + MESSAGE("It doesn't affect the opposing Pikachu…"); + } else { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); + STATUS_ICON(opponent, paralysis: TRUE); + } } } diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c index ea08a2ff5a..a43daf8d65 100644 --- a/test/battle/status1/sleep.c +++ b/test/battle/status1/sleep.c @@ -22,11 +22,25 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move") } } -SINGLE_BATTLE_TEST("Sleep: Spore doesn't affect grass types (Gen 6+)") +SINGLE_BATTLE_TEST("Sleep: Spore affects grass types (Gen1-5)") { GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_5); + ASSUME(IsPowderMove(MOVE_SPORE)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CHIKORITA); + } WHEN { + TURN { MOVE(player, MOVE_SPORE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); + } +} + +SINGLE_BATTLE_TEST("Sleep: Spore doesn't affect grass types (Gen6+)") +{ + GIVEN { + WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6); ASSUME(IsPowderMove(MOVE_SPORE)); - ASSUME(B_POWDER_GRASS >= GEN_6); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_CHIKORITA); } WHEN { diff --git a/test/pokemon.c b/test/pokemon.c index 60058407c4..61c9e86c04 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -288,7 +288,7 @@ TEST("givemon [moves]") ZeroPlayerPartyMons(); RUN_OVERWORLD_SCRIPT( - givemon SPECIES_WOBBUFFET, 100, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_NONE, move4=MOVE_NONE; + givemon SPECIES_WOBBUFFET, 100, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_NONE; ); EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);