06/10/25 Master to upcoming merge

This commit is contained in:
AlexOn1ine 2025-10-06 19:42:42 +02:00
commit 870b93aad9
63 changed files with 1077 additions and 300 deletions

View File

@ -43,9 +43,10 @@ body:
label: Version label: Version
description: What version of pokeemerald-expansion are you using? description: What version of pokeemerald-expansion are you using?
options: options:
- 1.13.1 (Latest release) - 1.13.2 (Latest release)
- master (default, unreleased bugfixes) - master (default, unreleased bugfixes)
- upcoming (Edge) - upcoming (Edge)
- 1.13.1
- 1.13.0 - 1.13.0
- 1.12.3 - 1.12.3
- 1.12.2 - 1.12.2

View File

@ -43,9 +43,10 @@ body:
label: Version label: Version
description: What version of pokeemerald-expansion are you using? description: What version of pokeemerald-expansion are you using?
options: options:
- 1.13.1 (Latest release) - 1.13.2 (Latest release)
- master (default, unreleased bugfixes) - master (default, unreleased bugfixes)
- upcoming (Edge) - upcoming (Edge)
- 1.13.1
- 1.13.0 - 1.13.0
- 1.12.3 - 1.12.3
- 1.12.2 - 1.12.2

View File

@ -43,9 +43,10 @@ body:
label: Version label: Version
description: What version of pokeemerald-expansion are you using? description: What version of pokeemerald-expansion are you using?
options: options:
- 1.13.1 (Latest release) - 1.13.2 (Latest release)
- master (default, unreleased bugfixes) - master (default, unreleased bugfixes)
- upcoming (Edge) - upcoming (Edge)
- 1.13.1
- 1.13.0 - 1.13.0
- 1.12.3 - 1.12.3
- 1.12.2 - 1.12.2

View File

@ -17,7 +17,7 @@
If you use **`pokeemerald-expansion`**, please credit **RHH (Rom Hacking Hideout)**. Optionally, include the version number for clarity. 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! Please consider [crediting all contributors](CREDITS.md) involved in the project!

View File

@ -199,7 +199,7 @@ BattleScript_EffectDoodle_AfterCopy:
printstring STRINGID_PKMNCOPIEDFOE printstring STRINGID_PKMNCOPIEDFOE
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER switchinabilities BS_ATTACKER
jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_MoveEnd jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_EffectDoodleMoveEnd
addbyte gBattleCommunication, 1 addbyte gBattleCommunication, 1
jumpifnoally BS_ATTACKER, BattleScript_EffectDoodleMoveEnd jumpifnoally BS_ATTACKER, BattleScript_EffectDoodleMoveEnd
setallytonextattacker BattleScript_EffectDoodle_CopyAbility setallytonextattacker BattleScript_EffectDoodle_CopyAbility
@ -751,13 +751,13 @@ BattleScript_FlingLightBall:
goto BattleScript_FlingEnd goto BattleScript_FlingEnd
BattleScript_FlingMentalHerb: BattleScript_FlingMentalHerb:
curecertainstatuses curecertainstatuses
savetarget saveattacker
copybyte gBattlerAttacker, gBattlerTarget copybyte gBattlerAttacker, gBattlerTarget
playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, NULL playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, NULL
printfromtable gMentalHerbCureStringIds printfromtable gMentalHerbCureStringIds
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
updatestatusicon BS_ATTACKER updatestatusicon BS_ATTACKER
restoretarget restoreattacker
goto BattleScript_FlingEnd goto BattleScript_FlingEnd
BattleScript_FlingPoisonBarb: BattleScript_FlingPoisonBarb:
seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_POISON seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_POISON
@ -7632,6 +7632,13 @@ BattleScript_AbilityCuredStatus::
updatestatusicon BS_SCRIPTING updatestatusicon BS_SCRIPTING
return return
BattleScript_AbilityCuredStatusEnd3::
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNSXCUREDITSYPROBLEM
waitmessage B_WAIT_TIME_LONG
updatestatusicon BS_SCRIPTING
end3
BattleScript_BattlerShookOffTaunt:: BattleScript_BattlerShookOffTaunt::
call BattleScript_AbilityPopUp call BattleScript_AbilityPopUp
printstring STRINGID_PKMNSHOOKOFFTHETAUNT printstring STRINGID_PKMNSHOOKOFFTHETAUNT

View File

@ -20,6 +20,7 @@
- [Day/Night System FAQ](tutorials/dns.md) - [Day/Night System FAQ](tutorials/dns.md)
- [Changelog](./CHANGELOG.md) - [Changelog](./CHANGELOG.md)
- [1.13.x]() - [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.1](changelogs/1.13.x/1.13.1.md)
- [Version 1.13.0](changelogs/1.13.x/1.13.0.md) - [Version 1.13.0](changelogs/1.13.x/1.13.0.md)
- [1.12.x]() - [1.12.x]()

View File

@ -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
<!--Last PR: 7814-->
<!--Used to keep track of the last PR merged in case new ones come in before the changelog is done.-->

View File

@ -598,7 +598,8 @@ struct PartyState
u32 supersweetSyrup:1; u32 supersweetSyrup:1;
u32 timesGotHit:5; u32 timesGotHit:5;
u32 changedSpecies:11; // For forms when multiple mons can change into the same pokemon. 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. // Cleared at the beginning of the battle. Fields need to be cleared when needed manually otherwise.

View File

@ -198,6 +198,7 @@ extern const u8 BattleScript_AbilityStatusEffect[];
extern const u8 BattleScript_SynchronizeActivates[]; extern const u8 BattleScript_SynchronizeActivates[];
extern const u8 BattleScript_NoItemSteal[]; extern const u8 BattleScript_NoItemSteal[];
extern const u8 BattleScript_AbilityCuredStatus[]; extern const u8 BattleScript_AbilityCuredStatus[];
extern const u8 BattleScript_AbilityCuredStatusEnd3[];
extern const u8 BattleScript_IgnoresWhileAsleep[]; extern const u8 BattleScript_IgnoresWhileAsleep[];
extern const u8 BattleScript_IgnoresAndUsesRandomMove[]; extern const u8 BattleScript_IgnoresAndUsesRandomMove[];
extern const u8 BattleScript_MoveUsedLoafingAround[]; extern const u8 BattleScript_MoveUsedLoafingAround[];

View File

