diff --git a/.all-contributorsrc b/.all-contributorsrc
index c4ed2ea558..7d2e4d4bbf 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -348,6 +348,15 @@
"doc",
"code"
]
+ },
+ {
+ "login": "Ddaretrogamer",
+ "name": "Phantonomy",
+ "avatar_url": "https://avatars.githubusercontent.com/u/131238004?v=4",
+ "profile": "https://github.com/Ddaretrogamer",
+ "contributions": [
+ "design"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
index a06bfb5247..56368ef27c 100644
--- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
+++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
@@ -43,9 +43,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using?
options:
- - 1.13.1 (Latest release)
+ - 1.13.2 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
+ - 1.13.1
- 1.13.0
- 1.12.3
- 1.12.2
diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
index 505cd5e6c4..06c55598d6 100644
--- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
+++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
@@ -43,9 +43,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using?
options:
- - 1.13.1 (Latest release)
+ - 1.13.2 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
+ - 1.13.1
- 1.13.0
- 1.12.3
- 1.12.2
diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml
index 4abed5dbb5..56cfecf33a 100644
--- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml
+++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml
@@ -43,9 +43,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using?
options:
- - 1.13.1 (Latest release)
+ - 1.13.2 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
+ - 1.13.1
- 1.13.0
- 1.12.3
- 1.12.2
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index dc900e224e..21785498fb 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -24,6 +24,7 @@
+
diff --git a/CREDITS.md b/CREDITS.md
index 17809a3c3f..4c0b6a3d3e 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -60,6 +60,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 lordraindance2 💻 |
 Pablo Pena 💻 |
 tustin2121 📖 💻 |
+  Phantonomy 🎨 |
diff --git a/INSTALL.md b/INSTALL.md
index d265d588a1..072d43a858 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -14,8 +14,8 @@ After completing the install instructions for your OS, proceed to [Building poke
On Windows, the project can be built using the following systems:
- WSL2, fastest
- WSL1, 7 times slower than WSL2
-- Msys2, 20 times slower than WSL2 (**NOTE**: Currently broken on pret upstream)
-- Cygwin, 30 timer slower than WSL2 (**NOTE**: Currently broken on pret upstream)
+- Msys2, 20 times slower than WSL2
+- Cygwin, 30 timer slower than WSL2
**NOTE**: Only WSL systems are recommended.
diff --git a/README.md b/README.md
index 21de8f622d..8f9088a6e8 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
If you use **`pokeemerald-expansion`**, please credit **RHH (Rom Hacking Hideout)**. Optionally, include the version number for clarity.
```
-Based off RHH's pokeemerald-expansion 1.13.1 https://github.com/rh-hideout/pokeemerald-expansion/
+Based off RHH's pokeemerald-expansion 1.13.2 https://github.com/rh-hideout/pokeemerald-expansion/
```
Please consider [crediting all contributors](CREDITS.md) involved in the project!
diff --git a/asm/macros/event.inc b/asm/macros/event.inc
index 49e0109451..43b9417860 100644
--- a/asm/macros/event.inc
+++ b/asm/macros/event.inc
@@ -986,7 +986,7 @@
@ Gives the player a Pokémon of the specified species and level, and allows to customize extra parameters.
@ VAR_RESULT will be set to MON_GIVEN_TO_PARTY, MON_GIVEN_TO_PC, or MON_CANT_GIVE depending on the outcome.
- .macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, gmaxFactor, teraType, dmaxLevel
+ .macro givemon species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, shinyMode, gmaxFactor, teraType, dmaxLevel
callnative ScrCmd_createmon, requests_effects=1
.byte 0
.byte PARTY_SIZE @ assign to first empty slot
@@ -1014,7 +1014,7 @@
.ifnb \move2; .set givemon_flags, givemon_flags | (1 << 18); .endif
.ifnb \move3; .set givemon_flags, givemon_flags | (1 << 19); .endif
.ifnb \move4; .set givemon_flags, givemon_flags | (1 << 20); .endif
- .ifnb \isShiny; .set givemon_flags, givemon_flags | (1 << 21); .endif
+ .ifnb \shinyMode; .set givemon_flags, givemon_flags | (1 << 21); .endif
.ifnb \gmaxFactor; .set givemon_flags, givemon_flags | (1 << 22); .endif
.ifnb \teraType; .set givemon_flags, givemon_flags | (1 << 23); .endif
.ifnb \dmaxLevel; .set givemon_flags, givemon_flags | (1 << 24); .endif
@@ -1040,7 +1040,7 @@
.ifnb \move2; .2byte \move2; .endif
.ifnb \move3; .2byte \move3; .endif
.ifnb \move4; .2byte \move4; .endif
- .ifnb \isShiny; .2byte \isShiny; .endif
+ .ifnb \shinyMode; .2byte \shinyMode; .endif
.ifnb \gmaxFactor; .2byte \gmaxFactor; .endif
.ifnb \teraType; .2byte \teraType; .endif
.ifnb \dmaxLevel; .2byte \dmaxLevel; .endif
@@ -1048,7 +1048,7 @@
@ creates a mon for a given party and slot
@ otherwise
- .macro createmon side:req, slot:req, species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, isShiny, gmaxFactor, teraType, dmaxLevel
+ .macro createmon side:req, slot:req, species:req, level:req, item, ball, nature, abilityNum, gender, hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv, hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv, move1, move2, move3, move4, shinyMode, gmaxFactor, teraType, dmaxLevel
callnative ScrCmd_createmon, requests_effects=1
.byte \side @ 0 - player, 1 - opponent
.byte \slot @ 0-5
@@ -1076,7 +1076,7 @@
.ifnb \move2; .set givemon_flags, givemon_flags | (1 << 18); .endif
.ifnb \move3; .set givemon_flags, givemon_flags | (1 << 19); .endif
.ifnb \move4; .set givemon_flags, givemon_flags | (1 << 20); .endif
- .ifnb \isShiny; .set givemon_flags, givemon_flags | (1 << 21); .endif
+ .ifnb \shinyMode; .set givemon_flags, givemon_flags | (1 << 21); .endif
.ifnb \gmaxFactor; .set givemon_flags, givemon_flags | (1 << 22); .endif
.ifnb \teraType; .set givemon_flags, givemon_flags | (1 << 23); .endif
.ifnb \dmaxLevel; .set givemon_flags, givemon_flags | (1 << 24); .endif
@@ -1102,7 +1102,7 @@
.ifnb \move2; .2byte \move2; .endif
.ifnb \move3; .2byte \move3; .endif
.ifnb \move4; .2byte \move4; .endif
- .ifnb \isShiny; .2byte \isShiny; .endif
+ .ifnb \shinyMode; .2byte \shinyMode; .endif
.ifnb \gmaxFactor; .2byte \gmaxFactor; .endif
.ifnb \teraType; .2byte \teraType; .endif
.ifnb \dmaxLevel; .2byte \dmaxLevel; .endif
diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s
index a741c372ea..4d952965e6 100644
--- a/data/battle_anim_scripts.s
+++ b/data/battle_anim_scripts.s
@@ -171,7 +171,7 @@ gBattleAnimMove_Brine::
playsewithpan SE_M_DIVE, -64
waitforvisualfinish
delay 16
- createvisualtask AnimTask_WaterSpoutRain, 5
+ createvisualtask AnimTask_BrineRain, 5
playsewithpan SE_M_SURF, +63
clearmonbg ANIM_DEF_PARTNER
blendoff
@@ -366,6 +366,7 @@ gBattleAnimMove_UTurn::
gBattleAnimMove_CloseCombat::
loadspritegfx ANIM_TAG_IMPACT
loadspritegfx ANIM_TAG_HANDS_AND_FEET
+ monbg ANIM_DEF_PARTNER
call SetHighSpeedBg
createsprite gFistFootRandomPosSpriteTemplate, ANIM_TARGET, 3, 1, 10, 0
createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 2, 0, 7, 1
@@ -440,7 +441,7 @@ gBattleAnimMove_CloseCombat::
playsewithpan SE_M_MEGA_KICK2, +63
delay 1
call UnsetHighSpeedBg
- clearmonbg ANIM_TARGET
+ clearmonbg ANIM_DEF_PARTNER
blendoff
delay 1
setarg 7, 0x1000
@@ -969,6 +970,7 @@ gBattleAnimMove_HeartSwap::
loadspritegfx ANIM_TAG_RED_HEART
loadspritegfx ANIM_TAG_PINKVIO_ORB
loadspritegfx ANIM_TAG_SPARKLE_2
+ monbg ANIM_TARGET
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 8, RGB(31, 24, 26)
createvisualtask AnimTask_HeartSwap, 3, ANIM_TARGET
createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_TARGET, RGB_WHITE, 12, 3, 1
@@ -1001,7 +1003,6 @@ gBattleAnimMove_HeartSwap::
createsprite gRedHeartCharmSpriteTemplate, ANIM_ATTACKER, 3, 20, 20
playsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER
waitforvisualfinish
- clearmonbg ANIM_ATTACKER
clearmonbg ANIM_TARGET
blendoff
end
@@ -1607,7 +1608,6 @@ gBattleAnimMove_DragonRush::
createsprite gRockFragmentSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, -30, -18, 8, 2
createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 3, 7, 1
waitforvisualfinish
- clearmonbg ANIM_DEF_PARTNER
blendoff
end
@@ -2310,7 +2310,6 @@ gBattleAnimMove_MirrorShot::
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 10, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
waitforvisualfinish
- clearmonbg ANIM_ATTACKER
blendoff
end
@@ -10990,7 +10989,6 @@ gBattleAnimMove_SmartStrike::
setalpha 12, 8
call SonicBoomProjectile
createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 3, 0, 10, 1
- loadspritegfx ANIM_TAG_FLASH_CANNON_BALL
createsprite gSmartStrikeImpactTemplate, ANIM_TARGET, 4, 0x0, 0x0, 0x8, 0x1, 0x0
playsewithpan SE_M_VITAL_THROW2, SOUND_PAN_TARGET
createsprite gSmartStrikeGemTemplate, ANIM_TARGET, 2, 0x1, 0x1, 0x0, 0xffe8, 0xa
@@ -11005,7 +11003,6 @@ gBattleAnimMove_SmartStrike::
clearmonbg ANIM_DEF_PARTNER
blendoff
waitforvisualfinish
- clearmonbg ANIM_ATTACKER
blendoff
waitforvisualfinish
end
diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s
index 4248807b01..f1eeeccdc9 100644
--- a/data/battle_scripts_1.s
+++ b/data/battle_scripts_1.s
@@ -6843,7 +6843,7 @@ BattleScript_PowderMoveNoEffectWaitMsg:
BattleScript_MoveUsedFlinched::
printstring STRINGID_PKMNFLINCHED
waitmessage B_WAIT_TIME_LONG
- waitmessage B_WAIT_TIME_LONG
+ jumpifability BS_ATTACKER, ABILITY_STEADFAST, BattleScript_TryActivateSteadFast
BattleScript_MoveUsedFlinchedEnd:
goto BattleScript_MoveEnd
BattleScript_TryActivateSteadFast:
@@ -6853,7 +6853,7 @@ BattleScript_TryActivateSteadFast:
copybyte gBattlerAbility, gBattlerAttacker
call BattleScript_AbilityPopUp
statbuffchange BS_ATTACKER, STAT_CHANGE_ALLOW_PTR, BattleScript_MoveUsedFlinchedEnd
- setbyte gBattleCommunication STAT_SPEED
+ setbyte gBattleCommunication, STAT_SPEED
stattextbuffer
printstring STRINGID_ATTACKERABILITYSTATRAISE
waitmessage B_WAIT_TIME_LONG
@@ -8285,6 +8285,13 @@ BattleScript_AbilityCuredStatus::
updatestatusicon BS_SCRIPTING
return
+BattleScript_AbilityCuredStatusEnd3::
+ call BattleScript_AbilityPopUp
+ printstring STRINGID_PKMNSXCUREDITSYPROBLEM
+ waitmessage B_WAIT_TIME_LONG
+ updatestatusicon BS_SCRIPTING
+ end3
+
BattleScript_BattlerShookOffTaunt::
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNSHOOKOFFTHETAUNT
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index 8fd400844d..c1e6a78752 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -20,6 +20,7 @@
- [Day/Night System FAQ](tutorials/dns.md)
- [Changelog](./CHANGELOG.md)
- [1.13.x]()
+ - [Version 1.13.2](changelogs/1.13.x/1.13.2.md)
- [Version 1.13.1](changelogs/1.13.x/1.13.1.md)
- [Version 1.13.0](changelogs/1.13.x/1.13.0.md)
- [1.12.x]()
diff --git a/docs/changelogs/1.13.x/1.13.2.md b/docs/changelogs/1.13.x/1.13.2.md
new file mode 100644
index 0000000000..6cd2249378
--- /dev/null
+++ b/docs/changelogs/1.13.x/1.13.2.md
@@ -0,0 +1,123 @@
+```md
+## How to update
+- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
+- Once you have your remote set up, run the command `git pull RHH expansion/1.13.2
+`.
+```
+
+
+## 🧬 General 🧬
+### Changed
+* Removed superflous palette compression check by @hedara90 in [#7718](https://github.com/rh-hideout/pokeemerald-expansion/pull/7718)
+* update of sv.json to consider newest DLC changes by @wiz1989 in [#7672](https://github.com/rh-hideout/pokeemerald-expansion/pull/7672)
+* Adjusted line break substring breaking by @hedara90 in [#7789](https://github.com/rh-hideout/pokeemerald-expansion/pull/7789)
+* Fixup add-new-trainer-front-pic tutorial. by @GraionDilach in [#7802](https://github.com/rh-hideout/pokeemerald-expansion/pull/7802)
+* Adds conversion script for trainers.h by @AlexOn1ine in [#7663](https://github.com/rh-hideout/pokeemerald-expansion/pull/7663)
+
+### Fixed
+* Fix bug with IF_GENDER evolution condition by @FosterProgramming in [#7749](https://github.com/rh-hideout/pokeemerald-expansion/pull/7749)
+* Pokemon storage moving items bugfix by @FosterProgramming in [#7763](https://github.com/rh-hideout/pokeemerald-expansion/pull/7763)
+* Fix catch bug introduced in #7774 by @FosterProgramming in [#7782](https://github.com/rh-hideout/pokeemerald-expansion/pull/7782)
+* Fix Party Menu move select name width by @AsparagusEduardo in [#7820](https://github.com/rh-hideout/pokeemerald-expansion/pull/7820)
+* Fix Debug Give Pokémon (Complex) with duplicate moves by @AsparagusEduardo in [#7821](https://github.com/rh-hideout/pokeemerald-expansion/pull/7821)
+
+## 🗺️ Overworld 🗺️
+### Changed
+* Added missing `LOCALID_NONE` by @estellarc in [#7783](https://github.com/rh-hideout/pokeemerald-expansion/pull/7783)
+
+### Fixed
+* Fix TRAINER_TYPE_SEE_ALL_DIRECTIONS by @DizzyEggg in [#7779](https://github.com/rh-hideout/pokeemerald-expansion/pull/7779)
+
+## 🐉 Pokémon 🐉
+### Fixed
+* fix: seen flags for first mon in enemy party by @khbsd in [#7791](https://github.com/rh-hideout/pokeemerald-expansion/pull/7791)
+
+## ⚔️ Battle General ⚔️
+### Fixed
+* Fix most failed tests with `GEN_LATEST` = `GEN_7` by @AsparagusEduardo in [#7688](https://github.com/rh-hideout/pokeemerald-expansion/pull/7688)
+* Fixes Sweet Veil not protecting sleep from Yawn status by @AlexOn1ine in [#7704](https://github.com/rh-hideout/pokeemerald-expansion/pull/7704)
+* Fix incorrect wrap turn amount by @AlexOn1ine in [#7667](https://github.com/rh-hideout/pokeemerald-expansion/pull/7667)
+* Fixes Rage Fist for gen7 Disguise by @AlexOn1ine in [#7692](https://github.com/rh-hideout/pokeemerald-expansion/pull/7692)
+* Fixes Intrepid Sword/Dauntless Shield boosting after entering while at max stats by @PhallenTree in [#7716](https://github.com/rh-hideout/pokeemerald-expansion/pull/7716)
+* Fixes incorrect ending for some scripts by @AlexOn1ine in [#7691](https://github.com/rh-hideout/pokeemerald-expansion/pull/7691)
+* Fixes Uproar not waking up mons by @AlexOn1ine in [#7714](https://github.com/rh-hideout/pokeemerald-expansion/pull/7714)
+* Fixes Endure and Eject Pack issues by @AlexOn1ine in [#7687](https://github.com/rh-hideout/pokeemerald-expansion/pull/7687)
+* Fix Beak Blast burning fire types by @hedara90 in [#7740](https://github.com/rh-hideout/pokeemerald-expansion/pull/7740)
+* Fixes Recharge not actually being removed when recharge turn occurs by @PhallenTree in [#7744](https://github.com/rh-hideout/pokeemerald-expansion/pull/7744)
+* Bugfixes Batch by @AlexOn1ine in [#7739](https://github.com/rh-hideout/pokeemerald-expansion/pull/7739)
+* Fixes Beat Up incorrect slots usage by @AlexOn1ine in [#7741](https://github.com/rh-hideout/pokeemerald-expansion/pull/7741)
+* Fixes Mycelium Might and Lagging Tail adjusting prio incorrectly by @AlexOn1ine in [#7742](https://github.com/rh-hideout/pokeemerald-expansion/pull/7742)
+* Wrong argument passed down by @AlexOn1ine in [#7751](https://github.com/rh-hideout/pokeemerald-expansion/pull/7751)
+* Fixed Ball Fetch Ability by @bassforte123 in [#7764](https://github.com/rh-hideout/pokeemerald-expansion/pull/7764)
+* Fixes Flower Shield affecting semi-invulnerable mons by @AlexOn1ine in [#7766](https://github.com/rh-hideout/pokeemerald-expansion/pull/7766)
+* Fixes Helping Hand boosts not stacking with each other by @PhallenTree in [#7775](https://github.com/rh-hideout/pokeemerald-expansion/pull/7775)
+* Fixes OHKO moves calculating accuracy twice by @AlexOn1ine in [#7785](https://github.com/rh-hideout/pokeemerald-expansion/pull/7785)
+* Fixes Instructed moves looking at the wrong turn order number by @PhallenTree in [#7788](https://github.com/rh-hideout/pokeemerald-expansion/pull/7788)
+* Fix Flame Burst timeout if primary target is fainted by @hedara90 in [#7793](https://github.com/rh-hideout/pokeemerald-expansion/pull/7793)
+* Fixes Leppa Berry timings by @AlexOn1ine in [#7787](https://github.com/rh-hideout/pokeemerald-expansion/pull/7787)
+* Fixes Effects activating when move wasn't successful by @AlexOn1ine in [#7803](https://github.com/rh-hideout/pokeemerald-expansion/pull/7803)
+* Fixes Throat Spray being blocked by Sheer Force by @AlexOn1ine in [#7808](https://github.com/rh-hideout/pokeemerald-expansion/pull/7808)
+* Fixes inaccurate save / restore in Fling script by @AlexOn1ine in [#7811](https://github.com/rh-hideout/pokeemerald-expansion/pull/7811)
+* Fix test exit prints for stored battlers by @AlexOn1ine in [#7807](https://github.com/rh-hideout/pokeemerald-expansion/pull/7807)
+* Fixes EndTurn Eject Pack by @AlexOn1ine in [#7813](https://github.com/rh-hideout/pokeemerald-expansion/pull/7813)
+* Fix Battle Frontier using Strange Balls by @AsparagusEduardo in [#7823](https://github.com/rh-hideout/pokeemerald-expansion/pull/7823)
+* Fix Throat Spray activating multiply times by @AlexOn1ine in [#7818](https://github.com/rh-hideout/pokeemerald-expansion/pull/7818)
+* fix (choice lock): Gorilla Tactics interactions with choice item removal by @ghostyboyy97 in [#7824](https://github.com/rh-hideout/pokeemerald-expansion/pull/7824)
+ - Fixed interactions with choice items and Gorilla Tactics both present when choice item is removed by a thief effect or item swap effect.
+* Fixes encore random target for gen5+ by @AlexOn1ine in [#7800](https://github.com/rh-hideout/pokeemerald-expansion/pull/7800)
+
+## 🤹 Moves 🤹
+### Changed
+* Initial Lash Out tests by @grintoul1 in [#7769](https://github.com/rh-hideout/pokeemerald-expansion/pull/7769)
+
+### Fixed
+* Fix Salt Cure in double battles by @Bassoonian in [#7797](https://github.com/rh-hideout/pokeemerald-expansion/pull/7797)
+
+## 🎭 Abilities 🎭
+### Fixed
+* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748)
+* Fix Forecast and Flower Gift corruption by @Bassoonian in [#7796](https://github.com/rh-hideout/pokeemerald-expansion/pull/7796)
+* Immunity abilities trigger on turn 0 (leads) by @spindrift64 in [#7814](https://github.com/rh-hideout/pokeemerald-expansion/pull/7814)
+
+## 🤖 Battle AI 🤖
+### Changed
+* Tidy up CanTargetFaintAiWithMod and CanTargetMoveFaintAi by @grintoul1 in [#7693](https://github.com/rh-hideout/pokeemerald-expansion/pull/7693)
+* Doubles AI: Trick Room timer fix and test for DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE by @grintoul1 in [#7622](https://github.com/rh-hideout/pokeemerald-expansion/pull/7622)
+
+### Fixed
+* Toxic thread uses light screen's scoring.... by @surskitty in [#7674](https://github.com/rh-hideout/pokeemerald-expansion/pull/7674)
+* Fix most failed and assume fail tests with `GEN_LATEST` = `GEN_6` by @AsparagusEduardo in [#7696](https://github.com/rh-hideout/pokeemerald-expansion/pull/7696)
+* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748)
+
+## 🧹 Other Cleanup 🧹
+* Tidy up CanTargetFaintAiWithMod and CanTargetMoveFaintAi by @grintoul1 in [#7693](https://github.com/rh-hideout/pokeemerald-expansion/pull/7693)
+* Removed superflous palette compression check by @hedara90 in [#7718](https://github.com/rh-hideout/pokeemerald-expansion/pull/7718)
+* Fix failing test for B_PREFERRED_ICE_WEATHER = B_ICE_WEATHER_SNOW by @phexmiau in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755)
+* Adjusted line break substring breaking by @hedara90 in [#7789](https://github.com/rh-hideout/pokeemerald-expansion/pull/7789)
+* Added missing `LOCALID_NONE` by @estellarc in [#7783](https://github.com/rh-hideout/pokeemerald-expansion/pull/7783)
+
+## 🧪 Test Runner 🧪
+### Changed
+* Add tests for Filter, Solid Rock and Prism Armor by @hedara90 in [#7734](https://github.com/rh-hideout/pokeemerald-expansion/pull/7734)
+* Fix failing test for B_PREFERRED_ICE_WEATHER = B_ICE_WEATHER_SNOW by @phexmiau in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755)
+* Initial Lash Out tests by @grintoul1 in [#7769](https://github.com/rh-hideout/pokeemerald-expansion/pull/7769)
+* Improve how test involving ball throw work by @FosterProgramming in [#7774](https://github.com/rh-hideout/pokeemerald-expansion/pull/7774)
+
+### Fixed
+* Fix EWRAM_INIT in tests and add a default state to test runner main loop by @hedara90 in [#7699](https://github.com/rh-hideout/pokeemerald-expansion/pull/7699)
+* Fix most failed and assume fail tests with `GEN_LATEST` = `GEN_6` by @AsparagusEduardo in [#7696](https://github.com/rh-hideout/pokeemerald-expansion/pull/7696)
+* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748)
+* Fix Big Root tests by @hedara90 in [#7817](https://github.com/rh-hideout/pokeemerald-expansion/pull/7817)
+
+## 📚 Documentation 📚
+* Fixup add-new-trainer-front-pic tutorial. by @GraionDilach in [#7802](https://github.com/rh-hideout/pokeemerald-expansion/pull/7802)
+
+## New Contributors
+* @phexmiau made their first contribution in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755)
+* @ghostyboyy97 made their first contribution in [#7824](https://github.com/rh-hideout/pokeemerald-expansion/pull/7824)
+
+**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.13.1...expansion/1.13.2
+
+
+
+
diff --git a/graphics/pokemon/pikachu/starter/icon.png b/graphics/pokemon/pikachu/starter/icon.png
index 737fababd7..891f0d1b3e 100644
Binary files a/graphics/pokemon/pikachu/starter/icon.png and b/graphics/pokemon/pikachu/starter/icon.png differ
diff --git a/include/battle_scripts.h b/include/battle_scripts.h
index d9bb476af7..16a07e971a 100644
--- a/include/battle_scripts.h
+++ b/include/battle_scripts.h
@@ -202,6 +202,7 @@ extern const u8 BattleScript_AbilityStatusEffect[];
extern const u8 BattleScript_SynchronizeActivates[];
extern const u8 BattleScript_NoItemSteal[];
extern const u8 BattleScript_AbilityCuredStatus[];
+extern const u8 BattleScript_AbilityCuredStatusEnd3[];
extern const u8 BattleScript_IgnoresWhileAsleep[];
extern const u8 BattleScript_IgnoresAndUsesRandomMove[];
extern const u8 BattleScript_MoveUsedLoafingAround[];
diff --git a/include/battle_util.h b/include/battle_util.h
index 0c486f4a3a..f22fd8b0ac 100644
--- a/include/battle_util.h
+++ b/include/battle_util.h
@@ -55,6 +55,7 @@ enum {
ABILITYEFFECT_SWITCH_IN_WEATHER,
ABILITYEFFECT_OPPORTUNIST,
ABILITYEFFECT_SWITCH_IN_STATUSES,
+ ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES,
};
// For the first argument of ItemBattleEffects, to deteremine which block of item effects to try
@@ -307,6 +308,7 @@ struct Pokemon *GetIllusionMonPtr(u32 battler);
void ClearIllusionMon(u32 battler);
u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler);
bool32 SetIllusionMon(struct Pokemon *mon, u32 battler);
+u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID);
bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler);
enum DamageCategory GetBattleMoveCategory(u32 move);
void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move);
@@ -388,6 +390,7 @@ bool32 IsPursuitTargetSet(void);
void ClearPursuitValuesIfSet(u32 battler);
void ClearPursuitValues(void);
bool32 HasWeatherEffect(void);
+bool32 IsAnyTargetAffected(u32 battlerAtk);
u32 RestoreWhiteHerbStats(u32 battler);
bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 HadMoreThanHalfHpNowDoesnt(u32 battler);
diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h
index 2b4d590286..94d2a2bd48 100644
--- a/include/constants/battle_script_commands.h
+++ b/include/constants/battle_script_commands.h
@@ -153,6 +153,7 @@ enum MoveEndEffects
MOVEEND_MULTIHIT_MOVE,
MOVEEND_MOVE_BLOCK,
MOVEEND_ITEM_EFFECTS_ATTACKER,
+ MOVEEND_ITEM_THROAT_SPRAY,
MOVEEND_ABILITY_BLOCK,
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist
MOVEEND_RED_CARD,
diff --git a/include/constants/expansion.h b/include/constants/expansion.h
index 02234f603c..92973512b2 100644
--- a/include/constants/expansion.h
+++ b/include/constants/expansion.h
@@ -1,10 +1,10 @@
#ifndef GUARD_CONSTANTS_EXPANSION_H
#define GUARD_CONSTANTS_EXPANSION_H
-// Last version: 1.13.1
+// Last version: 1.13.2
#define EXPANSION_VERSION_MAJOR 1
#define EXPANSION_VERSION_MINOR 13
-#define EXPANSION_VERSION_PATCH 2
+#define EXPANSION_VERSION_PATCH 3
// FALSE if this this version of Expansion is not a tagged commit, i.e.
// it contains unreleased changes.
diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h
index b365a6ea8f..7a9aed85c3 100644
--- a/include/constants/generational_changes.h
+++ b/include/constants/generational_changes.h
@@ -41,6 +41,10 @@ enum GenConfigTag
GEN_CONFIG_PRANKSTER_DARK_TYPES,
GEN_CONFIG_DESTINY_BOND_FAIL,
GEN_CONFIG_POWDER_RAIN,
+ GEN_CONFIG_POWDER_GRASS,
+ GEN_CONFIG_OBLIVIOUS_TAUNT,
+ GEN_CONFIG_TOXIC_NEVER_MISS,
+ GEN_CONFIG_PARALYZE_ELECTRIC,
GEN_CONFIG_COUNT
};
diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h
index 20cff8a30f..b2e6b23ead 100644
--- a/include/constants/pokemon.h
+++ b/include/constants/pokemon.h
@@ -347,6 +347,12 @@ enum EvoSpinDirections {
SPIN_EITHER, // Player spins either clockwise or counter-clockwise
};
+enum ShinyMode {
+ SHINY_MODE_ALWAYS,
+ SHINY_MODE_RANDOM,
+ SHINY_MODE_NEVER
+};
+
#define MON_PIC_WIDTH 64
#define MON_PIC_HEIGHT 64
#define MON_PIC_SIZE (MON_PIC_WIDTH * MON_PIC_HEIGHT / 2)
diff --git a/include/generational_changes.h b/include/generational_changes.h
index a45325caab..30739a14f5 100644
--- a/include/generational_changes.h
+++ b/include/generational_changes.h
@@ -44,6 +44,10 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_PRANKSTER_DARK_TYPES] = B_PRANKSTER_DARK_TYPES,
[GEN_CONFIG_DESTINY_BOND_FAIL] = B_DESTINY_BOND_FAIL,
[GEN_CONFIG_POWDER_RAIN] = B_POWDER_RAIN,
+ [GEN_CONFIG_POWDER_GRASS] = B_POWDER_GRASS,
+ [GEN_CONFIG_OBLIVIOUS_TAUNT] = B_OBLIVIOUS_TAUNT,
+ [GEN_CONFIG_TOXIC_NEVER_MISS] = B_TOXIC_NEVER_MISS,
+ [GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC,
};
#if TESTING
diff --git a/include/pokeball.h b/include/pokeball.h
index 530e168f93..51949206e2 100644
--- a/include/pokeball.h
+++ b/include/pokeball.h
@@ -45,6 +45,7 @@ enum {
extern const struct CompressedSpriteSheet gBallSpriteSheets[];
extern const struct SpritePalette gBallSpritePalettes[];
extern const struct SpriteTemplate gBallSpriteTemplates[];
+extern const u16 gBallItemIds[];
#define POKEBALL_PLAYER_SENDOUT 0xFF
#define POKEBALL_OPPONENT_SENDOUT 0xFE
diff --git a/include/pokedex.h b/include/pokedex.h
index 0dfc802312..c558cd1b07 100644
--- a/include/pokedex.h
+++ b/include/pokedex.h
@@ -8,6 +8,7 @@ void ResetPokedex(void);
u16 GetNationalPokedexCount(u8 caseID);
u16 GetHoennPokedexCount(u8 caseID);
u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality);
+u32 Pokedex_CreateCaughtMonSprite(u32 species, s32 x, s32 y);
s8 GetSetPokedexFlag(enum NationalDexOrder nationalDexNo, u8 caseID);
void DrawFootprint(u8 windowId, u16 species);
u16 CreateMonSpriteFromNationalDexNumber(enum NationalDexOrder nationalNum, s16 x, s16 y, u16 paletteSlot);
diff --git a/include/pokemon.h b/include/pokemon.h
index 1e31c425b6..f58ebbba1a 100644
--- a/include/pokemon.h
+++ b/include/pokemon.h
@@ -631,6 +631,13 @@ struct FormChange
u16 param3;
};
+enum FusionExtraMoveHandling
+{
+ FORGET_EXTRA_MOVES,
+ SWAP_EXTRA_MOVES_KYUREM_WHITE,
+ SWAP_EXTRA_MOVES_KYUREM_BLACK
+};
+
struct Fusion
{
u16 fusionStorageIndex;
@@ -639,11 +646,22 @@ struct Fusion
u16 targetSpecies2;
u16 fusingIntoMon;
u16 fusionMove;
- u16 unfuseForgetMove;
+ enum FusionExtraMoveHandling extraMoveHandling;
};
extern const struct Fusion *const gFusionTablePointers[NUM_SPECIES];
+#if P_FUSION_FORMS
+#if P_FAMILY_KYUREM
+#if P_FAMILY_RESHIRAM
+extern const u16 gKyurenWhiteSwapMoveTable[][2];
+#endif //P_FAMILY_RESHIRAM
+#if P_FAMILY_ZEKROM
+extern const u16 gKyurenBlackSwapMoveTable[][2];
+#endif //P_FAMILY_ZEKROM
+#endif //P_FAMILY_KYUREM
+#endif //P_FUSION_FORMS
+
#define NUM_UNOWN_FORMS 28
#define GET_UNOWN_LETTER(personality) (( \
diff --git a/migration_scripts/1.13/convert_trainers.py b/migration_scripts/1.13/convert_trainers.py
new file mode 100644
index 0000000000..ba48f5465e
--- /dev/null
+++ b/migration_scripts/1.13/convert_trainers.py
@@ -0,0 +1,401 @@
+import re
+import sys
+import os
+
+is_blank = re.compile(r'^[ \t]*(//.*)?$')
+
+begin_party_definition = re.compile(r'struct TrainerMon (\w+)\[\] =')
+end_party_definition = re.compile(r'^ },')
+begin_pokemon_definition = re.compile(r'^ { *$')
+end_pokemon_definition = re.compile(r'^ },? *$')
+level_definition = re.compile(r'\.lvl = (\d+)')
+species_definition = re.compile(r'\.species = SPECIES_(\w+)')
+gender_definition = re.compile(r'\.gender = TRAINER_MON_(\w+)')
+nickname_definition = re.compile(r'\.nickname = COMPOUND_STRING\("([^"]+)"\)')
+item_definition = re.compile(r'\.heldItem = ITEM_(\w+)')
+ball_definition = re.compile(r'\.ball = ITEM_(\w+)')
+ability_definition = re.compile(r'\.ability = ABILITY_(\w+)')
+friendship_definition = re.compile(r'\.friendship = (\d+)')
+shiny_definition = re.compile(r'\.isShiny = (\w+)')
+ivs_definition = re.compile(r'\.iv = TRAINER_PARTY_IVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)')
+evs_definition = re.compile(r'\.ev = TRAINER_PARTY_EVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)')
+moves_definition = re.compile(r'\.moves = \{([^}]+)\}')
+move_definition = re.compile(r'MOVE_(\w+)')
+nature_definition = re.compile(r'NATURE_(\w+)')
+
+is_trainer_skip = re.compile(r'(const struct Trainer gTrainers\[\] = \{)|(^ \{$)|(\.partySize =)|(\.party = NULL)|(\.mugshotEnabled = TRUE)|(\};)')
+
+trainer_normal_definition = re.compile(r' \[DIFFICULTY_NORMAL\]\[(TRAINER_\w+)\] =')
+trainer_easy_definition = re.compile(r' \[DIFFICULTY_EASY\]\[(TRAINER_\w+)\] =')
+trainer_hard_definition = re.compile(r' \[DIFFICULTY_HARD\]\[(TRAINER_\w+)\] =')
+end_pokemon_definition = re.compile(r' },')
+trainer_class_definition = re.compile(r'\.trainerClass = TRAINER_CLASS_(\w+)')
+encounter_music_gender_definition = re.compile(r'\.encounterMusic_gender = (F_TRAINER_FEMALE \| )?TRAINER_ENCOUNTER_MUSIC_(\w+)')
+encounter_music_definition = re.compile(r'TRAINER_ENCOUNTER_MUSIC_(\w+)')
+trainer_pic_definition = re.compile(r'\.trainerPic = TRAINER_PIC_(\w+)')
+trainer_name_definition = re.compile(r'\.trainerName = _\("([^"]*)"\)')
+trainer_items_definition = re.compile(r'\.items = \{([^}]*)\}')
+trainer_item_definition = re.compile(r'ITEM_(\w+)')
+trainer_double_battle_definition = re.compile(r'\.battleType = (\w+)')
+trainer_ai_flags_definition = re.compile(r'\.aiFlags = (.*)')
+trainer_ai_flag_definition = re.compile(r'AI_FLAG_(\w+)')
+trainer_party_definition = re.compile(r'\.party = ')
+trainer_mugshot_definition = re.compile(r'\.mugshotColor = MUGSHOT_COLOR_(\w+)')
+trainer_starting_status_definition = re.compile(r'\.startingStatus = STARTING_STATUS_(\w+)')
+
+# NOTE: These are just for aesthetics, the Pokemon would still compile
+# without them.
+species_replacements = {
+ "CHIEN_PAO": "Chien-Pao",
+ "CHI_YU": "Chi-Yu",
+ "HAKAMO_O": "Hakamo-o",
+ "HO_OH": "Ho-Oh",
+ "JANGMO_O": "Jangmo-o",
+ "KOMMO_O": "Kommo-o",
+ "PORYGON_Z": "Porygon-Z",
+ "ROTOM_": "Rotom-",
+ "TING_LU": "Ting-Lu",
+ "TYPE_NULL": "Type: Null",
+ "WO_CHIEN": "Wo-Chien",
+
+ "_ALOLAN": "-Alola",
+ "_AQUA_BREED": "-Aqua",
+ "_BATTLE_BOND": "-Bond",
+ "_BLAZE_BREED": "-Blaze",
+ "_CAP": "",
+ "_CLOAK": "",
+ "_COMBAT_BREED": "-Combat",
+ "_CROWED_SHIELD": "-Crowned",
+ "_CROWED_SWORD": "-Crowned",
+ "_DRIVE": "",
+ "_EAST_SEA": "-East",
+ "_FAMILY_OF_FOUR": "-Four",
+ "_FEMALE": "-F",
+ "_FLOWER": "",
+ "_GALARIAN": "-Galar",
+ "_GIGANTAMAX": "-Gmax",
+ "_HISUIAN": "-Hisui",
+ "_ICE_RIDER": "-Ice",
+ "_NOICE_FACE": "-Noice",
+ "_ORIGIN": "-Origin",
+ "_ORIGINAL_COLOR": "-Original",
+ "_PALDEAN": "-Paldea",
+ "_PLUMAGE": "",
+ "_POKE_BALL": "-Pokeball",
+ "_SHADOW_RIDER": "-Shadow",
+ "_STRIKE_STYLE": "-Style",
+ "_TOTEM": "-Totem",
+ "_ZEN_MODE": "-Zen",
+}
+
+class_fixups = {
+ "Rs": "RS",
+}
+
+pic_fixups = {
+ "Rs": "RS",
+}
+
+pokemon_attribute_order = ['Level', 'Ability', 'IVs', 'EVs', 'Happiness', 'Shiny', 'Ball']
+
+class Pokemon:
+ def __init__(self):
+ self.nickname = None
+ self.species = None
+ self.gender = None
+ self.item = None
+ self.nature = None
+ self.attributes = {}
+ self.attributes['IVs'] = "0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe"
+ self.moves = []
+
+
+class Trainer:
+ def __init__(self, id_):
+ self.id = id_
+ self.class_ = None
+ self.encounter_music = None
+ self.gender = None
+ self.pic = None
+ self.name = None
+ self.items = []
+ self.double_battle = None
+ self.ai_flags = None
+ self.mugshot = None
+ self.starting_status = None
+ self.party = None
+ self.difficulty = None
+
+
+def write_tutorial(output):
+ output.write('/*\n')
+ output.write('Trainers and their parties defined with Competetive Syntax.\n')
+ output.write('Compatible with Pokemon Showdown exports.\n')
+ output.write('https://github.com/smogon/pokemon-showdown/blob/master/sim/TEAMS.md\n')
+ output.write('\n')
+ output.write('A trainer specification starts with "=== TRAINER_XXXX ==="\n')
+ output.write('and includes everything until the next line that starts with "==="\n')
+ output.write('or the file ends.\n')
+ output.write('\n')
+ output.write('A blank line is required between the trainer and their Pokemon\n')
+ output.write('and between their Pokemon.\n')
+ output.write('TRAINER_XXXX is how the trainer is referred to within code.\n')
+ output.write('Fields with description and/or example of usage\n')
+ output.write('Required fields for trainers:\n')
+ output.write(' - Name\n')
+ output.write(' - Pic\n')
+ output.write('Optional (but still recommended) fields for trainers:\n')
+ output.write(' - Class (if not specified, PkMn Trainer will be used)\n')
+ output.write(' - Gender (Male/Female, affects random gender weights of party if not specified)\n')
+ output.write(' - Music\n')
+ output.write(' - Items (Some Item / Another Item / Third Item)\n')
+ output.write(' (Can also be specified with ITEM_SOME_ITEM)\n')
+ output.write(' - Battle Type (Singles / Doubles, defaults to Singles)\n')
+ output.write(' - AI (Ai Flag / Another Flag / Third Flag / ...\n')
+ output.write(' see "constants/battle_ai.h" for all flags)\n')
+ output.write(' - Mugshot (enable Mugshots during battle transition\n')
+ output.write(' set to one of Purple, Green, Pink, Blue or Yellow)\n')
+ output.write(' - Starting Status (see include/constants/battle.h for values)\n')
+ output.write('\n')
+ output.write('Pokemon are then specified using the Showdown Export format.\n')
+ output.write("If a field is not specified, it will use it's default value.\n")
+ output.write("\n")
+ output.write('Required fields for Pokemon:\n')
+ output.write(' - Species (Either as SPECIES_ABRA or Abra)\n')
+ output.write(' This line also specifies Gender, Nickname and Held item.\n')
+ output.write(' Alfred (Abra) (M) @ Eviolite\n')
+ output.write(' Roberta (SPECIES_ABRA) (F) @ ITEM_CHOICE_SPECS\n')
+ output.write(' Both lines are valid. Gender (M) or (F) must use a capital letter.\n')
+ output.write(' Nickname length is limited to 10 characters using standard letters.\n')
+ output.write(" With narrow font it's increased to 12. Longer strings will be silently shortened.\n")
+ output.write('\n')
+ output.write('Optional fields for Pokemon:\n')
+ output.write(' - Level (Number between 1 and 100, defaults to 100)\n')
+ output.write(' - Ability (Ability Name or ABILITY_ABILITY_NAME)\n')
+ output.write(' - IVs (0 HP / 1 Atk / 2 Def / 3 SpA / 4 SpD / 5 Spe, defaults to all 31)\n')
+ output.write(' (Order does not matter)\n')
+ output.write(' - EVs (252 HP / 128 Spe / 48 Def, defaults to all 0, is not capped at 512 total)\n')
+ output.write(' (Order does not matter)\n')
+ output.write(' - Ball (Poke Ball or ITEM_POKE_BALL, defaults to Poke Ball)\n')
+ output.write(' - Happiness (Number between 1 and 255)\n')
+ output.write(' - Nature (Rash or NATURE_RASH, defaults to Hardy)\n')
+ output.write(' - Shiny (Yes/No, defaults to No)\n')
+ output.write(' - Dynamax Level (Number between 0 and 10, default 10, also sets "shouldDynamax" to True)\n')
+ output.write(' - Gigantamax (Yes/No, sets to Gigantamax factor)\n')
+ output.write(' (doesn\'t do anything to Pokemon without a Gigantamax form, also sets "shouldDynamax" to True)\n')
+ output.write(' - Tera Type (Set to a Type, either Fire or TYPE_FIRE, also sets "shouldTerastal" to True)\n')
+ output.write('Moves are defined with a - (dash) followed by a single space, then the move name.\n')
+ output.write('Either "- Tackle" or "- MOVE_TACKLE" works. One move per line.\n')
+ output.write('Moves have to be the last lines of a Pokemon.\n')
+ output.write('If no moves are specified, the Pokemon will use the last 4 moves it learns\n')
+ output.write('through levelup at its level.\n')
+ output.write("\n")
+ output.write('Default IVs and Level can be changed in the "main" function of tools/trainerproc/main.c\n')
+ output.write("\n")
+ output.write('This file is processed with a custom preprocessor.\n')
+ output.write('*/\n')
+ output.write("\n")
+ output.write('/*\n')
+ output.write('Comments can be added as C comment blocks\n')
+ output.write('// cannot be used as comments\n')
+ output.write('*/\n')
+ output.write("\n")
+ output.write('/*Comments can also be on a single line*/\n')
+ output.write("\n")
+ output.write("\n")
+
+
+
+def write_to_file(trainer, output):
+ output.write(f'=== {trainer.id} ===\n')
+ output.write(f'Name: {trainer.name}\n')
+ output.write(f'Class: {trainer.class_}\n')
+ output.write(f'Pic: {trainer.pic}\n')
+ output.write(f'Gender: {trainer.gender}\n')
+ output.write(f'Music: {trainer.encounter_music}\n')
+ if len(trainer.items) > 0:
+ output.write(f'Items: {trainer.items}\n')
+ output.write(f'Battle Type: {trainer.double_battle}\n')
+ if trainer.ai_flags is not None:
+ output.write(f'AI: {trainer.ai_flags}\n')
+ if trainer.mugshot:
+ output.write(f'Mugshot: {trainer.mugshot}\n')
+ if trainer.difficulty is not None:
+ output.write(f'Difficulty: {trainer.difficulty}\n')
+
+ output.write(f'\n')
+
+ for pokemon in trainer.party:
+ if pokemon.species is None:
+ continue
+ if pokemon.item is not None:
+ output.write(f'{pokemon.species} @ {pokemon.item}\n')
+ else:
+ output.write(f'{pokemon.species}\n')
+ # for key in pokemon.attributes:
+ for key in pokemon_attribute_order:
+ if key in pokemon.attributes:
+ output.write(f'{key}: {pokemon.attributes[key]}\n')
+ if pokemon.nature:
+ output.write(f'Nature: {pokemon.nature}\n')
+ for move in pokemon.moves:
+ output.write(f'- {move}\n')
+ output.write(f'\n')
+
+
+def parse_trainers(content, output):
+ newlines = 0
+ trainer = None
+ pokemon = None
+ party = []
+ moves = []
+
+ write_tutorial(output)
+
+ for line_no, line in enumerate(content, 1):
+ try:
+ line = line[:-1]
+
+ # Trainer defition
+ if m := trainer_normal_definition.search(line):
+ if trainer is not None:
+ trainer.party = party
+ write_to_file(trainer, output)
+ trainer = None
+ party = []
+ [id_] = m.groups()
+ trainer = Trainer(id_)
+ trainer.difficulty = None
+ trainer.gender = 'Male'
+ elif m := trainer_easy_definition.search(line):
+ if trainer is not None:
+ trainer.party = party
+ write_to_file(trainer, output)
+ trainer = None
+ party = []
+ [id_] = m.groups()
+ trainer = Trainer(id_)
+ trainer.difficulty = "Easy"
+ trainer.gender = 'Male'
+ elif m := trainer_hard_definition.search(line):
+ if trainer is not None:
+ trainer.party = party
+ write_to_file(trainer, output)
+ trainer = None
+ party = []
+ [id_] = m.groups()
+ trainer = Trainer(id_)
+ trainer.difficulty = "Hard"
+ trainer.gender = 'Male'
+ elif m := trainer_class_definition.search(line):
+ [class_] = m.groups()
+ class_ = class_.replace("_", " ").title()
+ for match, replacement in class_fixups.items():
+ class_ = class_.replace(match, replacement)
+ trainer.class_ = class_
+ elif m := encounter_music_gender_definition.search(line):
+ [is_female, music] = m.groups()
+ trainer.gender = 'Female' if is_female else 'Male'
+ trainer.encounter_music = music.replace("_", " ").title()
+ elif m := encounter_music_definition.search(line):
+ [music] = m.groups()
+ trainer.encounter_music = music.replace("_", " ").title()
+ elif "F_TRAINER_FEMALE" in line:
+ trainer.gender = 'Female'
+ elif m := trainer_pic_definition.search(line):
+ [pic] = m.groups()
+ pic = pic.replace("_", " ").title()
+ for match, replacement in pic_fixups.items():
+ pic = pic.replace(match, replacement)
+ trainer.pic = pic
+ elif m := trainer_name_definition.search(line):
+ [name] = m.groups()
+ trainer.name = name
+ elif m := trainer_items_definition.search(line):
+ [items] = m.groups()
+ trainer.items = " / ".join(item.replace("_", " ").title() for item in trainer_item_definition.findall(items) if item != "NONE")
+ elif m := trainer_double_battle_definition.search(line):
+ [double_battle] = m.groups()
+ if double_battle == 'TRAINER_BATTLE_TYPE_DOUBLES':
+ trainer.double_battle = "Doubles"
+ elif double_battle == 'TRAINER_BATTLE_TYPE_SINGLES':
+ trainer.double_battle = "Singles"
+ elif m := trainer_ai_flags_definition.search(line):
+ [ai_flags] = m.groups()
+ trainer.ai_flags = " / ".join(ai_flag.replace("_", " ").title() for ai_flag in trainer_ai_flag_definition.findall(ai_flags))
+ elif m := trainer_mugshot_definition.search(line):
+ [color] = m.groups()
+ trainer.mugshot = color.title()
+ elif m := trainer_starting_status_definition.search(line):
+ [starting_status] = m.groups()
+ trainer.starting_status = starting_status.replace("_", " ").title()
+ elif m := trainer_party_definition.search(line):
+ pokemon = Pokemon()
+
+ # Party mons
+ elif end_pokemon_definition.search(line):
+ party.append(pokemon)
+ pokemon = Pokemon()
+ elif m := level_definition.search(line):
+ [level] = m.groups()
+ pokemon.attributes['Level'] = level
+ elif m := species_definition.search(line):
+ [species_] = m.groups()
+ for match, replacement in species_replacements.items():
+ species_ = species_.replace(match, replacement)
+ pokemon.species = species_.replace("_", " ").title()
+ elif m := gender_definition.search(line):
+ [gender_] = m.groups()
+ if gender_ == 'MALE':
+ pokemon.gender = 'M'
+ elif gender_ == 'FEMALE':
+ pokemon.gender = 'F'
+ elif m := nickname_definition.search(line):
+ [nickname] = m.groups()
+ pokemon.nickname = nickname
+ elif m := item_definition.search(line):
+ [item_] = m.groups()
+ pokemon.item = item_.replace("_", " ").title()
+ elif m := ball_definition.search(line):
+ [ball] = m.groups()
+ pokemon.attributes['Ball'] = ball.replace("_", " ").title()
+ elif m := ability_definition.search(line):
+ [ability] = m.groups()
+ pokemon.attributes['Ability'] = ability.replace("_", " ").title()
+ elif m := friendship_definition.search(line):
+ [friendship] = m.groups()
+ pokemon.attributes['Happiness'] = friendship
+ elif m := shiny_definition.search(line):
+ [shiny] = m.groups()
+ if shiny == 'TRUE':
+ pokemon.attributes['Shiny'] = 'Yes'
+ elif shiny == 'FALSE':
+ pokemon.attributes['Shiny'] = 'No'
+ elif m := ivs_definition.search(line):
+ [hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()]
+ stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed}
+ pokemon.attributes['IVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items())
+ elif m := evs_definition.search(line):
+ [hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()]
+ stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed}
+ pokemon.attributes['EVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items() if value != '0')
+ elif m := move_definition.search(line):
+ [move] = m.groups()
+ pokemon.moves.append(move.replace("_", " ").title())
+ elif m := nature_definition.search(line):
+ [nature] = m.groups()
+ pokemon.nature = nature.replace("_", " ").title()
+
+ except Exception as e:
+ print(f"{line_no}: {e}")
+
+if __name__ == '__main__':
+ try:
+ [argv0, trainers_in_path, out_path] = sys.argv
+ except:
+ print(f"usage: python3 {sys.argv[0]} ")
+ print("trainers.h path: src/data/trainers.h")
+ print("trainers.party output path: src/data/trainers.party")
+ else:
+ with open(trainers_in_path, "r") as source, open(out_path, 'w') as output:
+ parse_trainers(source, output)
diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c
index d9e70c6b4b..1960088640 100644
--- a/src/battle_ai_main.c
+++ b/src/battle_ai_main.c
@@ -319,7 +319,7 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
gAiBattleData->chosenTarget[battler] = gBattlerTarget;
}
-bool32 BattlerChoseNonMoveAction(void)
+bool32 BattlerChooseNonMoveAction(void)
{
if (gAiThinkingStruct->aiAction & AI_ACTION_FLEE)
{
@@ -361,10 +361,6 @@ void ComputeBattlerDecisions(u32 battler)
bool32 isAiBattler = (gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE));
if (isAiBattler || CanAiPredictMove())
{
- // If ai is about to flee or chosen to watch player, no need to calc anything
- if (isAiBattler && BattlerChoseNonMoveAction())
- return;
-
// Risky AI switches aggressively even mid battle
enum SwitchType switchType = (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE;
@@ -385,6 +381,8 @@ void ComputeBattlerDecisions(u32 battler)
// AI's move scoring
gAiBattleData->chosenMoveIndex[battler] = BattleAI_ChooseMoveIndex(battler); // Calculate score and chose move index
+ if (isAiBattler)
+ BattlerChooseNonMoveAction();
ModifySwitchAfterMoveScoring(battler);
gAiLogicData->aiCalcInProgress = FALSE;
diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c
index ed81c19659..96cb7e30df 100644
--- a/src/battle_ai_switch_items.c
+++ b/src/battle_ai_switch_items.c
@@ -1994,7 +1994,10 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle
{
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, AI_DEFENDING);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
+ {
+ *bestPlayerMove = playerMove;
return damageTaken;
+ }
if (damageTaken > maxDamageTaken)
{
maxDamageTaken = damageTaken;
@@ -2014,13 +2017,19 @@ static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposi
for (i = 0; i < MAX_MON_MOVES; i++)
{
+ // If player is choiced into a non-priority move, AI understands that it can't deal priority damage
+ if (gBattleStruct->choicedMove[opposingBattler] !=MOVE_NONE && GetMovePriority(gBattleStruct->choicedMove[opposingBattler]) < 1)
+ break;
playerMove = SMART_SWITCHING_OMNISCIENT ? gBattleMons[opposingBattler].moves[i] : playerMoves[i];
if (GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], playerMove) > 0
&& playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH && gBattleMons[opposingBattler].pp[i] > 0)
{
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, AI_DEFENDING);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
+ {
+ *bestPlayerPriorityMove = playerMove;
return damageTaken;
+ }
if (damageTaken > maxDamageTaken)
{
maxDamageTaken = damageTaken;
diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c
index bf06a8e6a1..5913c0842c 100644
--- a/src/battle_ai_util.c
+++ b/src/battle_ai_util.c
@@ -534,7 +534,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
{
if (ability == ABILITY_OVERCOAT
- || (B_POWDER_GRASS >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
+ || (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|| holdEffect == HOLD_EFFECT_SAFETY_GOGGLES)
return FALSE;
return TRUE;
@@ -1673,7 +1673,10 @@ s32 AI_DecideKnownAbilityForTurn(u32 battlerId)
enum ItemHoldEffect AI_DecideHoldEffectForTurn(u32 battlerId)
{
- enum ItemHoldEffect holdEffect;
+ enum ItemHoldEffect holdEffect = HOLD_EFFECT_NONE;
+
+ if (gBattleMons[battlerId].item == ITEM_NONE) // Failsafe for when user recorded an item but it was consumed
+ return holdEffect;
if (!IsAiBattlerAware(battlerId))
holdEffect = gAiPartyData->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect;
@@ -3171,14 +3174,16 @@ enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 mov
u32 battlerToSwitch;
u32 predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData);
- battlerToSwitch = gBattleStruct->AI_monToSwitchIntoId[battlerAtk];
-
// Palafin always wants to activate Zero to Hero
if (gBattleMons[battlerAtk].species == SPECIES_PALAFIN_ZERO
&& gBattleMons[battlerAtk].ability == ABILITY_ZERO_TO_HERO
&& CountUsablePartyMons(battlerAtk) != 0)
return SHOULD_PIVOT;
+ battlerToSwitch = gAiLogicData->mostSuitableMonId[battlerAtk];
+ // This shouldn't ever happen, but it's there to make sure we don't accidentally read past the gParty array.
+ if (battlerToSwitch >= PARTY_SIZE)
+ battlerToSwitch = 0;
if (PartyBattlerShouldAvoidHazards(battlerAtk, battlerToSwitch))
return DONT_PIVOT;
diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c
index 490136d116..5674cd5cc7 100644
--- a/src/battle_anim_water.c
+++ b/src/battle_anim_water.c
@@ -45,6 +45,7 @@ static void AnimTask_RunSinAnimTimer(u8);
static void AnimTask_CreateSurfWave_Step1(u8);
static void AnimTask_CreateSurfWave_Step2(u8);
static void AnimTask_SurfWaveScanlineEffect(u8);
+static void AnimTask_BrineRain_Step(u8);
static void AnimTask_WaterSpoutLaunch_Step(u8);
static void AnimTask_WaterSpoutRain_Step(u8);
static u8 GetWaterSpoutPowerForAnim(void);
@@ -1442,24 +1443,97 @@ static void AnimSmallWaterOrb(struct Sprite *sprite)
}
}
+#define tRainState data[0]
+#define tWaterSpoutPower data[1]
+#define tDropTaskDelay data[2]
+#define tDropInitialXPos data[4]
+#define tDropXRange data[5]
+#define tDropEndYPos data[6]
+#define tDropXPos data[7]
+#define tSineTableIndex data[8]
+#define tCurrentDropSprites data[9]
+#define tDropHasHit data[10]
+#define tCreatedDropSprites data[11]
+#define tMaxDropSprites data[12]
+#define tShakeTasksCreated data[13]
+#define tDropInitialYPos data[14]
+
+void AnimTask_BrineRain(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+
+ if (IsOnPlayerSide(gBattleAnimAttacker))
+ {
+ task->tDropEndYPos = 40;
+ task->tDropInitialYPos = 0;
+ }
+ else
+ {
+ task->tDropEndYPos = 90;
+ task->tDropInitialYPos = 40;
+ }
+ task->tDropInitialXPos = GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2);
+ task->tDropXRange = 40;
+ task->tDropXPos = task->tDropInitialXPos;
+ task->tMaxDropSprites = 10;
+ task->func = AnimTask_BrineRain_Step;
+}
+
+static void AnimTask_BrineRain_Step(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+ u8 taskId2;
+
+ switch (task->tRainState)
+ {
+ case 0:
+ if (++task->tDropTaskDelay > 2)
+ {
+ task->tDropTaskDelay = 0;
+ CreateWaterSpoutRainDroplet(task, taskId);
+ }
+ if (task->tDropHasHit != FALSE && task->tShakeTasksCreated == FALSE)
+ {
+ gBattleAnimArgs[0] = ANIM_TARGET;
+ gBattleAnimArgs[1] = 0;
+ gBattleAnimArgs[2] = 12;
+ taskId2 = CreateTask(AnimTask_HorizontalShake, 80);
+ if (taskId2 != TASK_NONE)
+ {
+ gTasks[taskId2].func(taskId2);
+ gAnimVisualTaskCount++;
+ }
+ task->tShakeTasksCreated = TRUE;
+ }
+ if (task->tCreatedDropSprites >= task->tMaxDropSprites)
+ task->tRainState++;
+ break;
+ case 1:
+ if (task->tCurrentDropSprites == 0)
+ DestroyAnimVisualTask(taskId);
+ break;
+ }
+}
+
void AnimTask_WaterSpoutRain(u8 taskId)
{
struct Task *task = &gTasks[taskId];
- task->data[1] = GetWaterSpoutPowerForAnim();
+ task->tWaterSpoutPower = GetWaterSpoutPowerForAnim();
if (IsOnPlayerSide(gBattleAnimAttacker))
{
- task->data[4] = 136;
- task->data[6] = 40;
+ task->tDropInitialXPos = 136;
+ task->tDropEndYPos = 40;
}
else
{
- task->data[4] = 16;
- task->data[6] = 80;
+ task->tDropInitialXPos = 16;
+ task->tDropEndYPos = 80;
}
- task->data[5] = 98;
- task->data[7] = task->data[4] + 49;
- task->data[12] = task->data[1] * 5 + 5;
+ task->tDropXRange = 98;
+ task->tDropXPos = task->tDropInitialXPos + 49;
+ task->tMaxDropSprites = task->tWaterSpoutPower * 5 + 5;
+ task->tDropInitialYPos = 0;
task->func = AnimTask_WaterSpoutRain_Step;
}
@@ -1468,15 +1542,15 @@ static void AnimTask_WaterSpoutRain_Step(u8 taskId)
struct Task *task = &gTasks[taskId];
u8 taskId2;
- switch (task->data[0])
+ switch (task->tRainState)
{
case 0:
- if (++task->data[2] > 2)
+ if (++task->tDropTaskDelay > 2)
{
- task->data[2] = 0;
+ task->tDropTaskDelay = 0;
CreateWaterSpoutRainDroplet(task, taskId);
}
- if (task->data[10] != 0 && task->data[13] == 0)
+ if (task->tDropHasHit != FALSE && task->tShakeTasksCreated == FALSE)
{
gBattleAnimArgs[0] = ANIM_TARGET;
gBattleAnimArgs[1] = 0;
@@ -1494,13 +1568,13 @@ static void AnimTask_WaterSpoutRain_Step(u8 taskId)
gTasks[taskId2].func(taskId2);
gAnimVisualTaskCount++;
}
- task->data[13] = 1;
+ task->tShakeTasksCreated = TRUE;
}
- if (task->data[11] >= task->data[12])
- task->data[0]++;
+ if (task->tCreatedDropSprites >= task->tMaxDropSprites)
+ task->tRainState++;
break;
case 1:
- if (task->data[9] == 0)
+ if (task->tCurrentDropSprites == 0)
DestroyAnimVisualTask(taskId);
break;
}
@@ -1508,8 +1582,8 @@ static void AnimTask_WaterSpoutRain_Step(u8 taskId)
static void CreateWaterSpoutRainDroplet(struct Task *task, u8 taskId)
{
- u16 yPosArg = ((gSineTable[task->data[8]] + 3) >> 4) + task->data[6];
- u8 spriteId = CreateSprite(&gSmallWaterOrbSpriteTemplate, task->data[7], 0, 0);
+ u16 yPosArg = ((gSineTable[task->tSineTableIndex] + 3) >> 4) + task->tDropEndYPos;
+ u8 spriteId = CreateSprite(&gSmallWaterOrbSpriteTemplate, task->tDropXPos, task->tDropInitialYPos, 0);
if (spriteId != MAX_SPRITES)
{
@@ -1517,11 +1591,11 @@ static void CreateWaterSpoutRainDroplet(struct Task *task, u8 taskId)
gSprites[spriteId].data[5] = yPosArg;
gSprites[spriteId].data[6] = taskId;
gSprites[spriteId].data[7] = 9;
- task->data[9]++;
+ task->tCurrentDropSprites++;
}
- task->data[11]++;
- task->data[8] = (task->data[8] + 39) & 0xFF;
- task->data[7] = (ISO_RANDOMIZE2(task->data[7]) % task->data[5]) + task->data[4];
+ task->tCreatedDropSprites++;
+ task->tSineTableIndex = (task->tSineTableIndex + 39) & 0xFF;
+ task->tDropXPos = (ISO_RANDOMIZE2(task->tDropXPos) % task->tDropXRange) + task->tDropInitialXPos;
}
static void AnimWaterSpoutRain(struct Sprite *sprite)
@@ -1531,7 +1605,7 @@ static void AnimWaterSpoutRain(struct Sprite *sprite)
sprite->y += 8;
if (sprite->y >= sprite->data[5])
{
- gTasks[sprite->data[6]].data[10] = 1;
+ gTasks[sprite->data[6]].tDropHasHit = TRUE;
sprite->data[1] = CreateSprite(&gWaterHitSplatSpriteTemplate, sprite->x, sprite->y, 1);
if (sprite->data[1] != MAX_SPRITES)
{
@@ -1560,6 +1634,21 @@ static void AnimWaterSpoutRainHit(struct Sprite *sprite)
}
}
+#undef tRainState
+#undef tWaterSpoutPower
+#undef tDropTaskDelay
+#undef tDropInitialXPos
+#undef tDropXRange
+#undef tDropEndYPos
+#undef tDropXPos
+#undef tSineTableIndex
+#undef tCurrentDropSprites
+#undef tDropHasHit
+#undef tCreatedDropSprites
+#undef tMaxDropSprites
+#undef tShakeTasksCreated
+#undef tDropInitialYPos
+
void AnimTask_WaterSport(u8 taskId)
{
struct Task *task = &gTasks[taskId];
diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c
index 8c469733c1..19aeef3f4f 100644
--- a/src/battle_controller_opponent.c
+++ b/src/battle_controller_opponent.c
@@ -537,6 +537,12 @@ static void OpponentHandleChoosePokemon(u32 battler)
{
if (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch)
switchType = SWITCH_MID_BATTLE;
+
+ // reset the AI data to consider the correct on-field state at time of switch
+ SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), gAiLogicData);
+ if (IsDoubleBattle())
+ SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT), gAiLogicData);
+
chosenMonId = GetMostSuitableMonToSwitchInto(battler, switchType);
if (chosenMonId == PARTY_SIZE)
{
diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c
index f9e83338cb..b59ed37432 100644
--- a/src/battle_controller_player.c
+++ b/src/battle_controller_player.c
@@ -2004,7 +2004,7 @@ static void PlayerHandleChooseAction(u32 battler)
else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_LEFT)
StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-"));
else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_RIGHT)
- StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-"));
+ StringAppend(gStringVar1, COMPOUND_STRING(" -{DOWN_ARROW}"));
}
else if (moveTarget == MOVE_TARGET_BOTH)
{
diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c
index 85a77399c0..73ffa12023 100644
--- a/src/battle_end_turn.c
+++ b/src/battle_end_turn.c
@@ -61,8 +61,8 @@ enum EndTurnResolutionOrder
ENDTURN_TERRAIN,
ENDTURN_THIRD_EVENT_BLOCK,
ENDTURN_EMERGENCY_EXIT_4,
- ENDTURN_ABILITIES,
- ENDTURN_FOURTH_EVENT_BLOCK,
+ ENDTURN_FORM_CHANGE_ABILITIES,
+ ENDTURN_EJECT_PACK,
ENDTURN_DYNAMAX,
ENDTURN_COUNT,
};
@@ -101,13 +101,6 @@ enum ThirdEventBlock
THIRD_EVENT_BLOCK_ITEMS,
};
-// Form changing abilities and Eject Pack
-enum FourthEventBlock
-{
- FOURTH_EVENT_BLOCK_HUNGER_SWITCH,
- FOURTH_EVENT_BLOCK_EJECT_PACK,
-};
-
static u32 GetBattlerSideForMessage(u32 side)
{
u32 battler = 0;
@@ -1458,7 +1451,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
return effect;
}
-static bool32 HandleEndTurnAbilities(u32 battler)
+static bool32 HandleEndTurnFormChangeAbilities(u32 battler)
{
bool32 effect = FALSE;
@@ -1472,6 +1465,7 @@ static bool32 HandleEndTurnAbilities(u32 battler)
case ABILITY_SCHOOLING:
case ABILITY_SHIELDS_DOWN:
case ABILITY_ZEN_MODE:
+ case ABILITY_HUNGER_SWITCH:
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
}
@@ -1479,38 +1473,10 @@ static bool32 HandleEndTurnAbilities(u32 battler)
return effect;
}
-static bool32 HandleEndTurnFourthEventBlock(u32 battler)
+static bool32 HandleEndTurnEjectPack(u32 battler)
{
- bool32 effect = FALSE;
-
- switch (gBattleStruct->eventBlockCounter)
- {
- case FOURTH_EVENT_BLOCK_HUNGER_SWITCH:
- {
- u32 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, TRUE);
- if (holdEffect == HOLD_EFFECT_EJECT_PACK)
- {
- if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler))
- effect = TRUE;
- }
- gBattleStruct->eventBlockCounter = 0;
- gBattleStruct->turnEffectsBattlerId++;
- break;
- }
- }
-
- return effect;
+ gBattleStruct->turnEffectsBattlerId++;
+ return TrySwitchInEjectPack(ITEMEFFECT_NORMAL);
}
static bool32 HandleEndTurnDynamax(u32 battler)
@@ -1577,8 +1543,8 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) =
[ENDTURN_TERRAIN] = HandleEndTurnTerrain,
[ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock,
[ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit,
- [ENDTURN_ABILITIES] = HandleEndTurnAbilities,
- [ENDTURN_FOURTH_EVENT_BLOCK] = HandleEndTurnFourthEventBlock,
+ [ENDTURN_FORM_CHANGE_ABILITIES] = HandleEndTurnFormChangeAbilities,
+ [ENDTURN_EJECT_PACK] = HandleEndTurnEjectPack,
[ENDTURN_DYNAMAX] = HandleEndTurnDynamax,
};
diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c
index e7bf06327d..d7004d7635 100644
--- a/src/battle_gfx_sfx_util.c
+++ b/src/battle_gfx_sfx_util.c
@@ -331,6 +331,7 @@ static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move)
case MOVE_TARGET_RANDOM:
case MOVE_TARGET_BOTH:
case MOVE_TARGET_FOES_AND_ALLY:
+ case MOVE_TARGET_ALL_BATTLERS:
if (IsBattleMoveStatus(move))
return PALACE_MOVE_GROUP_SUPPORT;
else
@@ -338,6 +339,7 @@ static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move)
break;
case MOVE_TARGET_DEPENDS:
case MOVE_TARGET_OPPONENTS_FIELD:
+ case MOVE_TARGET_ALLY:
return PALACE_MOVE_GROUP_SUPPORT;
case MOVE_TARGET_USER:
return PALACE_MOVE_GROUP_DEFENSE;
diff --git a/src/battle_interface.c b/src/battle_interface.c
index a1b9c23f5b..57094a7a8e 100644
--- a/src/battle_interface.c
+++ b/src/battle_interface.c
@@ -2971,7 +2971,8 @@ void TryAddLastUsedBallItemSprites(void)
static void DestroyLastUsedBallWinGfx(struct Sprite *sprite)
{
FreeSpriteTilesByTag(TAG_LAST_BALL_WINDOW);
- FreeSpritePaletteByTag(TAG_ABILITY_POP_UP);
+ if (GetSpriteTileStartByTag(MOVE_INFO_WINDOW_TAG) == 0xFFFF)
+ FreeSpritePaletteByTag(TAG_ABILITY_POP_UP);
DestroySprite(sprite);
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
}
@@ -3008,7 +3009,8 @@ void TryToHideMoveInfoWindow(void)
static void DestroyMoveInfoWinGfx(struct Sprite *sprite)
{
FreeSpriteTilesByTag(MOVE_INFO_WINDOW_TAG);
- FreeSpritePaletteByTag(TAG_ABILITY_POP_UP);
+ if (GetSpriteTileStartByTag(TAG_LAST_BALL_WINDOW) == 0xFFFF)
+ FreeSpritePaletteByTag(TAG_ABILITY_POP_UP);
DestroySprite(sprite);
gBattleStruct->moveInfoSpriteId = MAX_SPRITES;
}
diff --git a/src/battle_main.c b/src/battle_main.c
index db72bf90ea..cd1dc7c279 100644
--- a/src/battle_main.c
+++ b/src/battle_main.c
@@ -3870,6 +3870,8 @@ static void TryDoEventsBeforeFirstTurn(void)
return;
if (TryClearIllusion(i, ABILITYEFFECT_ON_SWITCHIN))
return;
+ if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, i, 0, 0, 0) != 0)
+ return;
}
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++;
@@ -5141,6 +5143,7 @@ static void TurnValuesCleanUp(bool8 var0)
gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF;
gBattleStruct->battlerState[i].usedEjectItem = FALSE;
gProtectStructs[i].lashOutAffected = FALSE;
+ gDisableStructs[i].endured = FALSE;
}
gSideTimers[B_SIDE_PLAYER].followmeTimer = 0;
@@ -5716,20 +5719,24 @@ static void TryEvolvePokemon(void)
if (!(sTriedEvolving & (1u << i)))
{
bool32 canStopEvo = TRUE;
- u32 species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_SPECIAL, i, NULL, &canStopEvo, CHECK_EVO);
+ enum EvolutionMode mode = EVO_MODE_BATTLE_SPECIAL;
+ u32 evolutionItemArg = i;
+ u32 species = GetEvolutionTargetSpecies(&gPlayerParty[i], mode, evolutionItemArg, NULL, &canStopEvo, CHECK_EVO);
sTriedEvolving |= 1u << i;
if (species == SPECIES_NONE && (gLeveledUpInBattle & (1u << i)))
{
gLeveledUpInBattle &= ~(1u << i);
- species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL, &canStopEvo, CHECK_EVO);
+ mode = EVO_MODE_BATTLE_ONLY;
+ evolutionItemArg = gLeveledUpInBattle;
+ species = GetEvolutionTargetSpecies(&gPlayerParty[i], mode, evolutionItemArg, NULL, &canStopEvo, CHECK_EVO);
}
if (species != SPECIES_NONE)
{
FreeAllWindowBuffers();
gBattleMainFunc = WaitForEvoSceneToFinish;
- GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL, &canStopEvo, DO_EVO);
+ GetEvolutionTargetSpecies(&gPlayerParty[i], mode, evolutionItemArg, NULL, &canStopEvo, DO_EVO);
EvolutionScene(&gPlayerParty[i], species, canStopEvo, i);
return;
}
@@ -5855,11 +5862,11 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState
enum ItemHoldEffect holdEffect;
enum Gimmick gimmick = GetActiveGimmick(battler);
- if (move == MOVE_STRUGGLE)
- return TYPE_NORMAL;
-
if (state == MON_IN_BATTLE)
{
+ if (moveEffect == EFFECT_STRUGGLE)
+ return TYPE_MYSTERY;
+
species = gBattleMons[battler].species;
heldItem = gBattleMons[battler].item;
holdEffect = GetBattlerHoldEffect(battler, TRUE);
@@ -6112,8 +6119,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battler)
&& GetBattleMoveType(move) == GetItemSecondaryId(heldItem)
&& effect != EFFECT_PLEDGE
&& effect != EFFECT_OHKO
- && effect != EFFECT_SHEER_COLD
- && effect != EFFECT_STRUGGLE)
+ && effect != EFFECT_SHEER_COLD)
{
gSpecialStatuses[battler].gemParam = GetBattlerHoldEffectParam(battler);
gSpecialStatuses[battler].gemBoost = TRUE;
diff --git a/src/battle_pike.c b/src/battle_pike.c
index 80c0ddaaa7..3218ef3846 100644
--- a/src/battle_pike.c
+++ b/src/battle_pike.c
@@ -862,7 +862,7 @@ static bool8 DoesTypePreventStatus(u16 species, u32 status)
break;
case STATUS1_PARALYSIS:
if (GetSpeciesType(species, 0) == TYPE_GROUND || GetSpeciesType(species, 1) == TYPE_GROUND
- || (B_PARALYZE_ELECTRIC >= GEN_6 && (GetSpeciesType(species, 0) == TYPE_ELECTRIC || GetSpeciesType(species, 1) == TYPE_ELECTRIC)))
+ || (GetGenConfig(GEN_CONFIG_PARALYZE_ELECTRIC) >= GEN_6 && (GetSpeciesType(species, 0) == TYPE_ELECTRIC || GetSpeciesType(species, 1) == TYPE_ELECTRIC)))
ret = TRUE;
break;
case STATUS1_BURN:
diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c
index f2b03a382e..45fa3f3e97 100755
--- a/src/battle_script_commands.c
+++ b/src/battle_script_commands.c
@@ -1046,7 +1046,7 @@ bool32 IsMovePowderBlocked(u32 battlerAtk, u32 battlerDef, u32 move)
if (IsPowderMove(move) && (battlerAtk != battlerDef))
{
- if (B_POWDER_GRASS >= GEN_6
+ if (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6
&& (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || GetBattlerAbility(battlerDef) == ABILITY_OVERCOAT))
{
gBattlerAbility = battlerDef;
@@ -1901,13 +1901,13 @@ static void Cmd_adjustdamage(void)
gBattleStruct->moveResultFlags[battlerDef] &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE);
gBattleStruct->moveDamage[battlerDef] = 0;
gSpecialStatuses[battlerDef].enduredDamage = TRUE;
- RecordAbilityBattle(gBattlerTarget, ABILITY_ICE_FACE);
+ RecordAbilityBattle(battlerDef, ABILITY_ICE_FACE);
gDisableStructs[battlerDef].iceFaceActivationPrevention = TRUE;
// Form change will be done after attack animation in Cmd_resultmessage.
continue;
}
- if (gBattleMons[gBattlerTarget].hp > gBattleStruct->moveDamage[battlerDef])
+ if (gBattleMons[battlerDef].hp > gBattleStruct->moveDamage[battlerDef])
continue;
holdEffect = GetBattlerHoldEffect(battlerDef, TRUE);
@@ -2861,7 +2861,8 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem)
BtlController_EmitSetMonData(battlerItem, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item
MarkBattlerForControllerExec(battlerItem);
- gBattleStruct->choicedMove[battlerItem] = 0;
+ if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
+ gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
TrySaveExchangedItem(battlerItem, gLastUsedItem);
}
@@ -5705,7 +5706,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
gLastUsedItem = gBattleMons[gBattlerTarget].item;
gBattleMons[gBattlerTarget].item = 0;
if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS)
- gBattleStruct->choicedMove[gBattlerTarget] = 0;
+ gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
CheckSetUnburden(gBattlerTarget);
// In Gen 5+, Knock Off removes the target's item rather than rendering it unusable.
@@ -6120,9 +6121,12 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities
- if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0))
- effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
- else
+ for (u16 battler = 0; battler < gBattlersCount; battler++)
+ {
+ if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0))
+ effect = TRUE;
+ }
+ if(!effect)
gBattleScripting.moveendState++;
break;
case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize
@@ -6494,10 +6498,30 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_ITEM_EFFECTS_ATTACKER:
+ // ITEMEFFECT_MOVE_END loops over all battlers, not just attacker.
+ // It will executre only the first mon with an applicable item.
+ // So presumably it is a bug
if (ItemBattleEffects(ITEMEFFECT_MOVE_END, gBattlerAttacker))
effect = TRUE;
gBattleScripting.moveendState++;
break;
+ case MOVEEND_ITEM_THROAT_SPRAY:
+ if (IsSoundMove(gCurrentMove)
+ && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
+ && GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_THROAT_SPRAY
+ && IsBattlerAlive(gBattlerAttacker)
+ && IsAnyTargetAffected(gBattlerAttacker)
+ && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)
+ && !NoAliveMonsForEitherParty()) // Don't activate if battle will end
+ {
+ gLastUsedItem = gBattleMons[gBattlerAttacker].item;
+ gBattleScripting.battler = gBattlerAttacker;
+ SET_STATCHANGER(STAT_SPATK, 1, FALSE);
+ effect = TRUE;
+ BattleScriptCall(BattleScript_AttackerItemStatRaise);
+ }
+ gBattleScripting.moveendState++;
+ break;
case MOVEEND_ABILITY_BLOCK:
effect = HandleMoveEndAbilityBlock(gBattlerAttacker, gBattlerTarget, gCurrentMove);
gBattleScripting.moveendState++;
@@ -12741,7 +12765,7 @@ static void Cmd_settaunt(void)
{
CMD_ARGS(const u8 *failInstr);
- if (B_OBLIVIOUS_TAUNT >= GEN_6 && GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS)
+ if (GetGenConfig(GEN_CONFIG_OBLIVIOUS_TAUNT) >= GEN_6 && GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS)
{
gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp;
gLastUsedAbility = ABILITY_OBLIVIOUS;
@@ -12865,8 +12889,10 @@ static void Cmd_tryswapitems(void)
BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item);
MarkBattlerForControllerExec(gBattlerTarget);
- gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
- gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE;
+ if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
+ gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
+ if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
+ gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE;
gBattlescriptCurrInstr = cmd->nextInstr;
@@ -17227,6 +17253,7 @@ void BS_SwitchinAbilities(void)
AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0);
AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0);
+ AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0);
if (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect())
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0);
diff --git a/src/battle_tower.c b/src/battle_tower.c
index b5f919dc13..e1346bc08c 100644
--- a/src/battle_tower.c
+++ b/src/battle_tower.c
@@ -1648,8 +1648,8 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
SetMonData(dst, MON_DATA_TERA_TYPE, &data);
}
-
- SetMonData(dst, MON_DATA_POKEBALL, &ball);
+ if (ball != BALL_STRANGE)
+ SetMonData(dst, MON_DATA_POKEBALL, &ball);
CalculateMonStats(dst);
}
diff --git a/src/battle_util.c b/src/battle_util.c
index 61d8ef0535..ee98c8ddb1 100644
--- a/src/battle_util.c
+++ b/src/battle_util.c
@@ -65,7 +65,6 @@ static u32 GetFlingPowerFromItemId(u32 itemId);
static void SetRandomMultiHitCounter();
static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item);
static bool32 CanBeInfinitelyConfused(u32 battler);
-static bool32 IsAnyTargetAffected(u32 battlerAtk);
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum FunctionCallOption option);
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option);
@@ -416,7 +415,8 @@ void HandleAction_UseMove(void)
{
gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove;
gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos;
- gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
+ if (GetGenConfig(GEN_CONFIG_ENCORE_TARGET) < GEN_5)
+ gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
}
// check if the encored move wasn't overwritten
else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
@@ -529,8 +529,7 @@ void HandleAction_UseMove(void)
gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
}
}
-
- if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget))
+ else if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget))
{
gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler;
}
@@ -4689,7 +4688,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_EFFECT_SPORE:
{
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
- if ((!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) || B_POWDER_GRASS < GEN_6)
+ if ((GetGenConfig(GEN_CONFIG_POWDER_GRASS) < GEN_6 || !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS))
&& abilityAtk != ABILITY_OVERCOAT
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES)
{
@@ -5092,94 +5091,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITYEFFECT_IMMUNITY:
- for (battler = 0; battler < gBattlersCount; battler++)
- {
- switch (GetBattlerAbilityIgnoreMoldBreaker(battler))
- {
- case ABILITY_IMMUNITY:
- case ABILITY_PASTEL_VEIL:
- if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
- {
- StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
- effect = 1;
- }
- break;
- case ABILITY_OWN_TEMPO:
- if (gBattleMons[battler].volatiles.confusionTurns > 0)
- {
- StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
- effect = 2;
- }
- break;
- case ABILITY_LIMBER:
- if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
- {
- StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
- effect = 1;
- }
- break;
- case ABILITY_INSOMNIA:
- case ABILITY_VITAL_SPIRIT:
- if (gBattleMons[battler].status1 & STATUS1_SLEEP)
- {
- TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
- gBattleMons[battler].volatiles.nightmare = FALSE;
- StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
- effect = 1;
- }
- break;
- case ABILITY_WATER_VEIL:
- case ABILITY_WATER_BUBBLE:
- case ABILITY_THERMAL_EXCHANGE:
- if (gBattleMons[battler].status1 & STATUS1_BURN)
- {
- StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
- effect = 1;
- }
- break;
- case ABILITY_MAGMA_ARMOR:
- if (gBattleMons[battler].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE))
- {
- StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
- effect = 1;
- }
- break;
- case ABILITY_OBLIVIOUS:
- if (gBattleMons[battler].volatiles.infatuation)
- effect = 3;
- else if (gDisableStructs[battler].tauntTimer != 0)
- effect = 4;
- break;
- }
-
- if (effect != 0)
- {
- switch (effect)
- {
- case 1: // status cleared
- gBattleMons[battler].status1 = 0;
- BattleScriptCall(BattleScript_AbilityCuredStatus);
- break;
- case 2: // get rid of confusion
- RemoveConfusionStatus(battler);
- BattleScriptCall(BattleScript_AbilityCuredStatus);
- break;
- case 3: // get rid of infatuation
- gBattleMons[battler].volatiles.infatuation = 0;
- BattleScriptCall(BattleScript_BattlerGotOverItsInfatuation);
- break;
- case 4: // get rid of taunt
- gDisableStructs[battler].tauntTimer = 0;
- BattleScriptCall(BattleScript_BattlerShookOffTaunt);
- break;
- }
-
- gBattleScripting.battler = gBattlerAbility = battler;
- BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
- MarkBattlerForControllerExec(battler);
- return effect;
- }
- }
+ effect = TryImmunityAbilityHealStatus(battler, caseID);
+ if (effect)
+ return effect;
+ break;
+ case ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES:
+ effect = TryImmunityAbilityHealStatus(battler, caseID);
break;
case ABILITYEFFECT_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE)
@@ -5698,7 +5615,7 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
{
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;
}
@@ -6501,21 +6418,6 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, ITEMEFFECT_NONE);
break;
- case HOLD_EFFECT_THROAT_SPRAY:
- if (IsSoundMove(gCurrentMove)
- && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
- && 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 = ITEM_STATS_CHANGE;
- BattleScriptCall(BattleScript_AttackerItemStatRaise);
- }
- break;
default:
break;
}
@@ -10419,6 +10321,110 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler)
return FALSE;
}
+u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID)
+{
+ u32 effect = 0;
+ switch (GetBattlerAbilityIgnoreMoldBreaker(battler))
+ {
+ case ABILITY_IMMUNITY:
+ case ABILITY_PASTEL_VEIL:
+ if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_OWN_TEMPO:
+ if (gBattleMons[battler].volatiles.confusionTurns > 0)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
+ effect = 2;
+ }
+ break;
+ case ABILITY_LIMBER:
+ if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_INSOMNIA:
+ case ABILITY_VITAL_SPIRIT:
+ if (gBattleMons[battler].status1 & STATUS1_SLEEP)
+ {
+ TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
+ gBattleMons[battler].volatiles.nightmare = FALSE;
+ StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_WATER_VEIL:
+ case ABILITY_WATER_BUBBLE:
+ case ABILITY_THERMAL_EXCHANGE:
+ if (gBattleMons[battler].status1 & STATUS1_BURN)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_MAGMA_ARMOR:
+ if (gBattleMons[battler].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE))
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_OBLIVIOUS:
+ if (gBattleMons[battler].volatiles.infatuation)
+ effect = 3;
+ else if (GetGenConfig(GEN_CONFIG_OBLIVIOUS_TAUNT) >= GEN_6 && gDisableStructs[battler].tauntTimer != 0)
+ effect = 4;
+ break;
+ }
+
+ if (effect != 0)
+ {
+ switch (effect)
+ {
+ case 1: // status cleared
+ gBattleMons[battler].status1 = 0;
+ if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
+ BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
+ else
+ BattleScriptCall(BattleScript_AbilityCuredStatus);
+ break;
+ case 2: // get rid of confusion
+ RemoveConfusionStatus(battler);
+ if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
+ BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
+ else
+ BattleScriptCall(BattleScript_AbilityCuredStatus);
+ break;
+ case 3: // get rid of infatuation
+ gBattleMons[battler].volatiles.infatuation = 0;
+ if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
+ BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
+ else
+ BattleScriptCall(BattleScript_AbilityCuredStatus);
+ break;
+ case 4: // get rid of taunt
+ gDisableStructs[battler].tauntTimer = 0;
+ if(caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES)
+ BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3);
+ else
+ BattleScriptCall(BattleScript_AbilityCuredStatus);
+ break;
+ }
+
+ gBattleScripting.battler = gBattlerAbility = battler;
+ BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
+ MarkBattlerForControllerExec(battler);
+ return effect;
+ }
+
+ return 0;
+}
+
bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler)
{
if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0)
@@ -11412,7 +11418,7 @@ bool32 HasWeatherEffect(void)
return TRUE;
}
-static bool32 IsAnyTargetAffected(u32 battlerAtk)
+bool32 IsAnyTargetAffected(u32 battlerAtk)
{
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{
@@ -11499,14 +11505,11 @@ bool32 TrySwitchInEjectPack(enum ItemCaseId caseID)
gBattleScripting.battler = battler;
gLastUsedItem = gBattleMons[battler].item;
if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN)
- {
BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3);
- }
+ else if (caseID == ITEMEFFECT_NORMAL)
+ BattleScriptExecute(BattleScript_EjectPackActivate_End2);
else
- {
- BattleScriptPushCursor();
- gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret;
- }
+ BattleScriptCall(BattleScript_EjectPackActivate_Ret);
gAiLogicData->ejectPackSwitch = TRUE;
return TRUE;
}
@@ -11659,7 +11662,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
u32 nonVolatileStatus = GetMoveNonVolatileStatus(move);
if ((gBattleMons[battlerDef].volatiles.lockOn && gDisableStructs[battlerDef].battlerWithSureHit == battlerAtk)
- || (B_TOXIC_NEVER_MISS >= GEN_6 && nonVolatileStatus == MOVE_EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
+ || (GetGenConfig(GEN_CONFIG_TOXIC_NEVER_MISS) >= GEN_6 && nonVolatileStatus == MOVE_EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
|| gBattleMons[battlerDef].volatiles.glaiveRush)
{
effect = TRUE;
diff --git a/src/clock.c b/src/clock.c
index f0a632c9ff..142e62ebf2 100644
--- a/src/clock.c
+++ b/src/clock.c
@@ -1,16 +1,16 @@
#include "global.h"
+#include "berry.h"
#include "clock.h"
+#include "dewford_trend.h"
#include "event_data.h"
+#include "field_specials.h"
+#include "field_weather.h"
+#include "main.h"
+#include "lottery_corner.h"
+#include "overworld.h"
#include "rtc.h"
#include "time_events.h"
-#include "field_specials.h"
-#include "lottery_corner.h"
-#include "dewford_trend.h"
#include "tv.h"
-#include "field_weather.h"
-#include "berry.h"
-#include "main.h"
-#include "overworld.h"
#include "wallclock.h"
#include "constants/form_change_types.h"
diff --git a/src/data/abilities.h b/src/data/abilities.h
index b32c3d1072..658c0d96f8 100644
--- a/src/data/abilities.h
+++ b/src/data/abilities.h
@@ -1282,6 +1282,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
{
.name = _("防弹"),
.description = COMPOUND_STRING("防守球和弹类招式"),
+ .breakable = TRUE,
.aiRating = 7,
},
diff --git a/src/data/moves_info.h b/src/data/moves_info.h
index 5ce3dd528b..6803746391 100644
--- a/src/data/moves_info.h
+++ b/src/data/moves_info.h
@@ -19184,7 +19184,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.power = 100,
.type = TYPE_ICE,
.accuracy = 85,
- .pp = 5,
+ .pp = B_UPDATED_MOVE_DATA >= GEN_9 ? 10 : 5,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_PHYSICAL,
diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h
index ea2cdfd2a2..ad2e6a040d 100644
--- a/src/data/pokemon/form_change_table_pointers.h
+++ b/src/data/pokemon/form_change_table_pointers.h
@@ -34,3 +34,22 @@ const struct Fusion *const gFusionTablePointers[NUM_SPECIES] =
#endif //P_FAMILY_CALYREX
#endif //P_FUSION_FORMS
};
+
+#if P_FUSION_FORMS
+#if P_FAMILY_KYUREM
+#if P_FAMILY_RESHIRAM
+const u16 gKyurenWhiteSwapMoveTable[][2] =
+{
+ {MOVE_SCARY_FACE, MOVE_FUSION_FLARE},
+ {MOVE_GLACIATE, MOVE_ICE_BURN},
+};
+#endif //P_FAMILY_RESHIRAM
+#if P_FAMILY_ZEKROM
+const u16 gKyurenBlackSwapMoveTable[][2] =
+{
+ {MOVE_SCARY_FACE, MOVE_FUSION_BOLT},
+ {MOVE_GLACIATE, MOVE_FREEZE_SHOCK},
+};
+#endif //P_FAMILY_ZEKROM
+#endif //P_FAMILY_KYUREM
+#endif //P_FUSION_FORMS
diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h
index ed80ae2178..ad8a7e90ca 100644
--- a/src/data/pokemon/form_change_tables.h
+++ b/src/data/pokemon/form_change_tables.h
@@ -755,8 +755,8 @@ static const struct FormChange sLandorusFormChangeTable[] = {
#if P_FAMILY_KYUREM
static const struct Fusion sKyuremFusionTable[] = {
- {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_RESHIRAM, SPECIES_KYUREM_WHITE},
- {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_ZEKROM, SPECIES_KYUREM_BLACK},
+ {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_RESHIRAM, SPECIES_KYUREM_WHITE, MOVE_NONE, SWAP_EXTRA_MOVES_KYUREM_WHITE},
+ {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_ZEKROM, SPECIES_KYUREM_BLACK, MOVE_NONE, SWAP_EXTRA_MOVES_KYUREM_BLACK},
{FUSION_TERMINATOR},
};
#endif //P_FAMILY_KYUREM
@@ -998,8 +998,8 @@ static const struct FormChange sMimikyuTotemFormChangeTable[] = {
#if P_FAMILY_NECROZMA
static const struct Fusion sNecrozmaFusionTable[] = {
- {1, ITEM_N_SOLARIZER, SPECIES_NECROZMA, SPECIES_SOLGALEO, SPECIES_NECROZMA_DUSK_MANE, MOVE_SUNSTEEL_STRIKE, MOVE_CONFUSION},
- {2, ITEM_N_LUNARIZER, SPECIES_NECROZMA, SPECIES_LUNALA, SPECIES_NECROZMA_DAWN_WINGS, MOVE_MOONGEIST_BEAM, MOVE_CONFUSION},
+ {1, ITEM_N_SOLARIZER, SPECIES_NECROZMA, SPECIES_SOLGALEO, SPECIES_NECROZMA_DUSK_MANE, MOVE_SUNSTEEL_STRIKE, FORGET_EXTRA_MOVES},
+ {2, ITEM_N_LUNARIZER, SPECIES_NECROZMA, SPECIES_LUNALA, SPECIES_NECROZMA_DAWN_WINGS, MOVE_MOONGEIST_BEAM, FORGET_EXTRA_MOVES},
{FUSION_TERMINATOR},
};
@@ -1266,8 +1266,8 @@ static const struct FormChange sUrshifuRapidStrikeFormChangeTable[] = {
#if P_FAMILY_CALYREX
static const struct Fusion sCalyrexFusionTable[] = {
- {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_GLASTRIER, SPECIES_CALYREX_ICE, MOVE_GLACIAL_LANCE, MOVE_CONFUSION},
- {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_SPECTRIER, SPECIES_CALYREX_SHADOW, MOVE_ASTRAL_BARRAGE, MOVE_CONFUSION},
+ {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_GLASTRIER, SPECIES_CALYREX_ICE, MOVE_GLACIAL_LANCE, FORGET_EXTRA_MOVES},
+ {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_SPECTRIER, SPECIES_CALYREX_SHADOW, MOVE_ASTRAL_BARRAGE, FORGET_EXTRA_MOVES},
{FUSION_TERMINATOR},
};
#endif //P_FAMILY_CALYREX
diff --git a/src/debug.c b/src/debug.c
index 0f49d50fd4..f01d560a36 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -55,6 +55,7 @@
#include "strings.h"
#include "string_util.h"
#include "task.h"
+#include "tv.h"
#include "pokemon_summary_screen.h"
#include "wild_encounter.h"
#include "constants/abilities.h"
@@ -2576,11 +2577,11 @@ static void DebugAction_Give_Pokemon_SelectDynamaxLevel(u8 taskId)
}
}
-static void Debug_Display_StatInfo(const u8* text, u32 stat, u32 value, u32 digit, u8 windowId)
+static void Debug_Display_StatInfo(const u8* text, u32 stat, u32 value, u32 digit, u8 windowId, u32 maxValue)
{
StringCopy(gStringVar1, gStatNamesTable[stat]);
StringCopy(gStringVar2, gText_DigitIndicator[digit]);
- ConvertIntToDecimalStringN(gStringVar3, value, STR_CONV_MODE_LEADING_ZEROS, 2);
+ ConvertIntToDecimalStringN(gStringVar3, value, STR_CONV_MODE_LEADING_ZEROS, CountDigits(maxValue));
StringCopyPadded(gStringVar3, gStringVar3, CHAR_SPACE, 15);
StringExpandPlaceholders(gStringVar4, text);
AddTextPrinterParameterized(windowId, DEBUG_MENU_FONT, gStringVar4, 0, 0, 0, NULL);
@@ -2600,7 +2601,7 @@ static void DebugAction_Give_Pokemon_SelectGigantamaxFactor(u8 taskId)
sDebugMonData->gmaxFactor = gTasks[taskId].tInput;
gTasks[taskId].tInput = 0;
gTasks[taskId].tDigit = 0;
- Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_IVS);
gTasks[taskId].func = DebugAction_Give_Pokemon_SelectIVs;
}
else if (JOY_NEW(B_BUTTON))
@@ -2617,7 +2618,7 @@ static void DebugAction_Give_Pokemon_SelectIVs(u8 taskId)
{
PlaySE(SE_SELECT);
Debug_HandleInput_Numeric(taskId, 0, MAX_PER_STAT_IVS, 3);
- Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_IVS);
}
//If A or B button
@@ -2633,7 +2634,7 @@ static void DebugAction_Give_Pokemon_SelectIVs(u8 taskId)
gTasks[taskId].tInput = 0;
gTasks[taskId].tDigit = 0;
- Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_IVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_IVS);
gTasks[taskId].func = DebugAction_Give_Pokemon_SelectIVs;
}
else
@@ -2642,7 +2643,7 @@ static void DebugAction_Give_Pokemon_SelectIVs(u8 taskId)
gTasks[taskId].tDigit = 0;
gTasks[taskId].tIterator = 0;
- Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS);
gTasks[taskId].func = DebugAction_Give_Pokemon_SelectEVs;
}
}
@@ -2690,7 +2691,7 @@ static void DebugAction_Give_Pokemon_SelectEVs(u8 taskId)
{
PlaySE(SE_SELECT);
Debug_HandleInput_Numeric(taskId, 0, MAX_PER_STAT_EVS, 4);
- Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS);
}
//If A or B button
@@ -2705,7 +2706,7 @@ static void DebugAction_Give_Pokemon_SelectEVs(u8 taskId)
gTasks[taskId].tIterator++;
gTasks[taskId].tInput = 0;
gTasks[taskId].tDigit = 0;
- Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS);
gTasks[taskId].func = DebugAction_Give_Pokemon_SelectEVs;
}
else
@@ -2722,7 +2723,7 @@ static void DebugAction_Give_Pokemon_SelectEVs(u8 taskId)
}
PlaySE(SE_FAILURE);
- Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId);
+ Debug_Display_StatInfo(sDebugText_EVs, gTasks[taskId].tIterator, gTasks[taskId].tInput, gTasks[taskId].tDigit, gTasks[taskId].tSubWindowId, MAX_PER_STAT_EVS);
gTasks[taskId].func = DebugAction_Give_Pokemon_SelectEVs;
}
else
@@ -2853,6 +2854,10 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu
//Moves
for (i = 0; i < MAX_MON_MOVES; i++)
{
+ // Non-default moveset chosen. Reset moves before setting the chosen moves.
+ if (moves[0] != MOVE_NONE)
+ SetMonMoveSlot(&mon, MOVE_NONE, i);
+
if (moves[i] == MOVE_NONE || moves[i] >= MOVES_COUNT)
continue;
@@ -3151,12 +3156,10 @@ static void DebugAction_PCBag_Fill_PocketItems(u8 taskId)
static void DebugAction_PCBag_Fill_PocketPokeBalls(u8 taskId)
{
- u16 ballId;
-
- for (ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++)
+ for (enum PokeBall ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++)
{
if (CheckBagHasSpace(ballId, MAX_BAG_ITEM_CAPACITY))
- AddBagItem(ballId, MAX_BAG_ITEM_CAPACITY);
+ AddBagItem(gBallItemIds[ballId], MAX_BAG_ITEM_CAPACITY);
}
}
diff --git a/src/easy_chat.c b/src/easy_chat.c
index fbe9fc18b0..25cc80cfbf 100644
--- a/src/easy_chat.c
+++ b/src/easy_chat.c
@@ -5851,16 +5851,23 @@ static u8 IsEasyChatWordUnlocked(u16 easyChatWord)
void InitializeEasyChatWordArray(u16 *words, u16 length)
{
u16 i;
- for (i = length - 1; i != EC_EMPTY_WORD; i--)
- *(words++) = EC_EMPTY_WORD;
+ if (words != NULL)
+ {
+ for (i = length - 1; i != EC_EMPTY_WORD; i--)
+ *(words++) = EC_EMPTY_WORD;
+ }
}
void InitQuestionnaireWords(void)
{
int i;
u16 *words = GetQuestionnaireWordsPtr();
- for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++)
- words[i] = EC_EMPTY_WORD;
+
+ if (words != NULL)
+ {
+ for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++)
+ words[i] = EC_EMPTY_WORD;
+ }
}
bool32 IsEasyChatAnswerUnlocked(int easyChatWord)
diff --git a/src/field_message_box.c b/src/field_message_box.c
index bf9ce1aa93..a87745f76c 100755
--- a/src/field_message_box.c
+++ b/src/field_message_box.c
@@ -39,9 +39,9 @@ static void Task_DrawFieldMessage(u8 taskId)
task->tState++;
break;
case 1:
- DrawDialogueFrame(0, TRUE);
- task->tState++;
- break;
+ DrawDialogueFrame(0, TRUE);
+ task->tState++;
+ break;
case 2:
if (RunTextPrintersAndIsPrinter0Active() != TRUE)
{
diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c
index 55542a3588..bb9723443d 100644
--- a/src/field_player_avatar.c
+++ b/src/field_player_avatar.c
@@ -1197,17 +1197,18 @@ void PlayerSetAnimId(u8 movementActionId, u8 copyableMovement)
// slow stairs (from FRLG--faster than slow)
static void PlayerWalkSlowStairs(u8 direction)
{
- PlayerSetAnimId(GetWalkSlowStairsMovementAction(direction), 2);
+ PlayerSetAnimId(GetWalkSlowStairsMovementAction(direction), COPY_MOVE_WALK);
}
// slow
static void UNUSED PlayerWalkSlow(u8 direction)
{
- PlayerSetAnimId(GetWalkSlowMovementAction(direction), 2);
+ PlayerSetAnimId(GetWalkSlowMovementAction(direction), COPY_MOVE_WALK);
}
+
static void PlayerRunSlow(u8 direction)
{
- PlayerSetAnimId(GetPlayerRunSlowMovementAction(direction), 2);
+ PlayerSetAnimId(GetPlayerRunSlowMovementAction(direction), COPY_MOVE_WALK);
}
// normal speed (1 speed)
@@ -1239,7 +1240,7 @@ static void PlayerRun(u8 direction)
void PlayerOnBikeCollide(u8 direction)
{
PlayCollisionSoundIfNotFacingWarp(direction);
- PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_WALK);
+ PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_FACE);
// Edge case: If the player stops at the top of a mud slide, but the NPC follower is still on a mud slide tile,
// move the follower into the player and hide them.
if (PlayerHasFollowerNPC())
@@ -1260,18 +1261,18 @@ void PlayerOnBikeCollide(u8 direction)
void PlayerOnBikeCollideWithFarawayIslandMew(u8 direction)
{
- PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_WALK);
+ PlayerSetAnimId(GetWalkInPlaceNormalMovementAction(direction), COPY_MOVE_FACE);
}
static void PlayerNotOnBikeCollide(u8 direction)
{
PlayCollisionSoundIfNotFacingWarp(direction);
- PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_WALK);
+ PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_FACE);
}
static void PlayerNotOnBikeCollideWithFarawayIslandMew(u8 direction)
{
- PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_WALK);
+ PlayerSetAnimId(GetWalkInPlaceSlowMovementAction(direction), COPY_MOVE_FACE);
}
void PlayerFaceDirection(u8 direction)
diff --git a/src/follower_npc.c b/src/follower_npc.c
index 11b066c12c..8bc3a61697 100644
--- a/src/follower_npc.c
+++ b/src/follower_npc.c
@@ -187,6 +187,7 @@ static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, c
u32 npcY = gObjectEvents[eventObjId].currentCoords.y;
const u8 *script;
u32 flag;
+ u16 facingDirection = gObjectEvents[eventObjId].facingDirection;
flag = GetObjectEventFlagIdByLocalIdAndMap(localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
// If the object does not have an event flag, don't create follower.
@@ -210,7 +211,7 @@ static void TurnNPCIntoFollower(u32 localId, u32 followerFlags, u32 setScript, c
SetFollowerNPCData(FNPC_DATA_OBJ_ID, TrySpawnObjectEventTemplate(&npc, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, npcX, npcY));
follower = &gObjectEvents[GetFollowerNPCData(FNPC_DATA_OBJ_ID)];
MoveObjectEventToMapCoords(follower, npcX, npcY);
- ObjectEventTurn(follower, gObjectEvents[eventObjId].facingDirection);
+ ObjectEventTurn(follower, facingDirection);
follower->movementType = MOVEMENT_TYPE_NONE;
gSprites[follower->spriteId].callback = MovementType_None;
diff --git a/src/item.c b/src/item.c
index a389efd83c..908e2814be 100644
--- a/src/item.c
+++ b/src/item.c
@@ -237,9 +237,9 @@ bool32 HasAtLeastOneBerry(void)
bool32 HasAtLeastOnePokeBall(void)
{
- for (u32 ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++)
+ for (enum PokeBall ballId = BALL_STRANGE; ballId < POKEBALL_COUNT; ballId++)
{
- if (CheckBagHasItem(ballId, 1) == TRUE)
+ if (CheckBagHasItem(gBallItemIds[ballId], 1) == TRUE)
return TRUE;
}
return FALSE;
diff --git a/src/overworld.c b/src/overworld.c
index f9c949a571..8c2eaf5d88 100644
--- a/src/overworld.c
+++ b/src/overworld.c
@@ -1558,7 +1558,7 @@ const struct BlendSettings gTimeOfDayBlend[] =
};
#define DEFAULT_WEIGHT 256
-#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - (DEFAULT_WEIGHT * SAFE_DIV(((hours - begin) * MINUTES_PER_HOUR + minutes), ((end - begin) * MINUTES_PER_HOUR))))
+#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - SAFE_DIV((DEFAULT_WEIGHT * ((hours - begin) * MINUTES_PER_HOUR + minutes)), ((end - begin) * MINUTES_PER_HOUR)))
#define MORNING_HOUR_MIDDLE (MORNING_HOUR_BEGIN + ((MORNING_HOUR_END - MORNING_HOUR_BEGIN) / 2))
diff --git a/src/party_menu.c b/src/party_menu.c
index f22b85675a..c37a5e5f92 100644
--- a/src/party_menu.c
+++ b/src/party_menu.c
@@ -5230,13 +5230,13 @@ static void ShowMoveSelectWindow(u8 slot)
{
u8 i;
u8 moveCount = 0;
- u8 fontId = FONT_NORMAL;
u8 windowId = DisplaySelectionWindow(SELECTWINDOW_MOVES);
u16 move;
for (i = 0; i < MAX_MON_MOVES; i++)
{
move = GetMonData(&gPlayerParty[slot], MON_DATA_MOVE1 + i);
+ u8 fontId = GetFontIdToFit(GetMoveName(move), FONT_NORMAL, 0, 72);
AddTextPrinterParameterized(windowId, fontId, GetMoveName(move), 8, (i * 16) + 1, TEXT_SKIP_DRAW, NULL);
if (move != MOVE_NONE)
moveCount++;
@@ -6113,13 +6113,14 @@ void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task)
#define tAnimWait data[2]
#define tNextFunc 3
-#define fusionType data[7]
-#define firstFusion data[8]
-#define firstFusionSlot data[9]
-#define fusionResult data[10]
-#define secondFusionSlot data[11]
-#define unfuseSecondMon data[12]
-#define moveToLearn data[13]
+#define fusionType data[6]
+#define firstFusion data[7]
+#define firstFusionSlot data[8]
+#define fusionResult data[9]
+#define secondFusionSlot data[10]
+#define unfuseSecondMon data[11]
+#define moveToLearn data[12]
+#define tExtraMoveHandling data[13]
#define forgetMove data[14]
#define storageIndex data[15]
@@ -6254,6 +6255,75 @@ static void RestoreFusionMon(struct Pokemon *mon)
}
}
+static void DeleteInvalidFusionMoves(struct Pokemon *mon, u32 species)
+{
+ for (u32 i = 0; i < MAX_MON_MOVES; i++)
+ {
+ u32 move = GetMonData(mon, MON_DATA_MOVE1 + i);
+ bool32 toDelete = TRUE;
+ const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species);
+ for (u32 j = 0; learnset[j].move != LEVEL_UP_MOVE_END;j++)
+ {
+ if (learnset[j].move == move)
+ {
+ toDelete = FALSE;
+ break;
+ }
+ }
+ if (!toDelete)
+ continue;
+ const u16 *learnset2 = GetSpeciesTeachableLearnset(species);
+ for (u32 j = 0; learnset2[j] != MOVE_UNAVAILABLE;j++)
+ {
+ if (learnset2[j] == move)
+ {
+ toDelete = FALSE;
+ break;
+ }
+ }
+ if (!toDelete)
+ continue;
+ const u16 *learnset3 = GetSpeciesEggMoves(species);
+ for (u32 j = 0; learnset3[j] != MOVE_UNAVAILABLE;j++)
+ {
+ if (learnset3[j] == move)
+ {
+ toDelete = FALSE;
+ break;
+ }
+ }
+ if (toDelete)
+ DeleteMove(mon, move);
+ }
+}
+
+static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u32 mode)
+{
+ u32 oldMoveIndex, newMoveIndex;
+ if (mode == FUSE_MON)
+ {
+ oldMoveIndex = 0;
+ newMoveIndex = 1;
+ }
+ else //mode == UNFUSE_MON
+ {
+ oldMoveIndex = 1;
+ newMoveIndex = 0;
+ }
+ for (u32 i = 0; i < MAX_MON_MOVES; i++)
+ {
+ u32 move = GetMonData(mon, MON_DATA_MOVE1 + i);
+ for (u32 j = 0; j < 2; j++)
+ {
+ if (move == moveTable[j][oldMoveIndex])
+ {
+ SetMonData(mon, MON_DATA_MOVE1 + i, &moveTable[j][newMoveIndex]);
+ SetMonData(mon, MON_DATA_PP1 + i, &gMovesInfo[moveTable[j][newMoveIndex]].pp);
+ }
+ }
+ }
+
+}
static void Task_TryItemUseFusionChange(u8 taskId)
{
struct Pokemon *mon = &gPlayerParty[gTasks[taskId].firstFusionSlot];
@@ -6345,15 +6415,38 @@ static void Task_TryItemUseFusionChange(u8 taskId)
case 6:
if (!IsPartyMenuTextPrinterActive())
{
- if (gTasks[taskId].moveToLearn != 0)
+ if (gTasks[taskId].fusionType == FUSE_MON)
{
- if (gTasks[taskId].fusionType == FUSE_MON)
+#if P_FAMILY_KYUREM
+#if P_FAMILY_RESHIRAM
+ if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
+ SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, FUSE_MON);
+#endif //P_FAMILY_RESHIRAM
+#if P_FAMILY_ZEKROM
+ if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK)
+ SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, FUSE_MON);
+#endif //P_FAMILY_ZEKROM
+#endif //P_FAMILY_KYUREM
+ if (gTasks[taskId].moveToLearn != 0)
FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot);
- else
+ }
+ else //(gTasks[taskId].fusionType == UNFUSE_MON)
+ {
+#if P_FAMILY_KYUREM
+#if P_FAMILY_RESHIRAM
+ if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
+ SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, UNFUSE_MON);
+#endif //P_FAMILY_RESHIRAM
+#if P_FAMILY_ZEKROM
+ if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK)
+ SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, UNFUSE_MON);
+#endif //P_FAMILY_ZEKROM
+#endif //P_FAMILY_KYUREM
+ if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES)
{
- DeleteMove(mon, gTasks[taskId].forgetMove);
+ DeleteInvalidFusionMoves(mon, gTasks[taskId].fusionResult);
if (!DoesMonHaveAnyMoves(mon))
- FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot);
+ FormChangeTeachMove(taskId, MOVE_CONFUSION, gTasks[taskId].firstFusionSlot);
}
}
gTasks[taskId].tState++;
@@ -6400,7 +6493,7 @@ void ItemUseCB_Fusion(u8 taskId, TaskFunc taskFunc)
task->storageIndex = itemFusion[i].fusionStorageIndex;
task->fusionResult = itemFusion[i].targetSpecies1;
task->unfuseSecondMon = itemFusion[i].targetSpecies2;
- task->moveToLearn = itemFusion[i].unfuseForgetMove;
+ task->tExtraMoveHandling = itemFusion[i].extraMoveHandling;
task->forgetMove = itemFusion[i].fusionMove;
TryItemUseFusionChange(taskId, taskFunc);
return;
@@ -6440,6 +6533,7 @@ void ItemUseCB_Fusion(u8 taskId, TaskFunc taskFunc)
task->fusionResult = itemFusion[i].fusingIntoMon;
task->secondFusionSlot = gPartyMenu.slotId;
task->moveToLearn = itemFusion[i].fusionMove;
+ task->tExtraMoveHandling = itemFusion[i].extraMoveHandling;
// Start Fusion
TryItemUseFusionChange(taskId, taskFunc);
return;
@@ -8025,4 +8119,3 @@ static void FieldCallback_RockClimb(void)
gFieldEffectArguments[0] = GetCursorSelectionMonId();
FieldEffectStart(FLDEFF_USE_ROCK_CLIMB);
}
-
diff --git a/src/pokeball.c b/src/pokeball.c
index 5608de9647..5b32aa7e1d 100644
--- a/src/pokeball.c
+++ b/src/pokeball.c
@@ -544,6 +544,38 @@ const struct SpriteTemplate gBallSpriteTemplates[POKEBALL_COUNT] =
#define tBattler data[3]
#define tOpponentBattler data[4]
+const u16 gBallItemIds[POKEBALL_COUNT] =
+{
+ [BALL_STRANGE] = ITEM_STRANGE_BALL,
+ [BALL_POKE] = ITEM_POKE_BALL,
+ [BALL_GREAT] = ITEM_GREAT_BALL,
+ [BALL_ULTRA] = ITEM_ULTRA_BALL,
+ [BALL_MASTER] = ITEM_MASTER_BALL,
+ [BALL_PREMIER] = ITEM_PREMIER_BALL,
+ [BALL_HEAL] = ITEM_HEAL_BALL,
+ [BALL_NET] = ITEM_NET_BALL,
+ [BALL_NEST] = ITEM_NEST_BALL,
+ [BALL_DIVE] = ITEM_DIVE_BALL,
+ [BALL_DUSK] = ITEM_DUSK_BALL,
+ [BALL_TIMER] = ITEM_TIMER_BALL,
+ [BALL_QUICK] = ITEM_QUICK_BALL,
+ [BALL_REPEAT] = ITEM_REPEAT_BALL,
+ [BALL_LUXURY] = ITEM_LUXURY_BALL,
+ [BALL_LEVEL] = ITEM_LEVEL_BALL,
+ [BALL_LURE] = ITEM_LURE_BALL,
+ [BALL_MOON] = ITEM_MOON_BALL,
+ [BALL_FRIEND] = ITEM_FRIEND_BALL,
+ [BALL_LOVE] = ITEM_LOVE_BALL,
+ [BALL_FAST] = ITEM_FAST_BALL,
+ [BALL_HEAVY] = ITEM_HEAVY_BALL,
+ [BALL_DREAM] = ITEM_DREAM_BALL,
+ [BALL_SAFARI] = ITEM_SAFARI_BALL,
+ [BALL_SPORT] = ITEM_SPORT_BALL,
+ [BALL_PARK] = ITEM_PARK_BALL,
+ [BALL_BEAST] = ITEM_BEAST_BALL,
+ [BALL_CHERISH] = ITEM_CHERISH_BALL,
+};
+
u8 DoPokeballSendOutAnimation(u32 battler, s16 pan, u8 kindOfThrow)
{
u8 taskId;
diff --git a/src/pokedex.c b/src/pokedex.c
index 39add53440..3c5fce0474 100644
--- a/src/pokedex.c
+++ b/src/pokedex.c
@@ -1,5 +1,6 @@
#include "global.h"
#include "battle_main.h"
+#include "battle_script_commands.h"
#include "bg.h"
#include "data.h"
#include "decompress.h"
@@ -4040,8 +4041,8 @@ static void HighlightSubmenuScreenSelectBarItem(u8 a, u16 b)
#define tPalTimer data[2]
#define tMonSpriteId data[3]
#define tIsShiny data[13]
-#define tPersonalityLo data[14]
-#define tPersonalityHi data[15]
+#define tPersonalityLo 14
+#define tPersonalityHi 15
u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality)
{
@@ -4054,11 +4055,29 @@ u8 DisplayCaughtMonDexPage(u16 species, bool32 isShiny, u32 personality)
gTasks[taskId].tState = 0;
gTasks[taskId].tSpecies = species;
gTasks[taskId].tIsShiny = isShiny;
- gTasks[taskId].tPersonalityLo = personality;
- gTasks[taskId].tPersonalityHi = personality >> 16;
+ gTasks[taskId].data[tPersonalityLo] = personality;
+ gTasks[taskId].data[tPersonalityHi] = personality >> 16;
return taskId;
}
+static void LoadDexMonPalette(u32 taskId, bool32 isShiny)
+{
+ const u16 *paletteData = GetMonSpritePalFromSpeciesAndPersonality(gTasks[taskId].tSpecies, isShiny, GetWordTaskArg(taskId, tPersonalityLo));
+ u32 paletteNum = gSprites[gTasks[taskId].tMonSpriteId].oam.paletteNum;
+ LoadPalette(paletteData, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP);
+}
+
+u32 Pokedex_CreateCaughtMonSprite(u32 species, s32 x, s32 y)
+{
+ u32 spriteId;
+
+ SetMultiuseSpriteTemplateToPokemon(species, GetCatchingBattler());
+ spriteId = CreateSprite(&gMultiuseSpriteTemplate, x, y, 0);
+ gSprites[spriteId].oam.priority = 0;
+ gSprites[spriteId].callback = SpriteCallbackDummy;
+ return spriteId;
+}
+
static void Task_DisplayCaughtMonDexPage(u8 taskId)
{
u8 spriteId;
@@ -4106,11 +4125,13 @@ static void Task_DisplayCaughtMonDexPage(u8 taskId)
gTasks[taskId].tState++;
break;
case 4:
- spriteId = CreateMonPicSprite(species, FALSE, ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo, TRUE, MON_PAGE_X, MON_PAGE_Y, 0, TAG_NONE);
+ // We're using a different mon sprite creation method, because we don't have enough memory to safely use CreateMonPicSprite.
+ spriteId = Pokedex_CreateCaughtMonSprite(species, MON_PAGE_X, MON_PAGE_Y);
+ gTasks[taskId].tMonSpriteId = spriteId;
+ LoadDexMonPalette(taskId, FALSE);
gSprites[spriteId].oam.priority = 0;
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
SetVBlankCallback(gPokedexVBlankCB);
- gTasks[taskId].tMonSpriteId = spriteId;
gTasks[taskId].tState++;
break;
case 5:
@@ -4157,9 +4178,6 @@ static void Task_ExitCaughtMonPage(u8 taskId)
if (!gPaletteFade.active)
{
bool32 isShiny;
- u32 personality;
- u8 paletteNum;
- const u16 *paletteData;
void *buffer;
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON);
@@ -4172,10 +4190,7 @@ static void Task_ExitCaughtMonPage(u8 taskId)
Free(buffer);
isShiny = (bool8)gTasks[taskId].tIsShiny;
- personality = ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo;
- paletteNum = gSprites[gTasks[taskId].tMonSpriteId].oam.paletteNum;
- paletteData = GetMonSpritePalFromSpeciesAndPersonality(gTasks[taskId].tSpecies, isShiny, personality);
- LoadPalette(paletteData, OBJ_PLTT_ID(paletteNum), PLTT_SIZE_4BPP);
+ LoadDexMonPalette(taskId, isShiny);
DestroyTask(taskId);
}
}
diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c
index 3ad02be310..dc29ce4b8f 100644
--- a/src/pokedex_plus_hgss.c
+++ b/src/pokedex_plus_hgss.c
@@ -4150,13 +4150,18 @@ void Task_DisplayCaughtMonDexPageHGSS(u8 taskId)
gTasks[taskId].tState++;
break;
case 4:
- spriteId = CreateMonSpriteFromNationalDexNumberHGSS(dexNum, MON_PAGE_X, MON_PAGE_Y, 0);
- gSprites[spriteId].oam.priority = 0;
+ {
+ u32 personality = ((u16)gTasks[taskId].tPersonalityHi << 16) | (u16)gTasks[taskId].tPersonalityLo;
+ const u16 *paletteData = GetMonSpritePalFromSpeciesAndPersonality(species, FALSE, personality);
+
+ spriteId = Pokedex_CreateCaughtMonSprite(species, MON_PAGE_X, MON_PAGE_Y);
+ LoadPalette(paletteData, OBJ_PLTT_ID(gSprites[spriteId].oam.paletteNum), PLTT_SIZE_4BPP);
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
SetVBlankCallback(gPokedexVBlankCB);
gTasks[taskId].tMonSpriteId = spriteId;
gTasks[taskId].tState++;
break;
+ }
case 5:
SetGpuReg(REG_OFFSET_BLDCNT, 0);
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
@@ -4534,8 +4539,8 @@ static u32 GetPokedexMonPersonality(u16 species)
static u16 CreateMonSpriteFromNationalDexNumberHGSS(u16 nationalNum, s16 x, s16 y, u16 paletteSlot)
{
- nationalNum = NationalPokedexNumToSpeciesHGSS(nationalNum);
- return CreateMonPicSprite(nationalNum, FALSE, GetPokedexMonPersonality(nationalNum), TRUE, x, y, paletteSlot, TAG_NONE);
+ u32 species = NationalPokedexNumToSpeciesHGSS(nationalNum);
+ return CreateMonPicSprite(nationalNum, FALSE, GetPokedexMonPersonality(species), TRUE, x, y, paletteSlot, TAG_NONE);
}
static u16 GetPokemonScaleFromNationalDexNumber(u16 nationalNum)
diff --git a/src/pokemon.c b/src/pokemon.c
index 655323d104..b6529ad615 100644
--- a/src/pokemon.c
+++ b/src/pokemon.c
@@ -4836,7 +4836,7 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16
case EVO_MODE_ITEM_CHECK:
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
{
- bool32 conditionMet = FALSE;
+ bool32 conditionsMet = FALSE;
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
continue;
@@ -4844,11 +4844,11 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16
{
case EVO_ITEM:
if (evolutions[i].param == evolutionItem)
- conditionMet = TRUE;
+ conditionsMet = TRUE;
break;
}
- if (conditionMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, PARTY_SIZE, canStopEvo, evoState))
+ if (conditionsMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, PARTY_SIZE, canStopEvo, evoState))
{
// All checks passed, so stop checking the rest of the evolutions.
// This is different from vanilla where the loop continues.
@@ -4870,9 +4870,9 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16
switch (evolutions[i].method)
{
- case EVO_BATTLE_END:
- conditionsMet = TRUE;
- break;
+ case EVO_BATTLE_END:
+ conditionsMet = TRUE;
+ break;
}
if (conditionsMet && DoesMonMeetAdditionalConditions(mon, evolutions[i].params, NULL, evolutionItem, canStopEvo, evoState))
@@ -4898,7 +4898,6 @@ u32 GetEvolutionTargetSpecies(struct Pokemon *mon, enum EvolutionMode mode, u16
case EVO_SPIN:
if (gSpecialVar_0x8000 == evolutions[i].param)
conditionsMet = TRUE;
-
break;
}
diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c
index 359ab75e41..d4fba87859 100644
--- a/src/script_pokemon_util.c
+++ b/src/script_pokemon_util.c
@@ -332,7 +332,7 @@ void SetTeraType(struct ScriptContext *ctx)
* if side/slot are assigned, it will create the mon at the assigned party location
* if slot == PARTY_SIZE, it will give the mon to first available party or storage slot
*/
-static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, enum PokeBall ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, bool8 isShiny, bool8 gmaxFactor, u8 teraType, u8 dmaxLevel)
+static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u16 item, enum PokeBall ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, enum ShinyMode shinyMode, bool8 gmaxFactor, u8 teraType, u8 dmaxLevel)
{
enum NationalDexOrder nationalDexNum;
int sentToPc;
@@ -340,6 +340,7 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
u32 i;
u8 genderRatio = gSpeciesInfo[species].genderRatio;
u16 targetSpecies;
+ bool32 isShiny;
// check whether to use a specific nature or a random one
if (nature >= NUM_NATURES)
@@ -360,10 +361,13 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u
CreateMonWithNature(&mon, species, level, 32, nature);
// shininess
- if (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY))
+ if (shinyMode == SHINY_MODE_ALWAYS || (P_FLAG_FORCE_SHINY != 0 && FlagGet(P_FLAG_FORCE_SHINY)))
isShiny = TRUE;
- else if (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY))
+ else if (shinyMode == SHINY_MODE_NEVER || (P_FLAG_FORCE_NO_SHINY != 0 && FlagGet(P_FLAG_FORCE_NO_SHINY)))
isShiny = FALSE;
+ else
+ isShiny = GetMonData(&mon, MON_DATA_IS_SHINY);
+
SetMonData(&mon, MON_DATA_IS_SHINY, &isShiny);
// gigantamax factor
@@ -479,7 +483,7 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item)
MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV.
u16 moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE};
- return ScriptGiveMonParameterized(0, PARTY_SIZE, species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES, 0);
+ return ScriptGiveMonParameterized(0, PARTY_SIZE, species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, SHINY_MODE_RANDOM, FALSE, NUMBER_OF_MON_TYPES, 0);
}
#define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_)
@@ -542,20 +546,20 @@ void ScrCmd_createmon(struct ScriptContext *ctx)
}
}
}
- hpIv = PARSE_FLAG(11, hpIv);
- atkIv = PARSE_FLAG(12, atkIv);
- defIv = PARSE_FLAG(13, defIv);
- speedIv = PARSE_FLAG(14, speedIv);
- spAtkIv = PARSE_FLAG(15, spAtkIv);
- spDefIv = PARSE_FLAG(16, spDefIv);
- u16 move1 = PARSE_FLAG(17, MOVE_NONE);
- u16 move2 = PARSE_FLAG(18, MOVE_NONE);
- u16 move3 = PARSE_FLAG(19, MOVE_NONE);
- u16 move4 = PARSE_FLAG(20, MOVE_NONE);
- bool8 isShiny = PARSE_FLAG(21, FALSE);
- bool8 gmaxFactor = PARSE_FLAG(22, FALSE);
- u8 teraType = PARSE_FLAG(23, NUMBER_OF_MON_TYPES);
- u8 dmaxLevel = PARSE_FLAG(24, 0);
+ hpIv = PARSE_FLAG(11, hpIv);
+ atkIv = PARSE_FLAG(12, atkIv);
+ defIv = PARSE_FLAG(13, defIv);
+ speedIv = PARSE_FLAG(14, speedIv);
+ spAtkIv = PARSE_FLAG(15, spAtkIv);
+ spDefIv = PARSE_FLAG(16, spDefIv);
+ u16 move1 = PARSE_FLAG(17, MOVE_NONE);
+ u16 move2 = PARSE_FLAG(18, MOVE_NONE);
+ u16 move3 = PARSE_FLAG(19, MOVE_NONE);
+ u16 move4 = PARSE_FLAG(20, MOVE_NONE);
+ enum ShinyMode shinyMode = PARSE_FLAG(21, SHINY_MODE_RANDOM);
+ bool8 gmaxFactor = PARSE_FLAG(22, FALSE);
+ u8 teraType = PARSE_FLAG(23, NUMBER_OF_MON_TYPES);
+ u8 dmaxLevel = PARSE_FLAG(24, 0);
u8 evs[NUM_STATS] = {hpEv, atkEv, defEv, speedEv, spAtkEv, spDefEv};
u8 ivs[NUM_STATS] = {hpIv, atkIv, defIv, speedIv, spAtkIv, spDefIv};
@@ -566,7 +570,7 @@ void ScrCmd_createmon(struct ScriptContext *ctx)
else
Script_RequestEffects(SCREFF_V1);
- gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, isShiny, gmaxFactor, teraType, dmaxLevel);
+ gSpecialVar_Result = ScriptGiveMonParameterized(side, slot, species, level, item, ball, nature, abilityNum, gender, evs, ivs, moves, shinyMode, gmaxFactor, teraType, dmaxLevel);
}
#undef PARSE_FLAG
diff --git a/src/window.c b/src/window.c
index 65419ff77b..51b473e89c 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5,14 +5,10 @@
#include "blit.h"
#include "decompress.h"
-// This global is set to 0 and never changed.
-COMMON_DATA u8 gTransparentTileNumber = 0;
COMMON_DATA void *gWindowBgTilemapBuffers[NUM_BACKGROUNDS] = {0};
extern u32 gWindowTileAutoAllocEnabled;
EWRAM_DATA struct Window gWindows[WINDOWS_MAX] = {0};
-EWRAM_DATA static struct Window *sWindowPtr = NULL;
-EWRAM_DATA static u16 sWindowSize = 0;
static u32 GetNumActiveWindowsOnBg(u32 bgId);
static u32 GetNumActiveWindowsOnBg8Bit(u32 bgId);
@@ -103,7 +99,6 @@ bool32 InitWindows(const struct WindowTemplate *templates)
}
}
- gTransparentTileNumber = 0;
return TRUE;
}
@@ -375,7 +370,7 @@ void ClearWindowTilemap(u32 windowId)
FillBgTilemapBufferRect(
windowLocal.window.bg,
- gTransparentTileNumber,
+ 0,
windowLocal.window.tilemapLeft,
windowLocal.window.tilemapTop,
windowLocal.window.width,
@@ -698,20 +693,20 @@ void BlitBitmapRectToWindow4BitTo8Bit(u32 windowId, const u8 *pixels, u16 srcX,
void CopyWindowToVram8Bit(u32 windowId, u8 mode)
{
- sWindowPtr = &gWindows[windowId];
- sWindowSize = 64 * (sWindowPtr->window.width * sWindowPtr->window.height);
+ struct Window *window = &gWindows[windowId];
+ u16 windowSize = 64 * (window->window.width * window->window.height);
switch (mode)
{
case COPYWIN_MAP:
- CopyBgTilemapBufferToVram(sWindowPtr->window.bg);
+ CopyBgTilemapBufferToVram(window->window.bg);
break;
case COPYWIN_GFX:
- LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock);
+ LoadBgTiles(window->window.bg, window->tileData, windowSize, window->window.baseBlock);
break;
case COPYWIN_FULL:
- LoadBgTiles(sWindowPtr->window.bg, sWindowPtr->tileData, sWindowSize, sWindowPtr->window.baseBlock);
- CopyBgTilemapBufferToVram(sWindowPtr->window.bg);
+ LoadBgTiles(window->window.bg, window->tileData, windowSize, window->window.baseBlock);
+ CopyBgTilemapBufferToVram(window->window.bg);
break;
}
}
diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c
index e097035f62..6ea5d9537b 100644
--- a/test/battle/ability/color_change.c
+++ b/test/battle/ability/color_change.c
@@ -153,3 +153,23 @@ SINGLE_BATTLE_TEST("Color Change changes the type to Normal when a Pokemon is hi
MESSAGE("The opposing Kecleon's Color Change made it the Normal type!");
}
}
+
+SINGLE_BATTLE_TEST("Color Change does not change the type to Normal when a Pokemon is hit by Struggle")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SOAK); }
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ TURN { }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SOAK, player);
+ MESSAGE("The opposing Kecleon transformed into the Water type!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE);
+ MESSAGE("The opposing Kecleon's Color Change made it the Normal type!");
+ }
+ }
+}
diff --git a/test/battle/ability/immunity.c b/test/battle/ability/immunity.c
index 88ff45d2a4..e18aef667c 100644
--- a/test/battle/ability/immunity.c
+++ b/test/battle/ability/immunity.c
@@ -63,3 +63,19 @@ SINGLE_BATTLE_TEST("Immunity doesn't prevent Pokémon from being poisoned by Tox
NOT HP_BAR(player);
}
}
+
+SINGLE_BATTLE_TEST("Immunity cures existing poison on turn 0")
+{
+ GIVEN {
+ PLAYER(SPECIES_ZANGOOSE) {
+ Ability(ABILITY_IMMUNITY);
+ Status1(STATUS1_POISON);
+ }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_IMMUNITY);
+ TURN { MOVE(player, MOVE_SPLASH); }
+ } THEN {
+ EXPECT_EQ(player->status1, STATUS1_NONE);
+ }
+}
diff --git a/test/battle/ability/oblivious.c b/test/battle/ability/oblivious.c
index 40506e1967..c160ab423e 100644
--- a/test/battle/ability/oblivious.c
+++ b/test/battle/ability/oblivious.c
@@ -31,22 +31,35 @@ SINGLE_BATTLE_TEST("Oblivious prevents Captivate")
}
}
-SINGLE_BATTLE_TEST("Oblivious prevents Taunt")
+SINGLE_BATTLE_TEST("Oblivious prevents Taunt (Gen6+)")
{
+ u32 gen = 0;
+ PARAMETRIZE { gen = GEN_5; }
+ PARAMETRIZE { gen = GEN_6; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_OBLIVIOUS_TAUNT, gen);
ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT);
- ASSUME(B_OBLIVIOUS_TAUNT >= GEN_6);
PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TAUNT); }
- TURN { MOVE(player, MOVE_SPORE); }
+ TURN { MOVE(player, MOVE_SPORE, allowed: gen == GEN_6); }
} SCENE {
- ABILITY_POPUP(player, ABILITY_OBLIVIOUS);
- NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); }
- MESSAGE("It doesn't affect Slowpoke…");
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player);
- ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent);
+ if (gen == GEN_6) {
+ NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent); }
+ ABILITY_POPUP(player, ABILITY_OBLIVIOUS);
+ MESSAGE("It doesn't affect Slowpoke…");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player);
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent);
+ } else {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TAUNT, opponent);
+ NONE_OF {
+ ABILITY_POPUP(player, ABILITY_OBLIVIOUS);
+ MESSAGE("It doesn't affect Slowpoke…");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player);
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent);
+ }
+ }
}
}
diff --git a/test/battle/ability/overcoat.c b/test/battle/ability/overcoat.c
index c722d7ac55..6d7b5f2a8d 100644
--- a/test/battle/ability/overcoat.c
+++ b/test/battle/ability/overcoat.c
@@ -1,25 +1,37 @@
#include "global.h"
#include "test/battle.h"
-SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves")
+SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves (Gen6+)")
{
+ u32 gen = 0;
+ PARAMETRIZE { gen = GEN_5; }
+ PARAMETRIZE { gen = GEN_6; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen);
ASSUME(IsPowderMove(MOVE_STUN_SPORE));
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); }
} WHEN {
TURN { MOVE(player, MOVE_STUN_SPORE); }
} SCENE {
- ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
- MESSAGE("It doesn't affect the opposing Pineco…");
+ if (gen == GEN_6) {
+ ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ MESSAGE("It doesn't affect the opposing Pineco…");
+ } else {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
+ MESSAGE("It doesn't affect the opposing Pineco…");
+ }
+ }
}
}
DOUBLE_BATTLE_TEST("Overcoat blocks damage from sandstorm")
{
GIVEN {
- PLAYER(SPECIES_WYNAUT) { Speed(50); }
+ PLAYER(SPECIES_WYNAUT) { Speed(50); }
PLAYER(SPECIES_HELIOLISK) { Speed(40); Ability(ABILITY_SAND_VEIL); }
OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_STARLY) { Speed(20); }
@@ -41,7 +53,7 @@ DOUBLE_BATTLE_TEST("Overcoat blocks damage from hail")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_HAIL) == EFFECT_HAIL);
- PLAYER(SPECIES_WYNAUT) { Speed(50); Ability(ABILITY_SNOW_CLOAK); }
+ PLAYER(SPECIES_WYNAUT) { Speed(50); Ability(ABILITY_SNOW_CLOAK); }
PLAYER(SPECIES_SOLOSIS) { Speed(40); Ability(ABILITY_RUN_AWAY); }
OPPONENT(SPECIES_PINECO) { Speed(30); Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_SNORUNT) { Speed(20); }
@@ -73,4 +85,3 @@ SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect")
EXPECT_EQ(player->status1, 0);
}
}
-
diff --git a/test/battle/ability/parental_bond.c b/test/battle/ability/parental_bond.c
index 6c516c7ffc..b82f16cc26 100644
--- a/test/battle/ability/parental_bond.c
+++ b/test/battle/ability/parental_bond.c
@@ -291,10 +291,10 @@ SINGLE_BATTLE_TEST("Parental Bond Snore strikes twice while asleep")
HP_BAR(opponent, captureDamage: &damage[1]);
MESSAGE("The Pokémon was hit 2 time(s)!");
} THEN {
- if (B_PARENTAL_BOND_DMG == GEN_6)
- EXPECT_MUL_EQ(damage[0], Q_4_12(0.5), damage[1]);
- else
+ if (B_PARENTAL_BOND_DMG >= GEN_7)
EXPECT_MUL_EQ(damage[0], Q_4_12(0.25), damage[1]);
+ else
+ EXPECT_MUL_EQ(damage[0], Q_4_12(0.5), damage[1]);
}
}
diff --git a/test/battle/ability/protean.c b/test/battle/ability/protean.c
index c5d141d244..fe4ae25e60 100644
--- a/test/battle/ability/protean.c
+++ b/test/battle/ability/protean.c
@@ -54,3 +54,19 @@ SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
}
}
+
+SINGLE_BATTLE_TEST("Protean does not change the user's type when using Struggle")
+{
+ GIVEN {
+ PLAYER(SPECIES_REGIROCK);
+ OPPONENT(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_STRUGGLE); }
+ } SCENE {
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_PROTEAN);
+ MESSAGE("The opposing Greninja transformed into the Normal type!");
+ }
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
+ }
+}
diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c
index 6fd271acb2..9bf6d85f43 100644
--- a/test/battle/ability/protosynthesis.c
+++ b/test/battle/ability/protosynthesis.c
@@ -59,6 +59,7 @@ SINGLE_BATTLE_TEST("Protosynthesis ability pop up activates only once during the
u16 turns;
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_ABILITY_WEATHER, GEN_6);
PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); }
OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); };
} WHEN {
diff --git a/test/battle/ability/steadfast.c b/test/battle/ability/steadfast.c
index fc35e94278..2d6fb5d901 100644
--- a/test/battle/ability/steadfast.c
+++ b/test/battle/ability/steadfast.c
@@ -1,4 +1,56 @@
#include "global.h"
#include "test/battle.h"
+SINGLE_BATTLE_TEST("Steadfast boosts Speed when the user attempts to move but is flinched")
+{
+ GIVEN {
+ ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100));
+ PLAYER(SPECIES_LUCARIO) { Ability(ABILITY_STEADFAST); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_STEADFAST);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1);
+ }
+}
+
+SINGLE_BATTLE_TEST("Steadfast doesn't activate if the user wasn't flinched")
+{
+ GIVEN {
+ ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100));
+ ASSUME(GetItemHoldEffect(ITEM_COVERT_CLOAK) == HOLD_EFFECT_COVERT_CLOAK);
+ PLAYER(SPECIES_LUCARIO) { Ability(ABILITY_STEADFAST); Item(ITEM_COVERT_CLOAK); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ NOT ABILITY_POPUP(player, ABILITY_STEADFAST);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Steadfast doesn't activate if the user has already moved")
+{
+ GIVEN {
+ ASSUME(MoveHasAdditionalEffect(MOVE_BITE, MOVE_EFFECT_FLINCH));
+ ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT);
+ PLAYER(SPECIES_LUCARIO) { Ability(ABILITY_STEADFAST); }
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SWORDS_DANCE); MOVE(opponentLeft, MOVE_BITE, target: playerLeft); MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, playerLeft);
+ NOT ABILITY_POPUP(playerLeft, ABILITY_STEADFAST);
+ } THEN {
+ EXPECT_EQ(playerLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ }
+}
+
TO_DO_BATTLE_TEST("TODO: Write Steadfast (Ability) test titles")
diff --git a/test/battle/ability/supersweet_syrup.c b/test/battle/ability/supersweet_syrup.c
index 4ff8c462ba..cd819eda72 100644
--- a/test/battle/ability/supersweet_syrup.c
+++ b/test/battle/ability/supersweet_syrup.c
@@ -56,11 +56,21 @@ SINGLE_BATTLE_TEST("Supersweet Syrup can not further lower opponents evasion if
TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
+ if (GetMoveEffect(MOVE_SWEET_SCENT) == EFFECT_EVASION_DOWN) {
+ TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
+ TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
+ TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
+ }
TURN { SWITCH(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent);
+ if (GetMoveEffect(MOVE_SWEET_SCENT) == EFFECT_EVASION_DOWN) {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent);
+ }
ABILITY_POPUP(opponent, ABILITY_SUPERSWEET_SYRUP);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
diff --git a/test/battle/ai/ai_flag_predict_switch.c b/test/battle/ai/ai_flag_predict_switch.c
index b64dea488c..8530c49675 100644
--- a/test/battle/ai/ai_flag_predict_switch.c
+++ b/test/battle/ai/ai_flag_predict_switch.c
@@ -59,7 +59,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: Considers ShouldSwitch and GetMos
// Switching in trapper is an advanced feature of ShouldSwitch that requires GetMostSuitableMonToSwitchInto to also return a specific mon; this passing means the AI can use both in prediction
PASSES_RANDOMLY(5, 10, RNG_AI_PREDICT_SWITCH);
GIVEN {
- ASSUME(B_POWDER_GRASS >= GEN_6);
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PREDICT_SWITCH | AI_FLAG_PREDICT_INCOMING_MON);
PLAYER(SPECIES_SKARMORY) { Moves(MOVE_SCRATCH); }
PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_ACROBATICS); }
diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c
index b1aad9afb2..14ea727f85 100644
--- a/test/battle/ai/ai_switching.c
+++ b/test/battle/ai/ai_switching.c
@@ -40,6 +40,28 @@ AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill")
}
}
+AI_SINGLE_BATTLE_TEST("AI sees on-field player ability correctly and does not see previous Pokémon's ability after player uses a pivot move when choosing a post-KO switch")
+{
+ u32 testAbility;
+ PARAMETRIZE { testAbility = ABILITY_WATER_ABSORB; }
+ PARAMETRIZE { testAbility = ABILITY_VOLT_ABSORB; }
+ GIVEN {
+ AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
+ PLAYER(SPECIES_PIKACHU) {Level(100); Moves(MOVE_VOLT_SWITCH, MOVE_SPARKLY_SWIRL); Ability(ABILITY_LIGHTNING_ROD); };
+ PLAYER(SPECIES_LANTURN) {Level(44); Moves(MOVE_SCALD); Ability(testAbility); };
+ OPPONENT(SPECIES_SOBBLE) {Level(44); Moves(MOVE_SCRATCH); }
+ OPPONENT(SPECIES_BOMBIRDIER) {Level(42); Moves(MOVE_ROCK_SLIDE); }
+ OPPONENT(SPECIES_IRON_THORNS) {Level(43); Moves(MOVE_SUPERCELL_SLAM, MOVE_ICE_PUNCH); }
+ } WHEN {
+ TURN {
+ MOVE(player, MOVE_VOLT_SWITCH);
+ SEND_OUT(player, 1);
+ EXPECT_MOVE(opponent, MOVE_SCRATCH);
+ testAbility == ABILITY_WATER_ABSORB ? EXPECT_SEND_OUT(opponent, 2) : EXPECT_SEND_OUT(opponent, 1);
+ }
+ }
+}
+
AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same Pokémon for 2 spots in a double battle (all bad moves)")
{
u32 flags;
@@ -1399,3 +1421,20 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: Fake Out style moves won't confu
TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_CLOSE_COMBAT); }
}
}
+
+AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will consider choice-locked player priority when determining which mon to send out")
+{
+ u32 item;
+ PARAMETRIZE { item = ITEM_NONE; }
+ PARAMETRIZE { item = ITEM_CHOICE_BAND; }
+ GIVEN {
+ ASSUME(gItemsInfo[ITEM_CHOICE_BAND].holdEffect == HOLD_EFFECT_CHOICE_BAND);
+ AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
+ PLAYER(SPECIES_LYCANROC) { Speed(5); Moves(MOVE_ACCELEROCK, MOVE_MIGHTY_CLEAVE); Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(4); HP(1); Moves(MOVE_TACKLE); }
+ OPPONENT(SPECIES_DECIDUEYE_HISUI) { Speed(4); Moves(MOVE_LEAF_BLADE); }
+ OPPONENT(SPECIES_PHEROMOSA) { Speed(6); HP(1); Moves(MOVE_EARTHQUAKE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_MIGHTY_CLEAVE); EXPECT_MOVE(opponent, MOVE_TACKLE); item == ITEM_NONE ? EXPECT_SEND_OUT(opponent, 1) : EXPECT_SEND_OUT(opponent, 2); }
+ }
+}
diff --git a/test/battle/damage_formula.c b/test/battle/damage_formula.c
index f41f6a2145..4a7e4ec1ef 100644
--- a/test/battle/damage_formula.c
+++ b/test/battle/damage_formula.c
@@ -101,6 +101,7 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Marshadow vs Mawile)")
PARAMETRIZE { expectedDamage = 123; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_SPECTRAL_THIEF) == DAMAGE_CATEGORY_PHYSICAL);
+ ASSUME(B_UPDATED_TYPE_MATCHUPS >= GEN_6); // Steel resists Ghost in Gen2-5
PLAYER(SPECIES_MARSHADOW) { Level(100); Attack(286); }
OPPONENT(SPECIES_MAWILE) { Level(100); Defense(226); HP(241); }
} WHEN {
@@ -196,6 +197,7 @@ SINGLE_BATTLE_TEST("Gem boosted Damage calculation")
{
s16 dmg;
s16 expectedDamage;
+#if I_GEM_BOOST_POWER >= GEN_6
PARAMETRIZE { expectedDamage = 240; }
PARAMETRIZE { expectedDamage = 237; }
PARAMETRIZE { expectedDamage = 234; }
@@ -212,6 +214,25 @@ SINGLE_BATTLE_TEST("Gem boosted Damage calculation")
PARAMETRIZE { expectedDamage = 208; }
PARAMETRIZE { expectedDamage = 205; }
PARAMETRIZE { expectedDamage = 204; }
+#else
+ KNOWN_FAILING;
+ PARAMETRIZE { expectedDamage = 273; }
+ PARAMETRIZE { expectedDamage = 270; }
+ PARAMETRIZE { expectedDamage = 267; }
+ PARAMETRIZE { expectedDamage = 264; }
+ PARAMETRIZE { expectedDamage = 261; }
+ PARAMETRIZE { expectedDamage = 258; }
+ PARAMETRIZE { expectedDamage = 256; }
+ PARAMETRIZE { expectedDamage = 253; }
+ PARAMETRIZE { expectedDamage = 250; }
+ PARAMETRIZE { expectedDamage = 247; }
+ PARAMETRIZE { expectedDamage = 244; }
+ PARAMETRIZE { expectedDamage = 241; }
+ PARAMETRIZE { expectedDamage = 240; }
+ PARAMETRIZE { expectedDamage = 237; }
+ PARAMETRIZE { expectedDamage = 234; }
+ PARAMETRIZE { expectedDamage = 231; }
+#endif
GIVEN {
PLAYER(SPECIES_MAKUHITA) { Item(ITEM_FIGHTING_GEM); }
OPPONENT(SPECIES_MAKUHITA);
diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c
index 8dfedad83f..bb8814fa42 100644
--- a/test/battle/gimmick/dynamax.c
+++ b/test/battle/gimmick/dynamax.c
@@ -1464,7 +1464,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Chi Strike boosts allies' crit chance by 1 st
{
u32 j;
GIVEN {
- ASSUME(B_CRIT_CHANCE >= GEN_6);
+ WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, GEN_6);
ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_CHI_STRIKE, MOVE_EFFECT_CRIT_PLUS_SIDE));
PLAYER(SPECIES_MACHAMP) { GigantamaxFactor(TRUE); }
PLAYER(SPECIES_MACHOP);
diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c
index 56416663ff..37a6d515b3 100644
--- a/test/battle/gimmick/terastal.c
+++ b/test/battle/gimmick/terastal.c
@@ -606,14 +606,14 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves
s16 damage[4];
GIVEN {
ASSUME(GetMovePower(MOVE_MEGA_DRAIN) == 40);
- ASSUME(GetMovePower(MOVE_BUBBLE) == 40);
+ ASSUME(GetMovePower(MOVE_WATER_GUN) == 40);
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MEGA_DRAIN); }
TURN { MOVE(player, MOVE_MEGA_DRAIN, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_MEGA_DRAIN); }
- TURN { MOVE(player, MOVE_BUBBLE); }
+ TURN { MOVE(player, MOVE_WATER_GUN); }
} SCENE {
// turn 1
MESSAGE("Wobbuffet used Mega Drain!");
@@ -628,8 +628,8 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
HP_BAR(opponent, captureDamage: &damage[2]);
// turn 4
- MESSAGE("Wobbuffet used Bubble!");
- ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player);
+ MESSAGE("Wobbuffet used Water Gun!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
HP_BAR(opponent, captureDamage: &damage[3]);
} THEN {
// The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost.
diff --git a/test/battle/hold_effect/big_root.c b/test/battle/hold_effect/big_root.c
index 41dd09adc0..3d2256ea34 100644
--- a/test/battle/hold_effect/big_root.c
+++ b/test/battle/hold_effect/big_root.c
@@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Big Root increases healing from absorbing moves", s16 damage
PARAMETRIZE { item = ITEM_BIG_ROOT; }
GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); }
+ PLAYER(SPECIES_XURKITREE) { HP(200); Item(item); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ABSORB); }
@@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("Big Root increases healing from absorbing moves", s16 damage
HP_BAR(player, captureDamage: &results[i].heal);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage); // Damage is unaffected
- EXPECT_MUL_EQ(results[1].heal, Q_4_12(5234 / 4096), results[0].heal);
+ EXPECT_MUL_EQ(results[0].heal, Q_4_12(1.3), results[1].heal);
}
}
@@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Big Root increases damage from absorbing Liquid Ooze", s16 d
PARAMETRIZE { item = ITEM_BIG_ROOT; }
GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); }
+ PLAYER(SPECIES_XURKITREE) { HP(200); Item(item); }
OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); }
} WHEN {
TURN { MOVE(player, MOVE_ABSORB); }
@@ -73,6 +73,6 @@ SINGLE_BATTLE_TEST("Big Root increases damage from absorbing Liquid Ooze", s16 d
ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
- EXPECT_MUL_EQ(results[1].damage, Q_4_12(5234 / 4096), results[0].damage);
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), results[1].damage);
}
}
diff --git a/test/battle/hold_effect/booster_energy.c b/test/battle/hold_effect/booster_energy.c
index 41e5fe3b47..a5113ef245 100644
--- a/test/battle/hold_effect/booster_energy.c
+++ b/test/battle/hold_effect/booster_energy.c
@@ -38,6 +38,7 @@ SINGLE_BATTLE_TEST("Booster Energy will activate Quark Drive after Electric Terr
SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunlight ends")
{
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_ABILITY_WEATHER, GEN_6);
PLAYER(SPECIES_RAGING_BOLT) { Attack(100); Defense(100); Speed(100); SpAttack(110); SpDefense(100); Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); }
OPPONENT(SPECIES_TORKOAL) { Speed(100); Ability(ABILITY_DROUGHT); };
} WHEN {
diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c
index 600af04515..836f3d0fd1 100644
--- a/test/battle/hold_effect/eject_pack.c
+++ b/test/battle/hold_effect/eject_pack.c
@@ -338,3 +338,26 @@ SINGLE_BATTLE_TEST("Eject Pack does not activate if mon is switched in due to Ej
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
}
}
+
+DOUBLE_BATTLE_TEST("Eject Pack will trigger on the fastest mon at the end of the turn")
+{
+ GIVEN {
+ ASSUME(MoveHasAdditionalEffect(MOVE_SYRUP_BOMB, MOVE_EFFECT_SYRUP_BOMB) == TRUE);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(1); Item(ITEM_EJECT_PACK); }
+ PLAYER(SPECIES_WYNAUT) { Speed(10); Item(ITEM_EJECT_PACK); }
+ PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
+ OPPONENT(SPECIES_WYNAUT) { Speed(4); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
+ } WHEN {
+ TURN {
+ MOVE(opponentLeft, MOVE_SYRUP_BOMB, target: playerLeft);
+ MOVE(opponentRight, MOVE_SYRUP_BOMB, target: playerRight);
+ SEND_OUT(playerRight, 2);
+ }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SYRUP_BOMB_SPEED_DROP, playerRight);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SYRUP_BOMB_SPEED_DROP, playerLeft);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
+ }
+}
diff --git a/test/battle/hold_effect/gems.c b/test/battle/hold_effect/gems.c
index 52c85d7fb5..b597beeab7 100644
--- a/test/battle/hold_effect/gems.c
+++ b/test/battle/hold_effect/gems.c
@@ -26,13 +26,40 @@ SINGLE_BATTLE_TEST("Gem is consumed when it corresponds to the type of a move")
}
}
+SINGLE_BATTLE_TEST("Gem is not consumed when using Struggle", s16 damage)
+{
+ u32 item = 0;
+
+ PARAMETRIZE { item = ITEM_NONE; }
+ PARAMETRIZE { item = ITEM_NORMAL_GEM; }
+
+ GIVEN {
+ if (item != ITEM_NONE) {
+ ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_GEMS);
+ ASSUME(GetItemSecondaryId(item) == GetMoveType(MOVE_STRUGGLE));
+ }
+ PLAYER(SPECIES_WOBBUFFET) { Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ } SCENE {
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
+ MESSAGE("The Normal Gem strengthened Wobbuffet's power!");
+ }
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_EQ(results[0].damage, results[1].damage);
+ }
+}
+
SINGLE_BATTLE_TEST("Gem boost is only applied once")
{
s16 boostedHit;
s16 normalHit;
GIVEN {
- ASSUME(I_GEM_BOOST_POWER >= GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@@ -46,7 +73,10 @@ SINGLE_BATTLE_TEST("Gem boost is only applied once")
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent, captureDamage: &normalHit);
} THEN {
- EXPECT_MUL_EQ(normalHit, Q_4_12(1.3), boostedHit);
+ if (I_GEM_BOOST_POWER >= GEN_6)
+ EXPECT_MUL_EQ(normalHit, Q_4_12(1.3), boostedHit);
+ else
+ EXPECT_MUL_EQ(normalHit, Q_4_12(1.5), boostedHit);
}
}
diff --git a/test/battle/hold_effect/type_power.c b/test/battle/hold_effect/type_power.c
index 5afe2a39c8..c03d2487d5 100644
--- a/test/battle/hold_effect/type_power.c
+++ b/test/battle/hold_effect/type_power.c
@@ -53,3 +53,27 @@ SINGLE_BATTLE_TEST("Type-enhancing items increase the base power of moves by 20%
}
}
}
+
+SINGLE_BATTLE_TEST("Type-enhancing items do not increase the power of Struggle", s16 damage)
+{
+ u32 item = 0;
+
+ PARAMETRIZE { item = ITEM_NONE; }
+ PARAMETRIZE { item = ITEM_SILK_SCARF; }
+
+ GIVEN {
+ if (item != ITEM_NONE) {
+ ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_TYPE_POWER);
+ ASSUME(GetItemSecondaryId(item) == GetMoveType(MOVE_STRUGGLE));
+ }
+ PLAYER(SPECIES_WOBBUFFET) { Item(item); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_EQ(results[0].damage, results[1].damage);
+ }
+}
diff --git a/test/battle/hold_effect/weakness_berry.c b/test/battle/hold_effect/weakness_berry.c
new file mode 100644
index 0000000000..17adb08aa3
--- /dev/null
+++ b/test/battle/hold_effect/weakness_berry.c
@@ -0,0 +1,124 @@
+#include "global.h"
+#include "test/battle.h"
+
+static const u16 sMoveItemTable[][4] =
+{
+ { TYPE_NORMAL, MOVE_SCRATCH, ITEM_CHILAN_BERRY, SPECIES_WOBBUFFET },
+ { TYPE_FIGHTING, MOVE_KARATE_CHOP, ITEM_CHOPLE_BERRY, SPECIES_RAMPARDOS },
+ { TYPE_FLYING, MOVE_WING_ATTACK, ITEM_COBA_BERRY, SPECIES_HARIYAMA },
+ { TYPE_POISON, MOVE_POISON_STING, ITEM_KEBIA_BERRY, SPECIES_GOGOAT },
+ { TYPE_GROUND, MOVE_MUD_SHOT, ITEM_SHUCA_BERRY, SPECIES_RAMPARDOS },
+ { TYPE_ROCK, MOVE_ROCK_THROW, ITEM_CHARTI_BERRY, SPECIES_CORVISQUIRE },
+ { TYPE_BUG, MOVE_BUG_BITE, ITEM_TANGA_BERRY, SPECIES_WOBBUFFET },
+ { TYPE_GHOST, MOVE_SHADOW_PUNCH, ITEM_KASIB_BERRY, SPECIES_WOBBUFFET },
+ { TYPE_STEEL, MOVE_METAL_CLAW, ITEM_BABIRI_BERRY, SPECIES_RAMPARDOS },
+ { TYPE_FIRE, MOVE_EMBER, ITEM_OCCA_BERRY, SPECIES_GOGOAT },
+ { TYPE_WATER, MOVE_WATER_GUN, ITEM_PASSHO_BERRY, SPECIES_RAMPARDOS },
+ { TYPE_GRASS, MOVE_VINE_WHIP, ITEM_RINDO_BERRY, SPECIES_RAMPARDOS },
+ { TYPE_ELECTRIC, MOVE_THUNDER_SHOCK, ITEM_WACAN_BERRY, SPECIES_CORVISQUIRE },
+ { TYPE_PSYCHIC, MOVE_CONFUSION, ITEM_PAYAPA_BERRY, SPECIES_HARIYAMA },
+ { TYPE_ICE, MOVE_AURORA_BEAM, ITEM_YACHE_BERRY, SPECIES_DRAGONAIR },
+ { TYPE_DRAGON, MOVE_DRAGON_BREATH, ITEM_HABAN_BERRY, SPECIES_DRAGONAIR },
+ { TYPE_DARK, MOVE_BITE, ITEM_COLBUR_BERRY, SPECIES_WOBBUFFET },
+ { TYPE_FAIRY, MOVE_DISARMING_VOICE, ITEM_ROSELI_BERRY, SPECIES_DRAGONAIR },
+};
+
+SINGLE_BATTLE_TEST("Weakness berries decrease the base power of moves by half", s16 damage)
+{
+ u32 move = 0, item = 0, type = 0, defender = 0;
+
+ for (u32 j = 0; j < ARRAY_COUNT(sMoveItemTable); j++)
+ {
+ PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; defender = sMoveItemTable[j][3]; item = ITEM_NONE; }
+ PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; defender = sMoveItemTable[j][3]; item = sMoveItemTable[j][2]; }
+ }
+
+ GIVEN {
+ ASSUME(GetMovePower(move) > 0);
+ ASSUME(GetMoveType(move) == type);
+ ASSUME(GetSpeciesType(defender, 0) == GetSpeciesType(defender, 1));
+ if (type != TYPE_NORMAL) {
+ ASSUME(gTypeEffectivenessTable[type][GetSpeciesType(defender, 0)] > UQ_4_12(1.0));
+ }
+ if (item != ITEM_NONE) {
+ ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_RESIST_BERRY);
+ ASSUME(GetItemHoldEffectParam(item) == type);
+ }
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(defender) { Item(item); }
+ } WHEN {
+ TURN { MOVE(player, move); }
+ } SCENE {
+ if (1 == i % 2) {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ }
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ for (u32 j = 0; j < ARRAY_COUNT(sMoveItemTable); j++) {
+ EXPECT_MUL_EQ(results[j*2].damage, Q_4_12(0.5), results[(j*2)+1].damage);
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Weakness berries do not activate unless a move is super effective", s16 damage)
+{
+ u32 move = 0, item = 0, type = 0, defender = 0;
+
+ for (u32 j = 0; j < ARRAY_COUNT(sMoveItemTable); j++)
+ {
+ if (TYPE_NORMAL == type)
+ {
+ // ITEM_CHILAN_BERRY activates without a weakness
+ }
+ else if (TYPE_FAIRY == type)
+ {
+ PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; item = sMoveItemTable[j][2]; defender = SPECIES_WOBBUFFET; }
+ }
+ else
+ {
+ PARAMETRIZE { type = sMoveItemTable[j][0]; move = sMoveItemTable[j][1]; item = sMoveItemTable[j][2]; defender = SPECIES_SABLEYE; }
+ }
+ }
+
+ GIVEN {
+ ASSUME(GetMovePower(move) > 0);
+ ASSUME(uq4_12_multiply(gTypeEffectivenessTable[type][GetSpeciesType(defender, 0)],
+ gTypeEffectivenessTable[type][GetSpeciesType(defender, 1)]) <= UQ_4_12(1.0));
+ ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_RESIST_BERRY);
+ ASSUME(GetItemHoldEffectParam(item) == type);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(defender) { Item(item); }
+ } WHEN {
+ TURN { MOVE(player, move); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ }
+}
+
+SINGLE_BATTLE_TEST("Weakness berries do not decrease the power of Struggle", s16 damage)
+{
+ u32 item = 0;
+
+ PARAMETRIZE { item = ITEM_NONE; }
+ PARAMETRIZE { item = ITEM_CHILAN_BERRY; }
+
+ GIVEN {
+ if (item != ITEM_NONE) {
+ ASSUME(GetItemHoldEffect(item) == HOLD_EFFECT_RESIST_BERRY);
+ ASSUME(GetItemHoldEffectParam(item) == TYPE_NORMAL);
+ }
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET) { Item(item); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ } SCENE {
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ MESSAGE("The Chilan Berry weakened the damage to the opposing Wobbuffet!");
+ }
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_EQ(results[0].damage, results[1].damage);
+ }
+}
diff --git a/test/battle/move_effect/acrobatics.c b/test/battle/move_effect/acrobatics.c
index 809b77f948..1229d4e2c5 100644
--- a/test/battle/move_effect/acrobatics.c
+++ b/test/battle/move_effect/acrobatics.c
@@ -30,7 +30,6 @@ SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consume
PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_FLYING_GEM; }
GIVEN {
- ASSUME(I_GEM_BOOST_POWER >= GEN_6);
ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS);
ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING);
PLAYER(SPECIES_WOBBUFFET);
diff --git a/test/battle/move_effect/dragon_darts.c b/test/battle/move_effect/dragon_darts.c
index 13a165a459..40a9177589 100644
--- a/test/battle/move_effect/dragon_darts.c
+++ b/test/battle/move_effect/dragon_darts.c
@@ -4,7 +4,6 @@
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
- ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
}
SINGLE_BATTLE_TEST("Dragon Darts strikes twice")
@@ -72,13 +71,13 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes an opponent twice if the other one is F
struct BattlePokemon *chosenTarget = NULL;
struct BattlePokemon *finalTarget = NULL;
u32 speciesLeft, speciesRight;
- PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; }
- PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_CLEFAIRY; speciesRight = SPECIES_WOBBUFFET; }
- PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; }
- PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_CLEFAIRY; }
+ PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentRight; speciesLeft = SPECIES_FIDOUGH; speciesRight = SPECIES_WOBBUFFET; }
+ PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentRight; speciesLeft = SPECIES_FIDOUGH; speciesRight = SPECIES_WOBBUFFET; }
+ PARAMETRIZE { chosenTarget = opponentLeft; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_FIDOUGH; }
+ PARAMETRIZE { chosenTarget = opponentRight; finalTarget = opponentLeft; speciesLeft = SPECIES_WOBBUFFET; speciesRight = SPECIES_FIDOUGH; }
GIVEN {
- ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
+ ASSUME(GetSpeciesType(SPECIES_FIDOUGH, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_FIDOUGH, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(speciesLeft);
@@ -257,7 +256,6 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes right ally twice if one strike misses")
DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me user")
{
GIVEN {
- ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@@ -273,14 +271,14 @@ DOUBLE_BATTLE_TEST("Dragon Darts strikes will be both redirected to Follow Me us
}
}
-DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a fairy type follow me user")
+DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a Fairy-type follow me user")
{
GIVEN {
- ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
+ ASSUME(GetSpeciesType(SPECIES_FIDOUGH, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_FIDOUGH, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_CLEFAIRY);
+ OPPONENT(SPECIES_FIDOUGH);
} WHEN {
TURN { MOVE(opponentRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
@@ -295,7 +293,6 @@ DOUBLE_BATTLE_TEST("Dragon Darts fails to strike any target if under a fairy typ
DOUBLE_BATTLE_TEST("Dragon Darts fails to strike the second target if first target fainted and follow me was active")
{
GIVEN {
- ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
diff --git a/test/battle/move_effect/endure.c b/test/battle/move_effect/endure.c
index f332fc28de..2d66bc41ef 100644
--- a/test/battle/move_effect/endure.c
+++ b/test/battle/move_effect/endure.c
@@ -1,13 +1,17 @@
#include "global.h"
#include "test/battle.h"
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_ENDURE) == EFFECT_ENDURE);
+}
+
TO_DO_BATTLE_TEST("Endure allows the user to survive any attack with 1 HP left");
SINGLE_BATTLE_TEST("Endure does not prevent multiple hits and stat changes occur at the end of the turn")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT);
- ASSUME(GetMoveEffect(MOVE_ENDURE) == EFFECT_ENDURE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
} WHEN {
@@ -54,6 +58,23 @@ DOUBLE_BATTLE_TEST("Endure is not transferred to a mon that is switched in due t
}
}
+SINGLE_BATTLE_TEST("Endure only lasts for one turn")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_ENDURE); MOVE(player, MOVE_POUND); }
+ TURN { MOVE(player, MOVE_POUND); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player);
+ MESSAGE("The opposing Wobbuffet endured the hit!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player);
+ NOT MESSAGE("The opposing Wobbuffet endured the hit!");
+ }
+}
+
TO_DO_BATTLE_TEST("Endure's success rate decreases for every consecutively used turn");
TO_DO_BATTLE_TEST("Endure uses the same counter as Protect");
TO_DO_BATTLE_TEST("Endure doesn't trigger effects that require damage to be done to the Pokémon (Gen 2-4)"); // Eg. Rough Skin
diff --git a/test/battle/move_effect/fickle_beam.c b/test/battle/move_effect/fickle_beam.c
index 0313823aa9..8554a29dac 100644
--- a/test/battle/move_effect/fickle_beam.c
+++ b/test/battle/move_effect/fickle_beam.c
@@ -12,15 +12,14 @@ SINGLE_BATTLE_TEST("Fickle Beam deals double damage 30% of the time")
PASSES_RANDOMLY(30, 100, RNG_FICKLE_BEAM);
GIVEN {
- ASSUME(GetMovePower(MOVE_POWER_GEM) == 80);
- ASSUME(GetMovePower(MOVE_FICKLE_BEAM) == 80);
+ ASSUME(GetMovePower(MOVE_DAZZLING_GLEAM) == GetMovePower(MOVE_FICKLE_BEAM));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
- TURN { MOVE(player, MOVE_POWER_GEM); }
+ TURN { MOVE(player, MOVE_DAZZLING_GLEAM); }
TURN { MOVE(player, MOVE_FICKLE_BEAM); }
} SCENE {
- ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_GEM, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_DAZZLING_GLEAM, player);
HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FICKLE_BEAM, player);
HP_BAR(opponent, captureDamage: &damage[1]);
diff --git a/test/battle/move_effect/future_sight.c b/test/battle/move_effect/future_sight.c
index 246c384a07..ee424c9bd9 100644
--- a/test/battle/move_effect/future_sight.c
+++ b/test/battle/move_effect/future_sight.c
@@ -1,10 +1,18 @@
#include "global.h"
#include "test/battle.h"
+#if B_UPDATED_MOVE_DATA >= GEN_6
+ #define FUTURE_SIGHT_EQUIVALENT MOVE_SEED_FLARE /* 120 power */
+#elif B_UPDATED_MOVE_DATA >= GEN_5
+ #define FUTURE_SIGHT_EQUIVALENT MOVE_DYNAMAX_CANNON /* 100 power */
+#else
+ #define FUTURE_SIGHT_EQUIVALENT MOVE_EXTRASENSORY /* 80 power */
+#endif
+
ASSUMPTIONS
{
- ASSUME(GetMovePower(MOVE_SEED_FLARE) == GetMovePower(MOVE_FUTURE_SIGHT));
- ASSUME(GetMoveCategory(MOVE_SEED_FLARE) == GetMoveCategory(MOVE_FUTURE_SIGHT));
+ ASSUME(GetMovePower(FUTURE_SIGHT_EQUIVALENT) == GetMovePower(MOVE_FUTURE_SIGHT));
+ ASSUME(GetMoveCategory(FUTURE_SIGHT_EQUIVALENT) == GetMoveCategory(MOVE_FUTURE_SIGHT));
ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT);
ASSUME(GetMovePower(MOVE_FUTURE_SIGHT) > 0);
}
@@ -23,13 +31,13 @@ SINGLE_BATTLE_TEST("Future Sight uses Sp. Atk stat of the original user without
PLAYER(SPECIES_RAICHU) { Item(item); }
OPPONENT(SPECIES_REGICE);
} WHEN {
- TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
+ TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
TURN { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); }
TURN { }
TURN { }
} SCENE {
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player);
+ ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent, captureDamage: &seedFlareDmg);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
MESSAGE("The opposing Regice took the Future Sight attack!");
@@ -49,13 +57,13 @@ SINGLE_BATTLE_TEST("Future Sight is not boosted by Life Orb is original user if
PLAYER(SPECIES_RAICHU) { Item(ITEM_LIFE_ORB); }
OPPONENT(SPECIES_REGICE);
} WHEN {
- TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
+ TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
TURN { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); }
TURN { }
TURN { }
} SCENE {
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player);
+ ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent, captureDamage: &seedFlareDmg);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
MESSAGE("The opposing Regice took the Future Sight attack!");
@@ -77,13 +85,13 @@ SINGLE_BATTLE_TEST("Future Sight receives STAB from party mon (Gen 5+)")
PLAYER(SPECIES_RAICHU);
OPPONENT(SPECIES_REGICE);
} WHEN {
- TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
+ TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
TURN { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); }
TURN { }
TURN { }
} SCENE {
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player);
+ ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent, captureDamage: &seedFlareDmg);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
HP_BAR(opponent, captureDamage: &futureSightDmg);
@@ -100,13 +108,13 @@ SINGLE_BATTLE_TEST("Future Sight is affected by type effectiveness (Gen 5+)")
PLAYER(SPECIES_RAICHU);
OPPONENT(SPECIES_HOUNDOOM);
} WHEN {
- TURN { MOVE(player, MOVE_SEED_FLARE, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
+ TURN { MOVE(player, FUTURE_SIGHT_EQUIVALENT, WITH_RNG(RNG_SECONDARY_EFFECT, FALSE)); }
TURN { MOVE(player, MOVE_FUTURE_SIGHT); }
TURN { SWITCH(player, 1); }
TURN { }
TURN { }
} SCENE {
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SEED_FLARE, player);
+ ANIMATION(ANIM_TYPE_MOVE, FUTURE_SIGHT_EQUIVALENT, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
MESSAGE("The opposing Houndoom took the Future Sight attack!");
diff --git a/test/battle/move_effect/instruct.c b/test/battle/move_effect/instruct.c
index 03a39fe154..070b6ba31d 100644
--- a/test/battle/move_effect/instruct.c
+++ b/test/battle/move_effect/instruct.c
@@ -280,6 +280,7 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after inst
PARAMETRIZE { moveTarget = opponentLeft; }
PARAMETRIZE { moveTarget = opponentRight; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME);
ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE);
ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK);
diff --git a/test/battle/move_effect/multi_hit.c b/test/battle/move_effect/multi_hit.c
index 7052a52cbf..291c78efdb 100644
--- a/test/battle/move_effect/multi_hit.c
+++ b/test/battle/move_effect/multi_hit.c
@@ -177,14 +177,14 @@ SINGLE_BATTLE_TEST("Scale Shot is immune to Fairy types and will end the move co
GIVEN {
ASSUME(GetMoveEffect(MOVE_SCALE_SHOT) == EFFECT_MULTI_HIT);
ASSUME(GetMoveType(MOVE_SCALE_SHOT) == TYPE_DRAGON);
- ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_CLEFAIRY, 1) == TYPE_FAIRY);
+ ASSUME(GetSpeciesType(SPECIES_FIDOUGH, 0) == TYPE_FAIRY || GetSpeciesType(SPECIES_FIDOUGH, 1) == TYPE_FAIRY);
PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_CLEFAIRY) { HP(1); }
+ OPPONENT(SPECIES_FIDOUGH) { HP(1); }
} WHEN {
TURN { MOVE(player, MOVE_SCALE_SHOT); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALE_SHOT, player);
- MESSAGE("It doesn't affect the opposing Clefairy…");
+ MESSAGE("It doesn't affect the opposing Fidough…");
}
}
diff --git a/test/battle/move_effect/powder.c b/test/battle/move_effect/powder.c
index c4e053db87..da8eba4caf 100644
--- a/test/battle/move_effect/powder.c
+++ b/test/battle/move_effect/powder.c
@@ -149,9 +149,10 @@ DOUBLE_BATTLE_TEST("Powder fails if target is already affected by Powder")
}
}
-SINGLE_BATTLE_TEST("Powder fails if the target is Grass type")
+SINGLE_BATTLE_TEST("Powder fails if the target is Grass type (Gen6+)")
{
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(GetSpeciesType(SPECIES_VENUSAUR, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_VENUSAUR, 1) == TYPE_GRASS);
PLAYER(SPECIES_VENUSAUR);
OPPONENT(SPECIES_VIVILLON);
@@ -164,9 +165,10 @@ SINGLE_BATTLE_TEST("Powder fails if the target is Grass type")
}
}
-SINGLE_BATTLE_TEST("Powder fails if the target has Overcoat")
+SINGLE_BATTLE_TEST("Powder fails if the target has Overcoat (Gen6+)")
{
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
PLAYER(SPECIES_FORRETRESS) { Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_VIVILLON);
} WHEN {
diff --git a/test/battle/move_effect/struggle.c b/test/battle/move_effect/struggle.c
new file mode 100644
index 0000000000..5bf9de6207
--- /dev/null
+++ b/test/battle/move_effect/struggle.c
@@ -0,0 +1,75 @@
+#include "global.h"
+#include "test/battle.h"
+
+TO_DO_BATTLE_TEST("Struggle deals recoil 1/4 of damage dealt (Gen 2-3)")
+
+SINGLE_BATTLE_TEST("Struggle deals recoil 1/4 of user's hp (Gen 4+)")
+{
+ ASSUME(GetMoveEffect(MOVE_STRUGGLE) == EFFECT_STRUGGLE);
+
+ s16 recoil;
+ u32 atkStat = 0;
+ u32 hpStat = 0;
+
+ PARAMETRIZE { atkStat = 100; hpStat = 200; }
+ PARAMETRIZE { atkStat = 50; hpStat = 200; }
+ PARAMETRIZE { atkStat = 100; hpStat = 300; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { MaxHP(hpStat); HP(hpStat); Attack(atkStat); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ HP_BAR(player, captureDamage: &recoil);
+ } THEN {
+ EXPECT_MUL_EQ(hpStat, Q_4_12(0.25), recoil);
+ }
+}
+
+SINGLE_BATTLE_TEST("Struggle can hit ghost types")
+{
+ ASSUME(GetSpeciesType(SPECIES_DRIFBLIM, 0) == TYPE_GHOST);
+
+ s16 damage;
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_DRIFBLIM);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ HP_BAR(opponent, captureDamage: &damage);
+ } THEN {
+ EXPECT_NE(0, damage);
+ }
+}
+
+SINGLE_BATTLE_TEST("Struggle does not receive normal-type STAB")
+{
+ // Compare with Cut, which does receive normal-type STAB
+ ASSUME(GetSpeciesType(SPECIES_ZANGOOSE, 0) == GetMoveType(MOVE_STRUGGLE));
+ ASSUME(GetMovePower(MOVE_CUT) == GetMovePower(MOVE_STRUGGLE));
+ ASSUME(GetMoveCategory(MOVE_CUT) == GetMoveCategory(MOVE_STRUGGLE));
+ ASSUME(GetMoveType(MOVE_CUT) == GetMoveType(MOVE_STRUGGLE));
+
+ s16 cutDamage;
+ s16 struggleDamage;
+
+ GIVEN {
+ PLAYER(SPECIES_ZANGOOSE);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CUT); }
+ TURN { MOVE(player, MOVE_STRUGGLE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CUT, player);
+ HP_BAR(opponent, captureDamage: &cutDamage);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ HP_BAR(opponent, captureDamage: &struggleDamage);
+ } THEN {
+ EXPECT_MUL_EQ(struggleDamage, Q_4_12(1.5), cutDamage);
+ }
+}
diff --git a/test/battle/move_effect/toxic.c b/test/battle/move_effect/toxic.c
index ef7c8f011a..a8134f9c66 100644
--- a/test/battle/move_effect/toxic.c
+++ b/test/battle/move_effect/toxic.c
@@ -43,14 +43,16 @@ SINGLE_BATTLE_TEST("Toxic can't bad poison a poison or steel type")
}
}
-SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type")
+SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type (Gen6+)")
{
- u32 species;
+ u32 species, gen;
bool32 hit;
- PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; }
- PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = TRUE; }
+ PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; gen = GEN_5; }
+ PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = FALSE; gen = GEN_5; }
+ PARAMETRIZE { species = SPECIES_WOBBUFFET; hit = FALSE; gen = GEN_6; }
+ PARAMETRIZE { species = SPECIES_NIDORAN_M; hit = TRUE; gen = GEN_6; }
GIVEN {
- ASSUME(B_TOXIC_NEVER_MISS >= GEN_6);
+ WITH_CONFIG(GEN_CONFIG_TOXIC_NEVER_MISS, gen);
ASSUME(GetSpeciesType(SPECIES_NIDORAN_M, 0) == TYPE_POISON);
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);
diff --git a/test/battle/move_effect_secondary/dire_claw.c b/test/battle/move_effect_secondary/dire_claw.c
index ad9ffcc4e2..abd4563132 100644
--- a/test/battle/move_effect_secondary/dire_claw.c
+++ b/test/battle/move_effect_secondary/dire_claw.c
@@ -38,11 +38,10 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respe
u8 statusAnim;
u16 species;
u32 rng;
- #if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; }
- #endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = MOVE_EFFECT_POISON; species = SPECIES_ARBOK; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
diff --git a/test/battle/move_effect_secondary/paralysis.c b/test/battle/move_effect_secondary/paralysis.c
index baa2183064..723309f01a 100644
--- a/test/battle/move_effect_secondary/paralysis.c
+++ b/test/battle/move_effect_secondary/paralysis.c
@@ -21,19 +21,27 @@ SINGLE_BATTLE_TEST("Thunder Shock inflicts paralysis")
}
}
-SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type")
+SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type (Gen6+)")
{
+ u32 gen = 0;
+ PARAMETRIZE { gen = GEN_5; }
+ PARAMETRIZE { gen = GEN_6; }
GIVEN {
- ASSUME(B_PARALYZE_ELECTRIC >= GEN_6);
+ WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, gen);
ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU);
} WHEN {
- TURN { MOVE(player, MOVE_THUNDER_SHOCK); }
+ TURN { MOVE(player, MOVE_THUNDER_SHOCK, secondaryEffect: TRUE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, player);
HP_BAR(opponent);
- NONE_OF {
+ if (gen == GEN_6) {
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
+ STATUS_ICON(opponent, paralysis: TRUE);
+ }
+ } else {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
STATUS_ICON(opponent, paralysis: TRUE);
}
diff --git a/test/battle/move_effect_secondary/tri_attack.c b/test/battle/move_effect_secondary/tri_attack.c
index ab33561cf0..4ec90f6f92 100644
--- a/test/battle/move_effect_secondary/tri_attack.c
+++ b/test/battle/move_effect_secondary/tri_attack.c
@@ -46,12 +46,11 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
u8 statusAnim;
u16 species;
u32 rng;
- #if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; }
- #endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_ARCANINE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
diff --git a/test/battle/move_flags/powder.c b/test/battle/move_flags/powder.c
index bb0b1e3e4f..989cfb6426 100644
--- a/test/battle/move_flags/powder.c
+++ b/test/battle/move_flags/powder.c
@@ -1,9 +1,13 @@
#include "global.h"
#include "test/battle.h"
-SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon")
+SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon (Gen6+)")
{
+ u32 gen = 0;
+ PARAMETRIZE { gen = GEN_5; }
+ PARAMETRIZE { gen = GEN_6; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen);
ASSUME(IsPowderMove(MOVE_STUN_SPORE));
ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS);
PLAYER(SPECIES_WYNAUT);
@@ -11,7 +15,12 @@ SINGLE_BATTLE_TEST("Powder moves are blocked by Grass-type Pokémon")
} WHEN {
TURN { MOVE(player, MOVE_STUN_SPORE); }
} SCENE {
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
- MESSAGE("It doesn't affect the opposing Oddish…");
+ if (gen == GEN_6) {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ MESSAGE("It doesn't affect the opposing Oddish…");
+ } else {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ NOT MESSAGE("It doesn't affect the opposing Oddish…");
+ }
}
}
diff --git a/test/battle/sleep_clause.c b/test/battle/sleep_clause.c
index 73f419289c..3b8999f668 100644
--- a/test/battle/sleep_clause.c
+++ b/test/battle/sleep_clause.c
@@ -1075,6 +1075,7 @@ SINGLE_BATTLE_TEST("Sleep Clause: Sleep clause is deactivated when a sleeping mo
u32 ability;
PARAMETRIZE { ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { ability = ABILITY_INSOMNIA; }
+
GIVEN {
FLAG_SET(B_FLAG_SLEEP_CLAUSE);
ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS);
diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c
index c81daa9179..1354c6490b 100644
--- a/test/battle/status1/paralysis.c
+++ b/test/battle/status1/paralysis.c
@@ -55,6 +55,7 @@ AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target")
PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); }
@@ -63,18 +64,30 @@ AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target")
}
}
-SINGLE_BATTLE_TEST("Thunder Wave doesn't affect Electric types in Gen6+")
+SINGLE_BATTLE_TEST("Thunder Wave doesn't affect Electric types (Gen6+)")
{
+ u32 gen = 0;
+ PARAMETRIZE { gen = GEN_5; }
+ PARAMETRIZE { gen = GEN_6; }
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, gen);
ASSUME(GetSpeciesType(SPECIES_PIKACHU, 0) == TYPE_ELECTRIC);
- ASSUME(B_PARALYZE_ELECTRIC >= GEN_6);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU);
} WHEN {
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
} SCENE {
MESSAGE("Wobbuffet used Thunder Wave!");
- MESSAGE("It doesn't affect the opposing Pikachu…");
+ if (gen == GEN_6) {
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
+ STATUS_ICON(opponent, paralysis: TRUE);
+ }
+ MESSAGE("It doesn't affect the opposing Pikachu…");
+ } else {
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
+ STATUS_ICON(opponent, paralysis: TRUE);
+ }
}
}
diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c
index 401c0d0bcd..7a9f60d5fe 100644
--- a/test/battle/status1/sleep.c
+++ b/test/battle/status1/sleep.c
@@ -22,11 +22,25 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
}
}
-SINGLE_BATTLE_TEST("Sleep: Spore doesn't affect grass types (Gen 6+)")
+SINGLE_BATTLE_TEST("Sleep: Spore affects grass types (Gen1-5)")
{
GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_5);
+ ASSUME(IsPowderMove(MOVE_SPORE));
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_CHIKORITA);
+ } WHEN {
+ TURN { MOVE(player, MOVE_SPORE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player);
+ }
+}
+
+SINGLE_BATTLE_TEST("Sleep: Spore doesn't affect grass types (Gen6+)")
+{
+ GIVEN {
+ WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(IsPowderMove(MOVE_SPORE));
- ASSUME(B_POWDER_GRASS >= GEN_6);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHIKORITA);
} WHEN {
diff --git a/test/pokemon.c b/test/pokemon.c
index 60058407c4..6079bd28bd 100644
--- a/test/pokemon.c
+++ b/test/pokemon.c
@@ -288,7 +288,7 @@ TEST("givemon [moves]")
ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT(
- givemon SPECIES_WOBBUFFET, 100, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_NONE, move4=MOVE_NONE;
+ givemon SPECIES_WOBBUFFET, 100, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_NONE;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);
@@ -304,7 +304,7 @@ TEST("givemon [all]")
ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT(
- givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, isShiny=TRUE, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7;
+ givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, shinyMode=SHINY_MODE_ALWAYS, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);
@@ -363,13 +363,13 @@ TEST("givemon [vars]")
VarSet(VAR_TEMP_6, MOVE_SPLASH);
VarSet(VAR_TEMP_7, MOVE_CELEBRATE);
VarSet(VAR_TEMP_8, MOVE_EXPLOSION);
- VarSet(VAR_TEMP_9, TRUE);
+ VarSet(VAR_TEMP_9, SHINY_MODE_ALWAYS);
VarSet(VAR_TEMP_A, TRUE);
VarSet(VAR_TEMP_B, TYPE_FIRE);
VarSet(VAR_TEMP_E, 7);
RUN_OVERWORLD_SCRIPT(
- givemon VAR_TEMP_C, VAR_TEMP_D, item=VAR_0x8000, ball=VAR_0x8001, nature=VAR_0x8002, abilityNum=VAR_0x8003, gender=VAR_0x8004, hpEv=VAR_0x8005, atkEv=VAR_0x8006, defEv=VAR_0x8007, speedEv=VAR_0x8008, spAtkEv=VAR_0x8009, spDefEv=VAR_0x800A, hpIv=VAR_0x800B, atkIv=VAR_TEMP_0, defIv=VAR_TEMP_1, speedIv=VAR_TEMP_2, spAtkIv=VAR_TEMP_3, spDefIv=VAR_TEMP_4, move1=VAR_TEMP_5, move2=VAR_TEMP_6, move3=VAR_TEMP_7, move4=VAR_TEMP_8, isShiny=VAR_TEMP_9, gmaxFactor=VAR_TEMP_A, teraType=VAR_TEMP_B, dmaxLevel=VAR_TEMP_E;
+ givemon VAR_TEMP_C, VAR_TEMP_D, item=VAR_0x8000, ball=VAR_0x8001, nature=VAR_0x8002, abilityNum=VAR_0x8003, gender=VAR_0x8004, hpEv=VAR_0x8005, atkEv=VAR_0x8006, defEv=VAR_0x8007, speedEv=VAR_0x8008, spAtkEv=VAR_0x8009, spDefEv=VAR_0x800A, hpIv=VAR_0x800B, atkIv=VAR_TEMP_0, defIv=VAR_TEMP_1, speedIv=VAR_TEMP_2, spAtkIv=VAR_TEMP_3, spDefIv=VAR_TEMP_4, move1=VAR_TEMP_5, move2=VAR_TEMP_6, move3=VAR_TEMP_7, move4=VAR_TEMP_8, shinyMode=VAR_TEMP_9, gmaxFactor=VAR_TEMP_A, teraType=VAR_TEMP_B, dmaxLevel=VAR_TEMP_E;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_WOBBUFFET);