@ -58,6 +58,7 @@ enum {
ABILITYEFFECT_OPPORTUNIST, ABILITYEFFECT_OPPORTUNIST,
ABILITYEFFECT_OPPORTUNIST_FIRST_TURN, ABILITYEFFECT_OPPORTUNIST_FIRST_TURN,
ABILITYEFFECT_SWITCH_IN_STATUSES, ABILITYEFFECT_SWITCH_IN_STATUSES,
ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES,
}; };
// For the first argument of ItemBattleEffects, to deteremine which block of item effects to try // 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); void ClearIllusionMon(u32 battler);
u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler); u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler);
bool32 SetIllusionMon(struct Pokemon *mon, u32 battler); bool32 SetIllusionMon(struct Pokemon *mon, u32 battler);
u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID);
bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler);
enum DamageCategory GetBattleMoveCategory(u32 move); enum DamageCategory GetBattleMoveCategory(u32 move);
void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move); void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move);
@ -409,6 +411,7 @@ bool32 IsPursuitTargetSet(void);
void ClearPursuitValuesIfSet(u32 battler); void ClearPursuitValuesIfSet(u32 battler);
void ClearPursuitValues(void); void ClearPursuitValues(void);
bool32 HasWeatherEffect(void); bool32 HasWeatherEffect(void);
bool32 IsAnyTargetAffected(u32 battlerAtk);
bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move); bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); bool32 HadMoreThanHalfHpNowDoesnt(u32 battler);
void UpdateStallMons(void); void UpdateStallMons(void);

View File

@ -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_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_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_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_LATEST 4 // Loops 4 times
#define NUM_BEEPS_GEN_3 -1 // Loops infinitely #define NUM_BEEPS_GEN_3 -1 // Loops infinitely

View File

@ -153,6 +153,7 @@ enum MoveEndEffects
MOVEEND_MULTIHIT_MOVE, MOVEEND_MULTIHIT_MOVE,
MOVEEND_MOVE_BLOCK, MOVEEND_MOVE_BLOCK,
MOVEEND_ITEM_EFFECTS_ATTACKER, MOVEEND_ITEM_EFFECTS_ATTACKER,
MOVEEND_ITEM_THROAT_SPRAY,
MOVEEND_ABILITY_BLOCK, MOVEEND_ABILITY_BLOCK,
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist
MOVEEND_COLOR_CHANGE, // Color Change / Berserk / Anger Shell MOVEEND_COLOR_CHANGE, // Color Change / Berserk / Anger Shell

View File

@ -1,7 +1,7 @@
#ifndef GUARD_CONSTANTS_EXPANSION_H #ifndef GUARD_CONSTANTS_EXPANSION_H
#define 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_MAJOR 1
#define EXPANSION_VERSION_MINOR 14 #define EXPANSION_VERSION_MINOR 14
#define EXPANSION_VERSION_PATCH 0 #define EXPANSION_VERSION_PATCH 0

View File

@ -41,6 +41,10 @@ enum GenConfigTag
GEN_CONFIG_PRANKSTER_DARK_TYPES, GEN_CONFIG_PRANKSTER_DARK_TYPES,
GEN_CONFIG_DESTINY_BOND_FAIL, GEN_CONFIG_DESTINY_BOND_FAIL,
GEN_CONFIG_POWDER_RAIN, GEN_CONFIG_POWDER_RAIN,
GEN_CONFIG_POWDER_GRASS,
GEN_CONFIG_OBLIVIOUS_TAUNT,
GEN_CONFIG_TOXIC_NEVER_MISS,
GEN_CONFIG_PARALYZE_ELECTRIC,
GEN_CONFIG_COUNT GEN_CONFIG_COUNT
}; };

View File

@ -44,6 +44,10 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_PRANKSTER_DARK_TYPES] = B_PRANKSTER_DARK_TYPES, [GEN_CONFIG_PRANKSTER_DARK_TYPES] = B_PRANKSTER_DARK_TYPES,
[GEN_CONFIG_DESTINY_BOND_FAIL] = B_DESTINY_BOND_FAIL, [GEN_CONFIG_DESTINY_BOND_FAIL] = B_DESTINY_BOND_FAIL,
[GEN_CONFIG_POWDER_RAIN] = B_POWDER_RAIN, [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 #if TESTING

View File

@ -853,6 +853,7 @@ u8 GetOpposingLinkMultiBattlerId(bool8 rightSide, u8 multiplayerId);
u16 FacilityClassToPicIndex(u16 facilityClass); u16 FacilityClassToPicIndex(u16 facilityClass);
u16 PlayerGenderToFrontTrainerPicId(u8 playerGender); u16 PlayerGenderToFrontTrainerPicId(u8 playerGender);
void HandleSetPokedexFlag(enum NationalDexOrder nationalNum, u8 caseId, u32 personality); void HandleSetPokedexFlag(enum NationalDexOrder nationalNum, u8 caseId, u32 personality);
void HandleSetPokedexFlagFromMon(struct Pokemon *mon, u32 caseId);
bool8 HasTwoFramesAnimation(u16 species); bool8 HasTwoFramesAnimation(u16 species);
struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 managerId, u8 mode); struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 managerId, u8 mode);
void DestroyMonSpritesGfxManager(u8 managerId); void DestroyMonSpritesGfxManager(u8 managerId);

View File

@ -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]} <trainers.h> <out>")
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)

View File

@ -1518,7 +1518,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
return bestMonId; 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()) if (!IsDoubleBattle())
return PARTY_SIZE; return PARTY_SIZE;
@ -2301,7 +2301,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
return aceMonId; return aceMonId;
// Fallback // Fallback
u32 bestMonId = GetFirstNonIvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2); u32 bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2);
if (bestMonId != PARTY_SIZE) if (bestMonId != PARTY_SIZE)
return bestMonId; return bestMonId;
@ -2423,7 +2423,7 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
return aceMonId; return aceMonId;
// Fallback // Fallback
bestMonId = GetFirstNonIvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2); bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2);
if (bestMonId != PARTY_SIZE) if (bestMonId != PARTY_SIZE)
return bestMonId; return bestMonId;

View File

@ -545,7 +545,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect) bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect)
{ {
if (ability == ABILITY_OVERCOAT 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) || holdEffect == HOLD_EFFECT_SAFETY_GOGGLES)
return FALSE; return FALSE;
return TRUE; return TRUE;
@ -1700,7 +1700,10 @@ enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId)
enum ItemHoldEffect AI_DecideHoldEffectForTurn(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)) if (!IsAiBattlerAware(battlerId))
holdEffect = gAiPartyData->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect; holdEffect = gAiPartyData->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect;

View File

@ -567,12 +567,14 @@ static void OpponentHandleChoosePokemon(u32 battler)
} }
} }
gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId;
GetBattlerPartyState(battler)->sentOut = TRUE;
} }
else else
{ {
chosenMonId = gBattleStruct->AI_monToSwitchIntoId[battler]; chosenMonId = gBattleStruct->AI_monToSwitchIntoId[battler];
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId;
GetBattlerPartyState(battler)->sentOut = TRUE;
} }
#if TESTING #if TESTING
TestRunner_Battle_CheckSwitch(battler, chosenMonId); TestRunner_Battle_CheckSwitch(battler, chosenMonId);

View File

@ -285,7 +285,6 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler)
else if (gBattleStruct->monToSwitchIntoId[battler] >= PARTY_SIZE || !IsValidForBattle(&gPlayerParty[gBattleStruct->monToSwitchIntoId[battler]])) else if (gBattleStruct->monToSwitchIntoId[battler] >= PARTY_SIZE || !IsValidForBattle(&gPlayerParty[gBattleStruct->monToSwitchIntoId[battler]]))
{ {
chosenMonId = GetMostSuitableMonToSwitchInto(battler, SWITCH_AFTER_KO); chosenMonId = GetMostSuitableMonToSwitchInto(battler, SWITCH_AFTER_KO);
if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon
{ {
s32 firstId = (IsAiVsAiBattle()) ? 0 : (PARTY_SIZE / 2); s32 firstId = (IsAiVsAiBattle()) ? 0 : (PARTY_SIZE / 2);
@ -303,12 +302,14 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler)
} }
} }
gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId;
GetBattlerPartyState(battler)->sentOut = TRUE;
} }
else // Mon to switch out has been already chosen. else // Mon to switch out has been already chosen.
{ {
chosenMonId = gBattleStruct->monToSwitchIntoId[battler]; chosenMonId = gBattleStruct->monToSwitchIntoId[battler];
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId;
GetBattlerPartyState(battler)->sentOut = TRUE;
} }
BtlController_EmitChosenMonReturnValue(battler, B_COMM_TO_ENGINE, chosenMonId, NULL); BtlController_EmitChosenMonReturnValue(battler, B_COMM_TO_ENGINE, chosenMonId, NULL);
BtlController_Complete(battler); BtlController_Complete(battler);

View File

@ -61,8 +61,8 @@ enum EndTurnResolutionOrder
ENDTURN_TERRAIN, ENDTURN_TERRAIN,
ENDTURN_THIRD_EVENT_BLOCK, ENDTURN_THIRD_EVENT_BLOCK,
ENDTURN_EMERGENCY_EXIT_4, ENDTURN_EMERGENCY_EXIT_4,
ENDTURN_ABILITIES, ENDTURN_FORM_CHANGE_ABILITIES,
ENDTURN_FOURTH_EVENT_BLOCK, ENDTURN_EJECT_PACK,
ENDTURN_DYNAMAX, ENDTURN_DYNAMAX,
ENDTURN_COUNT, ENDTURN_COUNT,
}; };
@ -101,13 +101,6 @@ enum ThirdEventBlock
THIRD_EVENT_BLOCK_ITEMS, 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) static u32 GetBattlerSideForMessage(u32 side)
{ {
u32 battler = 0; u32 battler = 0;
@ -1456,7 +1449,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
return effect; return effect;
} }
static bool32 HandleEndTurnAbilities(u32 battler) static bool32 HandleEndTurnFormChangeAbilities(u32 battler)
{ {
bool32 effect = FALSE; bool32 effect = FALSE;
@ -1470,6 +1463,7 @@ static bool32 HandleEndTurnAbilities(u32 battler)
case ABILITY_SCHOOLING: case ABILITY_SCHOOLING:
case ABILITY_SHIELDS_DOWN: case ABILITY_SHIELDS_DOWN:
case ABILITY_ZEN_MODE: case ABILITY_ZEN_MODE:
case ABILITY_HUNGER_SWITCH:
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE; effect = TRUE;
default: default:
@ -1479,38 +1473,10 @@ static bool32 HandleEndTurnAbilities(u32 battler)
return effect; return effect;
} }
static bool32 HandleEndTurnFourthEventBlock(u32 battler) static bool32 HandleEndTurnEjectPack(u32 battler)
{ {
bool32 effect = FALSE; gBattleStruct->turnEffectsBattlerId++;
return TrySwitchInEjectPack(ITEMEFFECT_NORMAL);
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;
} }
static bool32 HandleEndTurnDynamax(u32 battler) static bool32 HandleEndTurnDynamax(u32 battler)
@ -1590,8 +1556,8 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) =
[ENDTURN_TERRAIN] = HandleEndTurnTerrain, [ENDTURN_TERRAIN] = HandleEndTurnTerrain,
[ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock, [ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock,
[ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit, [ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit,
[ENDTURN_ABILITIES] = HandleEndTurnAbilities, [ENDTURN_FORM_CHANGE_ABILITIES] = HandleEndTurnFormChangeAbilities,
[ENDTURN_FOURTH_EVENT_BLOCK] = HandleEndTurnFourthEventBlock, [ENDTURN_EJECT_PACK] = HandleEndTurnEjectPack,
[ENDTURN_DYNAMAX] = HandleEndTurnDynamax, [ENDTURN_DYNAMAX] = HandleEndTurnDynamax,
}; };

View File

@ -3735,6 +3735,10 @@ static void DoBattleIntro(void)
gBattleStruct->overworldWeatherDone = FALSE; gBattleStruct->overworldWeatherDone = FALSE;
Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. 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 // Try to set a status to start the battle with
gBattleStruct->startingStatus = 0; gBattleStruct->startingStatus = 0;
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentB)) if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentB))
@ -3856,6 +3860,8 @@ static void TryDoEventsBeforeFirstTurn(void)
i = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; i = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, i, gBattleMons[i].ability, 0, 0) != 0) if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, i, gBattleMons[i].ability, 0, 0) != 0)
return; return;
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, i, 0, 0, 0) != 0)
return;
} }
gBattleStruct->switchInBattlerCounter = 0; gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++; gBattleStruct->eventsBeforeFirstTurnState++;
@ -5163,6 +5169,7 @@ static void TurnValuesCleanUp(bool8 var0)
gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF; gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF;
gBattleStruct->battlerState[i].usedEjectItem = FALSE; gBattleStruct->battlerState[i].usedEjectItem = FALSE;
gProtectStructs[i].lashOutAffected = FALSE; gProtectStructs[i].lashOutAffected = FALSE;
gDisableStructs[i].endured = FALSE;
} }
gSideTimers[B_SIDE_PLAYER].followmeTimer = 0; gSideTimers[B_SIDE_PLAYER].followmeTimer = 0;
@ -5587,14 +5594,31 @@ static void HandleEndTurn_FinishBattle(void)
GetMonData(GetBattlerMon(battler), MON_DATA_NICKNAME, gBattleResults.playerMon2Name); GetMonData(GetBattlerMon(battler), MON_DATA_NICKNAME, gBattleResults.playerMon2Name);
} }
} }
else if (!IsOnPlayerSide(battler))
{
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[battler].species), FLAG_SET_SEEN, gBattleMons[battler].personality);
}
} }
TryPutPokemonTodayOnAir(); 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 if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_RECORDED_LINK
| BATTLE_TYPE_TRAINER | BATTLE_TYPE_TRAINER
@ -5720,20 +5744,24 @@ static void TryEvolvePokemon(void)
if (!(sTriedEvolving & (1u << i))) if (!(sTriedEvolving & (1u << i)))
{ {
bool32 canStopEvo = TRUE; 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; sTriedEvolving |= 1u << i;
if (species == SPECIES_NONE && (gLeveledUpInBattle & (1u << i))) if (species == SPECIES_NONE && (gLeveledUpInBattle & (1u << i)))
{ {
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) if (species != SPECIES_NONE)
{ {
FreeAllWindowBuffers(); FreeAllWindowBuffers();
gBattleMainFunc = WaitForEvoSceneToFinish; 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); EvolutionScene(&gPlayerParty[i], species, canStopEvo, i);
return; return;
} }

View File

@ -862,7 +862,7 @@ static bool8 DoesTypePreventStatus(u16 species, u32 status)
break; break;
case STATUS1_PARALYSIS: case STATUS1_PARALYSIS:
if (GetSpeciesType(species, 0) == TYPE_GROUND || GetSpeciesType(species, 1) == TYPE_GROUND 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; ret = TRUE;
break; break;
case STATUS1_BURN: case STATUS1_BURN:

View File

@ -966,19 +966,17 @@ static void ValidateSavedBattlerCounts(void)
{ {
if (gBattleStruct->savedAttackerCount > 0) if (gBattleStruct->savedAttackerCount > 0)
{ {
// #if TESTING if (TESTING)
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!"); Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!", __FILE__, __LINE__);
// #else else
DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!"); DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #endif
} }
if (gBattleStruct->savedTargetCount > 0) if (gBattleStruct->savedTargetCount > 0)
{ {
// #if TESTING if (TESTING)
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!"); Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!", __FILE__, __LINE__);
// #else else
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!"); DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #endif
} }
} }
@ -1066,7 +1064,7 @@ bool32 IsMovePowderBlocked(struct BattleContext *ctx)
if (IsPowderMove(ctx->currentMove) && (ctx->battlerAtk != ctx->battlerDef)) 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)) && (IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GRASS) || ctx->ability[ctx->battlerDef] == ABILITY_OVERCOAT))
{ {
gBattlerAbility = ctx->battlerDef; 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 BtlController_EmitSetMonData(battlerItem, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item
MarkBattlerForControllerExec(battlerItem); MarkBattlerForControllerExec(battlerItem);
gBattleStruct->choicedMove[battlerItem] = 0; if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
TrySaveExchangedItem(battlerItem, gLastUsedItem); TrySaveExchangedItem(battlerItem, gLastUsedItem);
} }
@ -5668,7 +5667,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
gLastUsedItem = gBattleMons[gBattlerTarget].item; gLastUsedItem = gBattleMons[gBattlerTarget].item;
gBattleMons[gBattlerTarget].item = 0; gBattleMons[gBattlerTarget].item = 0;
if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS) if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerTarget] = 0; gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
CheckSetUnburden(gBattlerTarget); CheckSetUnburden(gBattlerTarget);
// In Gen 5+, Knock Off removes the target's item rather than rendering it unusable. // 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++; gBattleScripting.moveendState++;
break; break;
case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities
if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0)) for (u16 battler = 0; battler < gBattlersCount; battler++)
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers {
else if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0))
effect = TRUE;
}
if(!effect)
gBattleScripting.moveendState++; gBattleScripting.moveendState++;
break; break;
case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize
@ -6455,10 +6457,30 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++; gBattleScripting.moveendState++;
break; break;
case MOVEEND_ITEM_EFFECTS_ATTACKER: 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)) if (ItemBattleEffects(ITEMEFFECT_MOVE_END, gBattlerAttacker))
effect = TRUE; effect = TRUE;
gBattleScripting.moveendState++; gBattleScripting.moveendState++;
break; 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: case MOVEEND_ABILITY_BLOCK:
effect = HandleMoveEndAbilityBlock(gBattlerAttacker, gBattlerTarget, gCurrentMove); effect = HandleMoveEndAbilityBlock(gBattlerAttacker, gBattlerTarget, gCurrentMove);
gBattleScripting.moveendState++; gBattleScripting.moveendState++;
@ -7148,13 +7170,7 @@ static void Cmd_switchinanim(void)
battler = GetBattlerForBattleScript(cmd->battler); battler = GetBattlerForBattleScript(cmd->battler);
if (!IsOnPlayerSide(battler) GetBattlerPartyState(battler)->sentOut = TRUE;
&& !(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);
gAbsentBattlerFlags &= ~(1u << battler); gAbsentBattlerFlags &= ~(1u << battler);
@ -12516,7 +12532,7 @@ static void Cmd_settaunt(void)
{ {
CMD_ARGS(const u8 *failInstr); 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; gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp;
gLastUsedAbility = ABILITY_OBLIVIOUS; 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); BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item);
MarkBattlerForControllerExec(gBattlerTarget); MarkBattlerForControllerExec(gBattlerTarget);
gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE; gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE;
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
@ -16905,6 +16923,7 @@ void BS_SwitchinAbilities(void)
AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0);
if (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect()) if (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect())
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0);

View File

@ -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_TERA_TYPE, &data);
} }
if (ball != BALL_STRANGE)
SetMonData(dst, MON_DATA_POKEBALL, &ball); SetMonData(dst, MON_DATA_POKEBALL, &ball);
CalculateMonStats(dst); CalculateMonStats(dst);
} }

View File

@ -66,7 +66,6 @@ static u32 GetFlingPowerFromItemId(u32 itemId);
static void SetRandomMultiHitCounter(); static void SetRandomMultiHitCounter();
static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item);
static bool32 CanBeInfinitelyConfused(u32 battler); 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 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); static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option);
@ -420,7 +419,8 @@ void HandleAction_UseMove(void)
{ {
gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove; gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove;
gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos; 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 // check if the encored move wasn't overwritten
else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE 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: case ABILITY_EFFECT_SPORE:
{ {
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker); 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 && abilityAtk != ABILITY_OVERCOAT
&& GetBattlerHoldEffect(gBattlerAttacker) != HOLD_EFFECT_SAFETY_GOGGLES) && GetBattlerHoldEffect(gBattlerAttacker) != HOLD_EFFECT_SAFETY_GOGGLES)
{ {
@ -5386,96 +5386,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, enum Ability ability, u32 spec
} }
break; break;
case ABILITYEFFECT_IMMUNITY: case ABILITYEFFECT_IMMUNITY:
for (battler = 0; battler < gBattlersCount; battler++) effect = TryImmunityAbilityHealStatus(battler, caseID);
{ if (effect)
switch (GetBattlerAbilityIgnoreMoldBreaker(battler)) return effect;
{ break;
case ABILITY_IMMUNITY: case ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES:
case ABILITY_PASTEL_VEIL: effect = TryImmunityAbilityHealStatus(battler, caseID);
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;
}
}
break; break;
case ABILITYEFFECT_SYNCHRONIZE: case ABILITYEFFECT_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE) 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; 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; battleScript = BattleScript_NotAffected;
} }
@ -6801,6 +6717,9 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
BattleScriptCall(BattleScript_AttackerItemStatRaise); BattleScriptCall(BattleScript_AttackerItemStatRaise);
} }
break; break;
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, ITEMEFFECT_NONE);
break;
default: default:
break; break;
} }
@ -10771,6 +10690,110 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler)
return FALSE; 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) bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler)
{ {
if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0) if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0)
@ -11738,7 +11761,7 @@ bool32 HasWeatherEffect(void)
return TRUE; return TRUE;
} }
static bool32 IsAnyTargetAffected(u32 battlerAtk) bool32 IsAnyTargetAffected(u32 battlerAtk)
{ {
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{ {
@ -11825,14 +11848,11 @@ bool32 TrySwitchInEjectPack(enum ItemCaseId caseID)
gBattleScripting.battler = battler; gBattleScripting.battler = battler;
gLastUsedItem = gBattleMons[battler].item; gLastUsedItem = gBattleMons[battler].item;
if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN)
{
BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3); BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3);
} else if (caseID == ITEMEFFECT_NORMAL)
BattleScriptExecute(BattleScript_EjectPackActivate_End2);
else else
{ BattleScriptCall(BattleScript_EjectPackActivate_Ret);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret;
}
gAiLogicData->ejectPackSwitch = TRUE; gAiLogicData->ejectPackSwitch = TRUE;
return TRUE; return TRUE;
} }
@ -11985,7 +12005,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abil
u32 nonVolatileStatus = GetMoveNonVolatileStatus(move); u32 nonVolatileStatus = GetMoveNonVolatileStatus(move);
if ((gBattleMons[battlerDef].volatiles.lockOn && gDisableStructs[battlerDef].battlerWithSureHit == battlerAtk) 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) || gBattleMons[battlerDef].volatiles.glaiveRush)
{ {
effect = TRUE; effect = TRUE;

View File

@ -1,16 +1,16 @@
#include "global.h" #include "global.h"
#include "berry.h"
#include "clock.h" #include "clock.h"
#include "dewford_trend.h"
#include "event_data.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 "rtc.h"
#include "time_events.h" #include "time_events.h"
#include "field_specials.h"
#include "lottery_corner.h"
#include "dewford_trend.h"
#include "tv.h" #include "tv.h"
#include "field_weather.h"
#include "berry.h"
#include "main.h"
#include "overworld.h"
#include "wallclock.h" #include "wallclock.h"
#include "constants/form_change_types.h" #include "constants/form_change_types.h"

View File

@ -2889,6 +2889,10 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu
//Moves //Moves
for (i = 0; i < MAX_MON_MOVES; i++) 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) if (moves[i] == MOVE_NONE || moves[i] >= MOVES_COUNT)
continue; continue;

View File

@ -5851,16 +5851,23 @@ static u8 IsEasyChatWordUnlocked(u16 easyChatWord)
void InitializeEasyChatWordArray(u16 *words, u16 length) void InitializeEasyChatWordArray(u16 *words, u16 length)
{ {
u16 i; u16 i;
for (i = length - 1; i != EC_EMPTY_WORD; i--) if (words != NULL)
*(words++) = EC_EMPTY_WORD; {
for (i = length - 1; i != EC_EMPTY_WORD; i--)
*(words++) = EC_EMPTY_WORD;
}
} }
void InitQuestionnaireWords(void) void InitQuestionnaireWords(void)
{ {
int i; int i;
u16 *words = GetQuestionnaireWordsPtr(); 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) bool32 IsEasyChatAnswerUnlocked(int easyChatWord)

View File

@ -5230,13 +5230,13 @@ static void ShowMoveSelectWindow(u8 slot)
{ {
u8 i; u8 i;
u8 moveCount = 0; u8 moveCount = 0;
u8 fontId = FONT_NORMAL;
u8 windowId = DisplaySelectionWindow(SELECTWINDOW_MOVES); u8 windowId = DisplaySelectionWindow(SELECTWINDOW_MOVES);
u16 move; u16 move;
for (i = 0; i < MAX_MON_MOVES; i++) for (i = 0; i < MAX_MON_MOVES; i++)
{ {
move = GetMonData(&gPlayerParty[slot], MON_DATA_MOVE1 + 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); AddTextPrinterParameterized(windowId, fontId, GetMoveName(move), 8, (i * 16) + 1, TEXT_SKIP_DRAW, NULL);
if (move != MOVE_NONE) if (move != MOVE_NONE)
moveCount++; moveCount++;
@ -8025,4 +8025,3 @@ static void FieldCallback_RockClimb(void)
gFieldEffectArguments[0] = GetCursorSelectionMonId(); gFieldEffectArguments[0] = GetCursorSelectionMonId();
FieldEffectStart(FLDEFF_USE_ROCK_CLIMB); FieldEffectStart(FLDEFF_USE_ROCK_CLIMB);
} }

View File

@ -4844,7 +4844,7 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16
case EVO_MODE_ITEM_CHECK: case EVO_MODE_ITEM_CHECK:
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++) for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
{ {
bool32 conditionMet = FALSE; bool32 conditionsMet = FALSE;
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE) if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
continue; continue;
@ -4852,11 +4852,11 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16
{ {
case EVO_ITEM: case EVO_ITEM:
if (evolutions[i].param == evolutionItem) if (evolutions[i].param == evolutionItem)
conditionMet = TRUE; conditionsMet = TRUE;
break; 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. // All checks passed, so stop checking the rest of the evolutions.
// This is different from vanilla where the loop continues. // 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) switch (evolutions[i].method)
{ {
case EVO_BATTLE_END: case EVO_BATTLE_END:
conditionsMet = TRUE; conditionsMet = TRUE;
break; break;
} }
if (conditionsMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, evolutionItem, canStopEvo, evoState)) 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: case EVO_SPIN:
if (gSpecialVar_0x8000 == evolutions[i].param) if (gSpecialVar_0x8000 == evolutions[i].param)
conditionsMet = TRUE; conditionsMet = TRUE;
break; 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) bool8 HasTwoFramesAnimation(u16 species)
{ {
return P_TWO_FRAME_FRONT_SPRITES return P_TWO_FRAME_FRONT_SPRITES

View File

@ -63,3 +63,19 @@ SINGLE_BATTLE_TEST("Immunity doesn't prevent Pokémon from being poisoned by Tox
NOT HP_BAR(player); 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);
}
}

View File

@ -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 { GIVEN {
WITH_CONFIG(GEN_CONFIG_OBLIVIOUS_TAUNT, gen);
ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT); ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT);
ASSUME(B_OBLIVIOUS_TAUNT >= GEN_6);
PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); } PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); }
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { MOVE(opponent, MOVE_TAUNT); } TURN { MOVE(opponent, MOVE_TAUNT); }
TURN { MOVE(player, MOVE_SPORE); } TURN { MOVE(player, MOVE_SPORE, allowed: gen == GEN_6); }
} SCENE { } SCENE {
ABILITY_POPUP(player, ABILITY_OBLIVIOUS); if (gen == GEN_6) {
NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); } NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); }
MESSAGE("It doesn't affect Slowpoke…"); ABILITY_POPUP(player, ABILITY_OBLIVIOUS);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); MESSAGE("It doesn't affect Slowpoke…");
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); 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);
}
}
} }
} }

View File

@ -1,25 +1,37 @@
#include "global.h" #include "global.h"
#include "test/battle.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 { GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen);
ASSUME(IsPowderMove(MOVE_STUN_SPORE)); ASSUME(IsPowderMove(MOVE_STUN_SPORE));
PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); } OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); }
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_STUN_SPORE); } TURN { MOVE(player, MOVE_STUN_SPORE); }
} SCENE { } SCENE {
ABILITY_POPUP(opponent, ABILITY_OVERCOAT); if (gen == GEN_6) {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
MESSAGE("It doesn't affect the opposing Pineco…"); 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") DOUBLE_BATTLE_TEST("Overcoat blocks damage from sandstorm")
{ {
GIVEN { GIVEN {
PLAYER(SPECIES_WYNAUT) { Speed(50); } PLAYER(SPECIES_WYNAUT) { Speed(50); }
PLAYER(SPECIES_HELIOLISK) { Speed(40); Ability(ABILITY_SAND_VEIL); } PLAYER(SPECIES_HELIOLISK) { Speed(40); Ability(ABILITY_SAND_VEIL); }
OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); } OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_STARLY) { Speed(20); } OPPONENT(SPECIES_STARLY) { Speed(20); }
@ -41,7 +53,7 @@ DOUBLE_BATTLE_TEST("Overcoat blocks damage from hail")
{ {
GIVEN { GIVEN {
ASSUME(GetMoveEffect(MOVE_HAIL) == EFFECT_HAIL); 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); } PLAYER(SPECIES_SOLOSIS) { Speed(40); Ability(ABILITY_RUN_AWAY); }
OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); } OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_SNORUNT) { Speed(20); } OPPONENT(SPECIES_SNORUNT) { Speed(20); }
@ -73,4 +85,3 @@ SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect")
EXPECT_EQ(player->status1, 0); EXPECT_EQ(player->status1, 0);
} }
} }

View File

@ -292,10 +292,10 @@ SINGLE_BATTLE_TEST("Parental Bond Snore strikes twice while asleep")
HP_BAR(opponent, captureDamage: &damage[1]); HP_BAR(opponent, captureDamage: &damage[1]);
MESSAGE("The Pokémon was hit 2 time(s)!"); MESSAGE("The Pokémon was hit 2 time(s)!");
} THEN { } THEN {
if (B_PARENTAL_BOND_DMG == GEN_6) if (B_PARENTAL_BOND_DMG >= GEN_7)
EXPECT_MUL_EQ(damage[0], Q_4_12(0.5), damage[1]);
else
EXPECT_MUL_EQ(damage[0], Q_4_12(0.25), damage[1]); 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]);
} }
} }

View File

@ -59,6 +59,7 @@ SINGLE_BATTLE_TEST("Protosynthesis ability pop up activates only once during the
u16 turns; u16 turns;
GIVEN { GIVEN {
WITH_CONFIG(GEN_CONFIG_ABILITY_WEATHER, GEN_6);
PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); } PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); }
OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); }; OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); };
} WHEN { } WHEN {

View File

@ -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); } 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); } TURN { SWITCH(opponent, 1); }
} SCENE { } 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); 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); ABILITY_POPUP(opponent, ABILITY_SUPERSWEET_SYRUP);
NONE_OF { NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);

View File

@ -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 // 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); PASSES_RANDOMLY(5, 10, RNG_AI_PREDICT_SWITCH);
GIVEN { 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); 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_SKARMORY) { Moves(MOVE_SCRATCH); }
PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_ACROBATICS); } PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_ACROBATICS); }

View File

@ -101,6 +101,7 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Marshadow vs Mawile)")
PARAMETRIZE { expectedDamage = 123; } PARAMETRIZE { expectedDamage = 123; }
GIVEN { GIVEN {
ASSUME(GetMoveCategory(MOVE_SPECTRAL_THIEF) == DAMAGE_CATEGORY_PHYSICAL); 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); } PLAYER(SPECIES_MARSHADOW) { Level(100); Attack(286); }
OPPONENT(SPECIES_MAWILE) { Level(100); Defense(226); HP(241); } OPPONENT(SPECIES_MAWILE) { Level(100); Defense(226); HP(241); }
} WHEN { } WHEN {
@ -196,6 +197,7 @@ SINGLE_BATTLE_TEST("Gem boosted Damage calculation")
{ {
s16 dmg; s16 dmg;
s16 expectedDamage; s16 expectedDamage;
#if I_GEM_BOOST_POWER >= GEN_6
PARAMETRIZE { expectedDamage = 240; } PARAMETRIZE { expectedDamage = 240; }
PARAMETRIZE { expectedDamage = 237; } PARAMETRIZE { expectedDamage = 237; }
PARAMETRIZE { expectedDamage = 234; } PARAMETRIZE { expectedDamage = 234; }
@ -212,6 +214,25 @@ SINGLE_BATTLE_TEST("Gem boosted Damage calculation")
PARAMETRIZE { expectedDamage = 208; } PARAMETRIZE { expectedDamage = 208; }
PARAMETRIZE { expectedDamage = 205; } PARAMETRIZE { expectedDamage = 205; }
PARAMETRIZE { expectedDamage = 204; } 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 { GIVEN {
PLAYER(SPECIES_MAKUHITA) { Item(ITEM_FIGHTING_GEM); } PLAYER(SPECIES_MAKUHITA) { Item(ITEM_FIGHTING_GEM); }
OPPONENT(SPECIES_MAKUHITA); OPPONENT(SPECIES_MAKUHITA);

View File

@ -1464,7 +1464,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Chi Strike boosts allies' crit chance by 1 st
{ {
u32 j; u32 j;
GIVEN { 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)); ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_CHI_STRIKE, MOVE_EFFECT_CRIT_PLUS_SIDE));
PLAYER(SPECIES_MACHAMP) { GigantamaxFactor(TRUE); } PLAYER(SPECIES_MACHAMP) { GigantamaxFactor(TRUE); }
PLAYER(SPECIES_MACHOP); PLAYER(SPECIES_MACHOP);

View File

@ -606,14 +606,14 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves
s16 damage[4]; s16 damage[4];
GIVEN { GIVEN {
ASSUME(GetMovePower(MOVE_MEGA_DRAIN) == 40); ASSUME(GetMovePower(MOVE_MEGA_DRAIN) == 40);
ASSUME(GetMovePower(MOVE_BUBBLE) == 40); ASSUME(GetMovePower(MOVE_WATER_GUN) == 40);
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_MEGA_DRAIN); } TURN { MOVE(player, MOVE_MEGA_DRAIN); }
TURN { MOVE(player, MOVE_MEGA_DRAIN, gimmick: GIMMICK_TERA); } TURN { MOVE(player, MOVE_MEGA_DRAIN, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_MEGA_DRAIN); } TURN { MOVE(player, MOVE_MEGA_DRAIN); }
TURN { MOVE(player, MOVE_BUBBLE); } TURN { MOVE(player, MOVE_WATER_GUN); }
} SCENE { } SCENE {
// turn 1 // turn 1
MESSAGE("Wobbuffet used Mega Drain!"); 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); ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
HP_BAR(opponent, captureDamage: &damage[2]); HP_BAR(opponent, captureDamage: &damage[2]);
// turn 4 // turn 4
MESSAGE("Wobbuffet used Bubble!"); MESSAGE("Wobbuffet used Water Gun!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
HP_BAR(opponent, captureDamage: &damage[3]); HP_BAR(opponent, captureDamage: &damage[3]);
} THEN { } THEN {
// The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost. // The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost.

View File

@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Big Root increases healing from absorbing moves", s16 damage
PARAMETRIZE { item = ITEM_BIG_ROOT; } PARAMETRIZE { item = ITEM_BIG_ROOT; }
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); } PLAYER(SPECIES_XURKITREE) { HP(200); Item(item); }
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_ABSORB); } 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); HP_BAR(player, captureDamage: &results[i].heal);
} FINALLY { } FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage); // Damage is unaffected 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; } PARAMETRIZE { item = ITEM_BIG_ROOT; }
GIVEN { GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); } PLAYER(SPECIES_XURKITREE) { HP(200); Item(item); }
OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); } OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); }
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_ABSORB); } 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); ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
HP_BAR(player, captureDamage: &results[i].damage); HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY { } 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);
} }
} }

View File

@ -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") SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunlight ends")
{ {
GIVEN { 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); } 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); }; OPPONENT(SPECIES_TORKOAL) { Speed(100); Ability(ABILITY_DROUGHT); };
} WHEN { } WHEN {

View File

@ -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); 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);
}
}

View File

@ -32,7 +32,6 @@ SINGLE_BATTLE_TEST("Gem boost is only applied once")
s16 normalHit; s16 normalHit;
GIVEN { GIVEN {
ASSUME(I_GEM_BOOST_POWER >= GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); };
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
@ -46,7 +45,10 @@ SINGLE_BATTLE_TEST("Gem boost is only applied once")
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent, captureDamage: &normalHit); HP_BAR(opponent, captureDamage: &normalHit);
} THEN { } 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);
} }
} }

View File

@ -30,7 +30,6 @@ SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consume
PARAMETRIZE { heldItem = ITEM_NONE; } PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_FLYING_GEM; } PARAMETRIZE { heldItem = ITEM_FLYING_GEM; }
GIVEN { GIVEN {
ASSUME(I_GEM_BOOST_POWER >= GEN_6);
ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS); ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS);
ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING); ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);

View File

@ -4,7 +4,6 @@
ASSUMPTIONS ASSUMPTIONS
{ {
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS); 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") 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 *chosenTarget = NULL;
struct BattlePokemon *finalTarget = NULL; struct BattlePokemon *finalTarget = NULL;
u32 speciesLeft, speciesRight; u32 speciesLeft, speciesRight;
PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; } PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentRight; speciesLeft = SPECIES_FIDOUGH; speciesRight = SPECIES_WOBBUFFET; }
PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; } PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_FIDOUGH; speciesRight = SPECIES_WOBBUFFET; }
PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_FIDOUGH; }
PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; } PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_FIDOUGH; }
GIVEN { 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);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(speciesLeft); 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") DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me user")
{ {
GIVEN { GIVEN {
ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(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 { 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);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CLEFAIRY); OPPONENT(SPECIES_FIDOUGH);
} WHEN { } WHEN {
TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); } TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE { } 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") DOUBLE_BATTLE_TEST("Dragon Darts fails to strike the second target if first target fainted and follow me was active")
{ {
GIVEN { GIVEN {
ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);

View File

@ -1,13 +1,17 @@
#include "global.h" #include "global.h"
#include "test/battle.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"); 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") SINGLE_BATTLE_TEST("Endure does not prevent multiple hits and stat changes occur at the end of the turn")
{ {
GIVEN { GIVEN {
ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT); ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT);
ASSUME(GetMoveEffect(MOVE_ENDURE) == EFFECT_ENDURE);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); } OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
} WHEN { } 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'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 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 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

View File

@ -12,15 +12,14 @@ SINGLE_BATTLE_TEST("Fickle Beam deals double damage 30% of the time")
PASSES_RANDOMLY(30, 100, RNG_FICKLE_BEAM); PASSES_RANDOMLY(30, 100, RNG_FICKLE_BEAM);
GIVEN { GIVEN {
ASSUME(GetMovePower(MOVE_POWER_GEM) == 80); ASSUME(GetMovePower(MOVE_DAZZLING_GLEAM) == GetMovePower(MOVE_FICKLE_BEAM));
ASSUME(GetMovePower(MOVE_FICKLE_BEAM) == 80);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_POWER_GEM); } TURN { MOVE(player, MOVE_DAZZLING_GLEAM); }
TURN { MOVE(player, MOVE_FICKLE_BEAM); } TURN { MOVE(player, MOVE_FICKLE_BEAM); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_GEM, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_DAZZLING_GLEAM, player);
HP_BAR(opponent, captureDamage: &damage[0]); HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FICKLE_BEAM, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_FICKLE_BEAM, player);
HP_BAR(opponent, captureDamage: &damage[1]); HP_BAR(opponent, captureDamage: &damage[1]);

View File

@ -1,10 +1,18 @@
#include "global.h" #include "global.h"
#include "test/battle.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 ASSUMPTIONS
{ {
ASSUME(GetMovePower(MOVE_SEED_FLARE) == GetMovePower(MOVE_FUTURE_SIGHT)); ASSUME(GetMovePower(FUTURE_SIGHT_EQUIVALENT) == GetMovePower(MOVE_FUTURE_SIGHT));
ASSUME(GetMoveCategory(MOVE_SEED_FLARE) == GetMoveCategory(MOVE_FUTURE_SIGHT)); ASSUME(GetMoveCategory(FUTURE_SIGHT_EQUIVALENT) == GetMoveCategory(MOVE_FUTURE_SIGHT));
ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT); ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT);
ASSUME(GetMovePower(MOVE_FUTURE_SIGHT) > 0); 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); } PLAYER(SPECIES_RAICHU) { Item(item); }
OPPONENT(SPECIES_REGICE); OPPONENT(SPECIES_REGICE);
} WHEN { } 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 { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); } TURN { SWITCH(player, 1); }
TURN { } TURN { }
TURN { } TURN { }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent, captureDamage: &seedFlareDmg); HP_BAR(opponent, captureDamage: &seedFlareDmg);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
MESSAGE("The opposing Regice took the Future Sight attack!"); 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); } PLAYER(SPECIES_RAICHU) { Item(ITEM_LIFE_ORB); }
OPPONENT(SPECIES_REGICE); OPPONENT(SPECIES_REGICE);
} WHEN { } 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 { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); } TURN { SWITCH(player, 1); }
TURN { } TURN { }
TURN { } TURN { }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent, captureDamage: &seedFlareDmg); HP_BAR(opponent, captureDamage: &seedFlareDmg);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
MESSAGE("The opposing Regice took the Future Sight attack!"); 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); PLAYER(SPECIES_RAICHU);
OPPONENT(SPECIES_REGICE); OPPONENT(SPECIES_REGICE);
} WHEN { } 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 { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); } TURN { SWITCH(player, 1); }
TURN { } TURN { }
TURN { } TURN { }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent, captureDamage: &seedFlareDmg); HP_BAR(opponent, captureDamage: &seedFlareDmg);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
HP_BAR(opponent, captureDamage: &futureSightDmg); HP_BAR(opponent, captureDamage: &futureSightDmg);
@ -100,13 +108,13 @@ SINGLE_BATTLE_TEST("Future Sight is affected by type effectiveness (Gen 5+)")
PLAYER(SPECIES_RAICHU); PLAYER(SPECIES_RAICHU);
OPPONENT(SPECIES_HOUNDOOM); OPPONENT(SPECIES_HOUNDOOM);
} WHEN { } 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 { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); } TURN { SWITCH(player, 1); }
TURN { } TURN { }
TURN { } TURN { }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player); ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent); HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
MESSAGE("The opposing Houndoom took the Future Sight attack!"); MESSAGE("The opposing Houndoom took the Future Sight attack!");

View File

@ -280,6 +280,7 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after inst
PARAMETRIZE { moveTarget = opponentLeft; } PARAMETRIZE { moveTarget = opponentLeft; }
PARAMETRIZE { moveTarget = opponentRight; } PARAMETRIZE { moveTarget = opponentRight; }
GIVEN { GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME); ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME);
ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE); ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE);
ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK); ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK);

View File

@ -177,14 +177,14 @@ SINGLE_BATTLE_TEST("Scale Shot is immune to Fairy types and will end the move co
GIVEN { GIVEN {
ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT); ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT);
ASSUME(GetMoveType(MOVE_SCALE_SHOT) == TYPE_DRAGON); 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); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CLEFAIRY) { HP(1); } OPPONENT(SPECIES_FIDOUGH) { HP(1); }
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_SCALE_SHOT); } TURN { MOVE(player, MOVE_SCALE_SHOT); }
} SCENE { } SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALE_SHOT, player); 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");
} }
} }

View File

@ -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 { GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(GetSpeciesType(SPECIES_VENUSAUR, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_VENUSAUR, 1) == TYPE_GRASS); ASSUME(GetSpeciesType(SPECIES_VENUSAUR, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_VENUSAUR, 1) == TYPE_GRASS);
PLAYER(SPECIES_VENUSAUR); PLAYER(SPECIES_VENUSAUR);
OPPONENT(SPECIES_VIVILLON); 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 { GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
PLAYER(SPECIES_FORRETRESS) { Ability(ABILITY_OVERCOAT); } PLAYER(SPECIES_FORRETRESS) { Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_VIVILLON); OPPONENT(SPECIES_VIVILLON);
} WHEN { } WHEN {

View File

@ -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; bool32 hit;
PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; } PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; gen = GEN_5; }
PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = TRUE; } 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 { GIVEN {
ASSUME(B_TOXIC_NEVER_MISS >= GEN_6); WITH_CONFIG(GEN_CONFIG_TOXIC_NEVER_MISS, gen);
ASSUME(GetSpeciesType(SPECIES_NIDORAN_M, 0) == TYPE_POISON); ASSUME(GetSpeciesType(SPECIES_NIDORAN_M, 0) == TYPE_POISON);
PLAYER(species); PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET);

View File

@ -38,11 +38,10 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respe
u8 statusAnim; u8 statusAnim;
u16 species; u16 species;
u32 rng; u32 rng;
#if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; } 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; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = MOVE_EFFECT_POISON; species = SPECIES_ARBOK; }
GIVEN { GIVEN {
WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species); OPPONENT(species);
} WHEN { } WHEN {

View File

@ -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 { GIVEN {
ASSUME(B_PARALYZE_ELECTRIC >= GEN_6); WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, gen);
ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC); ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU); OPPONENT(SPECIES_PIKACHU);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_THUNDER_SHOCK); } TURN { MOVE(player, MOVE_THUNDER_SHOCK, secondaryEffect: TRUE); }
} SCENE { } SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, player);
HP_BAR(opponent); 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); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
STATUS_ICON(opponent, paralysis: TRUE); STATUS_ICON(opponent, paralysis: TRUE);
} }

View File

@ -46,12 +46,11 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
u8 statusAnim; u8 statusAnim;
u16 species; u16 species;
u32 rng; u32 rng;
#if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; } 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_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_ARCANINE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; }
GIVEN { GIVEN {
WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species); OPPONENT(species);
} WHEN { } WHEN {

View File

@ -1,9 +1,13 @@
#include "global.h" #include "global.h"
#include "test/battle.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 { GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen);
ASSUME(IsPowderMove(MOVE_STUN_SPORE)); ASSUME(IsPowderMove(MOVE_STUN_SPORE));
ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS); ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS);
PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_WYNAUT);
@ -11,7 +15,12 @@ SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon")
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_STUN_SPORE); } TURN { MOVE(player, MOVE_STUN_SPORE); }
} SCENE { } SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); if (gen == GEN_6) {
MESSAGE("It doesn't affect the opposing Oddish…"); 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…");
}
} }
} }

View File

@ -1075,6 +1075,7 @@ SINGLE_BATTLE_TEST("Sleep Clause: Sleep clause is deactivated when a sleeping mo
enum Ability ability; enum Ability ability;
PARAMETRIZE { ability = ABILITY_VITAL_SPIRIT; } PARAMETRIZE { ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { ability = ABILITY_INSOMNIA; } PARAMETRIZE { ability = ABILITY_INSOMNIA; }
GIVEN { GIVEN {
FLAG_SET(B_FLAG_SLEEP_CLAUSE); FLAG_SET(B_FLAG_SLEEP_CLAUSE);
ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS); ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS);

View File

@ -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; } PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; }
GIVEN { 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); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); } PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } 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 { GIVEN {
WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, gen);
ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC); ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC);
ASSUME(B_PARALYZE_ELECTRIC >= GEN_6);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU); OPPONENT(SPECIES_PIKACHU);
} WHEN { } WHEN {
TURN { MOVE(player, MOVE_THUNDER_WAVE); } TURN { MOVE(player, MOVE_THUNDER_WAVE); }
} SCENE { } SCENE {
MESSAGE("Wobbuffet used Thunder Wave!"); 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);
}
} }
} }

View File

@ -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 { 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(IsPowderMove(MOVE_SPORE));
ASSUME(B_POWDER_GRASS >= GEN_6);
PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHIKORITA); OPPONENT(SPECIES_CHIKORITA);
} WHEN { } WHEN {

View File

@ -288,7 +288,7 @@ TEST("givemon [moves]")
ZeroPlayerPartyMons(); ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT( 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); EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);