Merge branch 'master' of https://github.com/rh-hideout/pokeemerald-expansion into rh-hideout-master

This commit is contained in:
RoamerX 2025-10-12 22:53:13 +08:00
commit f9570a4404
96 changed files with 1861 additions and 443 deletions

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -24,6 +24,7 @@
<!-- CREDITS -->
<!-- Once your PR is submitted, leave a comment asking the bot to add you to the credits. -->
<!-- If anybody helped with this PR, please encourage them to comment on your PR and ask the bot to add them to the credits. -->
<!-- If somebody has contributed at any point and has indicated they wish to be credited, please ask the bot to add them to the credits. If they do not have a known GitHub account, add them to the list at bottom of `CREDITS.md`. -->
<!-- EVERY contribution matters! -->
<!-- https://github.com/rh-hideout/pokeemerald-expansion/wiki/CREDITS.md-Frequently-Asked-Questions -->

View File

@ -60,6 +60,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lordraindance2"><img src="https://avatars.githubusercontent.com/u/47706100?v=4?s=100" width="100px;" alt="lordraindance2"/><br /><sub><b>lordraindance2</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=lordraindance2" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pablopenna"><img src="https://avatars.githubusercontent.com/u/11214682?v=4?s=100" width="100px;" alt="Pablo Pena"/><br /><sub><b>Pablo Pena</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=pablopenna" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://tustin2121.github.io/"><img src="https://avatars.githubusercontent.com/u/794812?v=4?s=100" width="100px;" alt="tustin2121"/><br /><sub><b>tustin2121</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=tustin2121" title="Documentation">📖</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=tustin2121" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ddaretrogamer"><img src="https://avatars.githubusercontent.com/u/131238004?v=4?s=100" width="100px;" alt="Phantonomy"/><br /><sub><b>Phantonomy</b></sub></a><br /><a href="#design-Ddaretrogamer" title="Design">🎨</a></td>
</tr>
</tbody>
<tfoot>

View File

@ -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.

View File

@ -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!

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]()

View File

@ -0,0 +1,123 @@
```md
## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.13.2
`.
```
## 🧬 General 🧬
### Changed
* Removed superflous palette compression check by @hedara90 in [#7718](https://github.com/rh-hideout/pokeemerald-expansion/pull/7718)
* update of sv.json to consider newest DLC changes by @wiz1989 in [#7672](https://github.com/rh-hideout/pokeemerald-expansion/pull/7672)
* Adjusted line break substring breaking by @hedara90 in [#7789](https://github.com/rh-hideout/pokeemerald-expansion/pull/7789)
* Fixup add-new-trainer-front-pic tutorial. by @GraionDilach in [#7802](https://github.com/rh-hideout/pokeemerald-expansion/pull/7802)
* Adds conversion script for trainers.h by @AlexOn1ine in [#7663](https://github.com/rh-hideout/pokeemerald-expansion/pull/7663)
### Fixed
* Fix bug with IF_GENDER evolution condition by @FosterProgramming in [#7749](https://github.com/rh-hideout/pokeemerald-expansion/pull/7749)
* Pokemon storage moving items bugfix by @FosterProgramming in [#7763](https://github.com/rh-hideout/pokeemerald-expansion/pull/7763)
* Fix catch bug introduced in #7774 by @FosterProgramming in [#7782](https://github.com/rh-hideout/pokeemerald-expansion/pull/7782)
* Fix Party Menu move select name width by @AsparagusEduardo in [#7820](https://github.com/rh-hideout/pokeemerald-expansion/pull/7820)
* Fix Debug Give Pokémon (Complex) with duplicate moves by @AsparagusEduardo in [#7821](https://github.com/rh-hideout/pokeemerald-expansion/pull/7821)
## 🗺️ Overworld 🗺️
### Changed
* Added missing `LOCALID_NONE` by @estellarc in [#7783](https://github.com/rh-hideout/pokeemerald-expansion/pull/7783)
### Fixed
* Fix TRAINER_TYPE_SEE_ALL_DIRECTIONS by @DizzyEggg in [#7779](https://github.com/rh-hideout/pokeemerald-expansion/pull/7779)
## 🐉 Pokémon 🐉
### Fixed
* fix: seen flags for first mon in enemy party by @khbsd in [#7791](https://github.com/rh-hideout/pokeemerald-expansion/pull/7791)
## ⚔️ Battle General ⚔️
### Fixed
* Fix most failed tests with `GEN_LATEST` = `GEN_7` by @AsparagusEduardo in [#7688](https://github.com/rh-hideout/pokeemerald-expansion/pull/7688)
* Fixes Sweet Veil not protecting sleep from Yawn status by @AlexOn1ine in [#7704](https://github.com/rh-hideout/pokeemerald-expansion/pull/7704)
* Fix incorrect wrap turn amount by @AlexOn1ine in [#7667](https://github.com/rh-hideout/pokeemerald-expansion/pull/7667)
* Fixes Rage Fist for gen7 Disguise by @AlexOn1ine in [#7692](https://github.com/rh-hideout/pokeemerald-expansion/pull/7692)
* Fixes Intrepid Sword/Dauntless Shield boosting after entering while at max stats by @PhallenTree in [#7716](https://github.com/rh-hideout/pokeemerald-expansion/pull/7716)
* Fixes incorrect ending for some scripts by @AlexOn1ine in [#7691](https://github.com/rh-hideout/pokeemerald-expansion/pull/7691)
* Fixes Uproar not waking up mons by @AlexOn1ine in [#7714](https://github.com/rh-hideout/pokeemerald-expansion/pull/7714)
* Fixes Endure and Eject Pack issues by @AlexOn1ine in [#7687](https://github.com/rh-hideout/pokeemerald-expansion/pull/7687)
* Fix Beak Blast burning fire types by @hedara90 in [#7740](https://github.com/rh-hideout/pokeemerald-expansion/pull/7740)
* Fixes Recharge not actually being removed when recharge turn occurs by @PhallenTree in [#7744](https://github.com/rh-hideout/pokeemerald-expansion/pull/7744)
* Bugfixes Batch by @AlexOn1ine in [#7739](https://github.com/rh-hideout/pokeemerald-expansion/pull/7739)
* Fixes Beat Up incorrect slots usage by @AlexOn1ine in [#7741](https://github.com/rh-hideout/pokeemerald-expansion/pull/7741)
* Fixes Mycelium Might and Lagging Tail adjusting prio incorrectly by @AlexOn1ine in [#7742](https://github.com/rh-hideout/pokeemerald-expansion/pull/7742)
* Wrong argument passed down by @AlexOn1ine in [#7751](https://github.com/rh-hideout/pokeemerald-expansion/pull/7751)
* Fixed Ball Fetch Ability by @bassforte123 in [#7764](https://github.com/rh-hideout/pokeemerald-expansion/pull/7764)
* Fixes Flower Shield affecting semi-invulnerable mons by @AlexOn1ine in [#7766](https://github.com/rh-hideout/pokeemerald-expansion/pull/7766)
* Fixes Helping Hand boosts not stacking with each other by @PhallenTree in [#7775](https://github.com/rh-hideout/pokeemerald-expansion/pull/7775)
* Fixes OHKO moves calculating accuracy twice by @AlexOn1ine in [#7785](https://github.com/rh-hideout/pokeemerald-expansion/pull/7785)
* Fixes Instructed moves looking at the wrong turn order number by @PhallenTree in [#7788](https://github.com/rh-hideout/pokeemerald-expansion/pull/7788)
* Fix Flame Burst timeout if primary target is fainted by @hedara90 in [#7793](https://github.com/rh-hideout/pokeemerald-expansion/pull/7793)
* Fixes Leppa Berry timings by @AlexOn1ine in [#7787](https://github.com/rh-hideout/pokeemerald-expansion/pull/7787)
* Fixes Effects activating when move wasn't successful by @AlexOn1ine in [#7803](https://github.com/rh-hideout/pokeemerald-expansion/pull/7803)
* Fixes Throat Spray being blocked by Sheer Force by @AlexOn1ine in [#7808](https://github.com/rh-hideout/pokeemerald-expansion/pull/7808)
* Fixes inaccurate save / restore in Fling script by @AlexOn1ine in [#7811](https://github.com/rh-hideout/pokeemerald-expansion/pull/7811)
* Fix test exit prints for stored battlers by @AlexOn1ine in [#7807](https://github.com/rh-hideout/pokeemerald-expansion/pull/7807)
* Fixes EndTurn Eject Pack by @AlexOn1ine in [#7813](https://github.com/rh-hideout/pokeemerald-expansion/pull/7813)
* Fix Battle Frontier using Strange Balls by @AsparagusEduardo in [#7823](https://github.com/rh-hideout/pokeemerald-expansion/pull/7823)
* Fix Throat Spray activating multiply times by @AlexOn1ine in [#7818](https://github.com/rh-hideout/pokeemerald-expansion/pull/7818)
* fix (choice lock): Gorilla Tactics interactions with choice item removal by @ghostyboyy97 in [#7824](https://github.com/rh-hideout/pokeemerald-expansion/pull/7824)
- Fixed interactions with choice items and Gorilla Tactics both present when choice item is removed by a thief effect or item swap effect.
* Fixes encore random target for gen5+ by @AlexOn1ine in [#7800](https://github.com/rh-hideout/pokeemerald-expansion/pull/7800)
## 🤹 Moves 🤹
### Changed
* Initial Lash Out tests by @grintoul1 in [#7769](https://github.com/rh-hideout/pokeemerald-expansion/pull/7769)
### Fixed
* Fix Salt Cure in double battles by @Bassoonian in [#7797](https://github.com/rh-hideout/pokeemerald-expansion/pull/7797)
## 🎭 Abilities 🎭
### Fixed
* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748)
* Fix Forecast and Flower Gift corruption by @Bassoonian in [#7796](https://github.com/rh-hideout/pokeemerald-expansion/pull/7796)
* Immunity abilities trigger on turn 0 (leads) by @spindrift64 in [#7814](https://github.com/rh-hideout/pokeemerald-expansion/pull/7814)
## 🤖 Battle AI 🤖
### Changed
* Tidy up CanTargetFaintAiWithMod and CanTargetMoveFaintAi by @grintoul1 in [#7693](https://github.com/rh-hideout/pokeemerald-expansion/pull/7693)
* Doubles AI: Trick Room timer fix and test for DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE by @grintoul1 in [#7622](https://github.com/rh-hideout/pokeemerald-expansion/pull/7622)
### Fixed
* Toxic thread uses light screen's scoring.... by @surskitty in [#7674](https://github.com/rh-hideout/pokeemerald-expansion/pull/7674)
* Fix most failed and assume fail tests with `GEN_LATEST` = `GEN_6` by @AsparagusEduardo in [#7696](https://github.com/rh-hideout/pokeemerald-expansion/pull/7696)
* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748)
## 🧹 Other Cleanup 🧹
* Tidy up CanTargetFaintAiWithMod and CanTargetMoveFaintAi by @grintoul1 in [#7693](https://github.com/rh-hideout/pokeemerald-expansion/pull/7693)
* Removed superflous palette compression check by @hedara90 in [#7718](https://github.com/rh-hideout/pokeemerald-expansion/pull/7718)
* Fix failing test for B_PREFERRED_ICE_WEATHER = B_ICE_WEATHER_SNOW by @phexmiau in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755)
* Adjusted line break substring breaking by @hedara90 in [#7789](https://github.com/rh-hideout/pokeemerald-expansion/pull/7789)
* Added missing `LOCALID_NONE` by @estellarc in [#7783](https://github.com/rh-hideout/pokeemerald-expansion/pull/7783)
## 🧪 Test Runner 🧪
### Changed
* Add tests for Filter, Solid Rock and Prism Armor by @hedara90 in [#7734](https://github.com/rh-hideout/pokeemerald-expansion/pull/7734)
* Fix failing test for B_PREFERRED_ICE_WEATHER = B_ICE_WEATHER_SNOW by @phexmiau in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755)
* Initial Lash Out tests by @grintoul1 in [#7769](https://github.com/rh-hideout/pokeemerald-expansion/pull/7769)
* Improve how test involving ball throw work by @FosterProgramming in [#7774](https://github.com/rh-hideout/pokeemerald-expansion/pull/7774)
### Fixed
* Fix EWRAM_INIT in tests and add a default state to test runner main loop by @hedara90 in [#7699](https://github.com/rh-hideout/pokeemerald-expansion/pull/7699)
* Fix most failed and assume fail tests with `GEN_LATEST` = `GEN_6` by @AsparagusEduardo in [#7696](https://github.com/rh-hideout/pokeemerald-expansion/pull/7696)
* Fix for Levitate and Mold Breaker being seen correctly by switch AI, with Levitate tests by @grintoul1 in [#7748](https://github.com/rh-hideout/pokeemerald-expansion/pull/7748)
* Fix Big Root tests by @hedara90 in [#7817](https://github.com/rh-hideout/pokeemerald-expansion/pull/7817)
## 📚 Documentation 📚
* Fixup add-new-trainer-front-pic tutorial. by @GraionDilach in [#7802](https://github.com/rh-hideout/pokeemerald-expansion/pull/7802)
## New Contributors
* @phexmiau made their first contribution in [#7755](https://github.com/rh-hideout/pokeemerald-expansion/pull/7755)
* @ghostyboyy97 made their first contribution in [#7824](https://github.com/rh-hideout/pokeemerald-expansion/pull/7824)
**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.13.1...expansion/1.13.2
<!--Last PR: 7814-->
<!--Used to keep track of the last PR merged in case new ones come in before the changelog is done.-->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 388 B

View File

@ -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[];

View File

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

View File

@ -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,

View File

@ -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.

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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) (( \

View File

@ -0,0 +1,401 @@
import re
import sys
import os
is_blank = re.compile(r'^[ \t]*(//.*)?$')
begin_party_definition = re.compile(r'struct TrainerMon (\w+)\[\] =')
end_party_definition = re.compile(r'^ },')
begin_pokemon_definition = re.compile(r'^ { *$')
end_pokemon_definition = re.compile(r'^ },? *$')
level_definition = re.compile(r'\.lvl = (\d+)')
species_definition = re.compile(r'\.species = SPECIES_(\w+)')
gender_definition = re.compile(r'\.gender = TRAINER_MON_(\w+)')
nickname_definition = re.compile(r'\.nickname = COMPOUND_STRING\("([^"]+)"\)')
item_definition = re.compile(r'\.heldItem = ITEM_(\w+)')
ball_definition = re.compile(r'\.ball = ITEM_(\w+)')
ability_definition = re.compile(r'\.ability = ABILITY_(\w+)')
friendship_definition = re.compile(r'\.friendship = (\d+)')
shiny_definition = re.compile(r'\.isShiny = (\w+)')
ivs_definition = re.compile(r'\.iv = TRAINER_PARTY_IVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)')
evs_definition = re.compile(r'\.ev = TRAINER_PARTY_EVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)')
moves_definition = re.compile(r'\.moves = \{([^}]+)\}')
move_definition = re.compile(r'MOVE_(\w+)')
nature_definition = re.compile(r'NATURE_(\w+)')
is_trainer_skip = re.compile(r'(const struct Trainer gTrainers\[\] = \{)|(^ \{$)|(\.partySize =)|(\.party = NULL)|(\.mugshotEnabled = TRUE)|(\};)')
trainer_normal_definition = re.compile(r' \[DIFFICULTY_NORMAL\]\[(TRAINER_\w+)\] =')
trainer_easy_definition = re.compile(r' \[DIFFICULTY_EASY\]\[(TRAINER_\w+)\] =')
trainer_hard_definition = re.compile(r' \[DIFFICULTY_HARD\]\[(TRAINER_\w+)\] =')
end_pokemon_definition = re.compile(r' },')
trainer_class_definition = re.compile(r'\.trainerClass = TRAINER_CLASS_(\w+)')
encounter_music_gender_definition = re.compile(r'\.encounterMusic_gender = (F_TRAINER_FEMALE \| )?TRAINER_ENCOUNTER_MUSIC_(\w+)')
encounter_music_definition = re.compile(r'TRAINER_ENCOUNTER_MUSIC_(\w+)')
trainer_pic_definition = re.compile(r'\.trainerPic = TRAINER_PIC_(\w+)')
trainer_name_definition = re.compile(r'\.trainerName = _\("([^"]*)"\)')
trainer_items_definition = re.compile(r'\.items = \{([^}]*)\}')
trainer_item_definition = re.compile(r'ITEM_(\w+)')
trainer_double_battle_definition = re.compile(r'\.battleType = (\w+)')
trainer_ai_flags_definition = re.compile(r'\.aiFlags = (.*)')
trainer_ai_flag_definition = re.compile(r'AI_FLAG_(\w+)')
trainer_party_definition = re.compile(r'\.party = ')
trainer_mugshot_definition = re.compile(r'\.mugshotColor = MUGSHOT_COLOR_(\w+)')
trainer_starting_status_definition = re.compile(r'\.startingStatus = STARTING_STATUS_(\w+)')
# NOTE: These are just for aesthetics, the Pokemon would still compile
# without them.
species_replacements = {
"CHIEN_PAO": "Chien-Pao",
"CHI_YU": "Chi-Yu",
"HAKAMO_O": "Hakamo-o",
"HO_OH": "Ho-Oh",
"JANGMO_O": "Jangmo-o",
"KOMMO_O": "Kommo-o",
"PORYGON_Z": "Porygon-Z",
"ROTOM_": "Rotom-",
"TING_LU": "Ting-Lu",
"TYPE_NULL": "Type: Null",
"WO_CHIEN": "Wo-Chien",
"_ALOLAN": "-Alola",
"_AQUA_BREED": "-Aqua",
"_BATTLE_BOND": "-Bond",
"_BLAZE_BREED": "-Blaze",
"_CAP": "",
"_CLOAK": "",
"_COMBAT_BREED": "-Combat",
"_CROWED_SHIELD": "-Crowned",
"_CROWED_SWORD": "-Crowned",
"_DRIVE": "",
"_EAST_SEA": "-East",
"_FAMILY_OF_FOUR": "-Four",
"_FEMALE": "-F",
"_FLOWER": "",
"_GALARIAN": "-Galar",
"_GIGANTAMAX": "-Gmax",
"_HISUIAN": "-Hisui",
"_ICE_RIDER": "-Ice",
"_NOICE_FACE": "-Noice",
"_ORIGIN": "-Origin",
"_ORIGINAL_COLOR": "-Original",
"_PALDEAN": "-Paldea",
"_PLUMAGE": "",
"_POKE_BALL": "-Pokeball",
"_SHADOW_RIDER": "-Shadow",
"_STRIKE_STYLE": "-Style",
"_TOTEM": "-Totem",
"_ZEN_MODE": "-Zen",
}
class_fixups = {
"Rs": "RS",
}
pic_fixups = {
"Rs": "RS",
}
pokemon_attribute_order = ['Level', 'Ability', 'IVs', 'EVs', 'Happiness', 'Shiny', 'Ball']
class Pokemon:
def __init__(self):
self.nickname = None
self.species = None
self.gender = None
self.item = None
self.nature = None
self.attributes = {}
self.attributes['IVs'] = "0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe"
self.moves = []
class Trainer:
def __init__(self, id_):
self.id = id_
self.class_ = None
self.encounter_music = None
self.gender = None
self.pic = None
self.name = None
self.items = []
self.double_battle = None
self.ai_flags = None
self.mugshot = None
self.starting_status = None
self.party = None
self.difficulty = None
def write_tutorial(output):
output.write('/*\n')
output.write('Trainers and their parties defined with Competetive Syntax.\n')
output.write('Compatible with Pokemon Showdown exports.\n')
output.write('https://github.com/smogon/pokemon-showdown/blob/master/sim/TEAMS.md\n')
output.write('\n')
output.write('A trainer specification starts with "=== TRAINER_XXXX ==="\n')
output.write('and includes everything until the next line that starts with "==="\n')
output.write('or the file ends.\n')
output.write('\n')
output.write('A blank line is required between the trainer and their Pokemon\n')
output.write('and between their Pokemon.\n')
output.write('TRAINER_XXXX is how the trainer is referred to within code.\n')
output.write('Fields with description and/or example of usage\n')
output.write('Required fields for trainers:\n')
output.write(' - Name\n')
output.write(' - Pic\n')
output.write('Optional (but still recommended) fields for trainers:\n')
output.write(' - Class (if not specified, PkMn Trainer will be used)\n')
output.write(' - Gender (Male/Female, affects random gender weights of party if not specified)\n')
output.write(' - Music\n')
output.write(' - Items (Some Item / Another Item / Third Item)\n')
output.write(' (Can also be specified with ITEM_SOME_ITEM)\n')
output.write(' - Battle Type (Singles / Doubles, defaults to Singles)\n')
output.write(' - AI (Ai Flag / Another Flag / Third Flag / ...\n')
output.write(' see "constants/battle_ai.h" for all flags)\n')
output.write(' - Mugshot (enable Mugshots during battle transition\n')
output.write(' set to one of Purple, Green, Pink, Blue or Yellow)\n')
output.write(' - Starting Status (see include/constants/battle.h for values)\n')
output.write('\n')
output.write('Pokemon are then specified using the Showdown Export format.\n')
output.write("If a field is not specified, it will use it's default value.\n")
output.write("\n")
output.write('Required fields for Pokemon:\n')
output.write(' - Species (Either as SPECIES_ABRA or Abra)\n')
output.write(' This line also specifies Gender, Nickname and Held item.\n')
output.write(' Alfred (Abra) (M) @ Eviolite\n')
output.write(' Roberta (SPECIES_ABRA) (F) @ ITEM_CHOICE_SPECS\n')
output.write(' Both lines are valid. Gender (M) or (F) must use a capital letter.\n')
output.write(' Nickname length is limited to 10 characters using standard letters.\n')
output.write(" With narrow font it's increased to 12. Longer strings will be silently shortened.\n")
output.write('\n')
output.write('Optional fields for Pokemon:\n')
output.write(' - Level (Number between 1 and 100, defaults to 100)\n')
output.write(' - Ability (Ability Name or ABILITY_ABILITY_NAME)\n')
output.write(' - IVs (0 HP / 1 Atk / 2 Def / 3 SpA / 4 SpD / 5 Spe, defaults to all 31)\n')
output.write(' (Order does not matter)\n')
output.write(' - EVs (252 HP / 128 Spe / 48 Def, defaults to all 0, is not capped at 512 total)\n')
output.write(' (Order does not matter)\n')
output.write(' - Ball (Poke Ball or ITEM_POKE_BALL, defaults to Poke Ball)\n')
output.write(' - Happiness (Number between 1 and 255)\n')
output.write(' - Nature (Rash or NATURE_RASH, defaults to Hardy)\n')
output.write(' - Shiny (Yes/No, defaults to No)\n')
output.write(' - Dynamax Level (Number between 0 and 10, default 10, also sets "shouldDynamax" to True)\n')
output.write(' - Gigantamax (Yes/No, sets to Gigantamax factor)\n')
output.write(' (doesn\'t do anything to Pokemon without a Gigantamax form, also sets "shouldDynamax" to True)\n')
output.write(' - Tera Type (Set to a Type, either Fire or TYPE_FIRE, also sets "shouldTerastal" to True)\n')
output.write('Moves are defined with a - (dash) followed by a single space, then the move name.\n')
output.write('Either "- Tackle" or "- MOVE_TACKLE" works. One move per line.\n')
output.write('Moves have to be the last lines of a Pokemon.\n')
output.write('If no moves are specified, the Pokemon will use the last 4 moves it learns\n')
output.write('through levelup at its level.\n')
output.write("\n")
output.write('Default IVs and Level can be changed in the "main" function of tools/trainerproc/main.c\n')
output.write("\n")
output.write('This file is processed with a custom preprocessor.\n')
output.write('*/\n')
output.write("\n")
output.write('/*\n')
output.write('Comments can be added as C comment blocks\n')
output.write('// cannot be used as comments\n')
output.write('*/\n')
output.write("\n")
output.write('/*Comments can also be on a single line*/\n')
output.write("\n")
output.write("\n")
def write_to_file(trainer, output):
output.write(f'=== {trainer.id} ===\n')
output.write(f'Name: {trainer.name}\n')
output.write(f'Class: {trainer.class_}\n')
output.write(f'Pic: {trainer.pic}\n')
output.write(f'Gender: {trainer.gender}\n')
output.write(f'Music: {trainer.encounter_music}\n')
if len(trainer.items) > 0:
output.write(f'Items: {trainer.items}\n')
output.write(f'Battle Type: {trainer.double_battle}\n')
if trainer.ai_flags is not None:
output.write(f'AI: {trainer.ai_flags}\n')
if trainer.mugshot:
output.write(f'Mugshot: {trainer.mugshot}\n')
if trainer.difficulty is not None:
output.write(f'Difficulty: {trainer.difficulty}\n')
output.write(f'\n')
for pokemon in trainer.party:
if pokemon.species is None:
continue
if pokemon.item is not None:
output.write(f'{pokemon.species} @ {pokemon.item}\n')
else:
output.write(f'{pokemon.species}\n')
# for key in pokemon.attributes:
for key in pokemon_attribute_order:
if key in pokemon.attributes:
output.write(f'{key}: {pokemon.attributes[key]}\n')
if pokemon.nature:
output.write(f'Nature: {pokemon.nature}\n')
for move in pokemon.moves:
output.write(f'- {move}\n')
output.write(f'\n')
def parse_trainers(content, output):
newlines = 0
trainer = None
pokemon = None
party = []
moves = []
write_tutorial(output)
for line_no, line in enumerate(content, 1):
try:
line = line[:-1]
# Trainer defition
if m := trainer_normal_definition.search(line):
if trainer is not None:
trainer.party = party
write_to_file(trainer, output)
trainer = None
party = []
[id_] = m.groups()
trainer = Trainer(id_)
trainer.difficulty = None
trainer.gender = 'Male'
elif m := trainer_easy_definition.search(line):
if trainer is not None:
trainer.party = party
write_to_file(trainer, output)
trainer = None
party = []
[id_] = m.groups()
trainer = Trainer(id_)
trainer.difficulty = "Easy"
trainer.gender = 'Male'
elif m := trainer_hard_definition.search(line):
if trainer is not None:
trainer.party = party
write_to_file(trainer, output)
trainer = None
party = []
[id_] = m.groups()
trainer = Trainer(id_)
trainer.difficulty = "Hard"
trainer.gender = 'Male'
elif m := trainer_class_definition.search(line):
[class_] = m.groups()
class_ = class_.replace("_", " ").title()
for match, replacement in class_fixups.items():
class_ = class_.replace(match, replacement)
trainer.class_ = class_
elif m := encounter_music_gender_definition.search(line):
[is_female, music] = m.groups()
trainer.gender = 'Female' if is_female else 'Male'
trainer.encounter_music = music.replace("_", " ").title()
elif m := encounter_music_definition.search(line):
[music] = m.groups()
trainer.encounter_music = music.replace("_", " ").title()
elif "F_TRAINER_FEMALE" in line:
trainer.gender = 'Female'
elif m := trainer_pic_definition.search(line):
[pic] = m.groups()
pic = pic.replace("_", " ").title()
for match, replacement in pic_fixups.items():
pic = pic.replace(match, replacement)
trainer.pic = pic
elif m := trainer_name_definition.search(line):
[name] = m.groups()
trainer.name = name
elif m := trainer_items_definition.search(line):
[items] = m.groups()
trainer.items = " / ".join(item.replace("_", " ").title() for item in trainer_item_definition.findall(items) if item != "NONE")
elif m := trainer_double_battle_definition.search(line):
[double_battle] = m.groups()
if double_battle == 'TRAINER_BATTLE_TYPE_DOUBLES':
trainer.double_battle = "Doubles"
elif double_battle == 'TRAINER_BATTLE_TYPE_SINGLES':
trainer.double_battle = "Singles"
elif m := trainer_ai_flags_definition.search(line):
[ai_flags] = m.groups()
trainer.ai_flags = " / ".join(ai_flag.replace("_", " ").title() for ai_flag in trainer_ai_flag_definition.findall(ai_flags))
elif m := trainer_mugshot_definition.search(line):
[color] = m.groups()
trainer.mugshot = color.title()
elif m := trainer_starting_status_definition.search(line):
[starting_status] = m.groups()
trainer.starting_status = starting_status.replace("_", " ").title()
elif m := trainer_party_definition.search(line):
pokemon = Pokemon()
# Party mons
elif end_pokemon_definition.search(line):
party.append(pokemon)
pokemon = Pokemon()
elif m := level_definition.search(line):
[level] = m.groups()
pokemon.attributes['Level'] = level
elif m := species_definition.search(line):
[species_] = m.groups()
for match, replacement in species_replacements.items():
species_ = species_.replace(match, replacement)
pokemon.species = species_.replace("_", " ").title()
elif m := gender_definition.search(line):
[gender_] = m.groups()
if gender_ == 'MALE':
pokemon.gender = 'M'
elif gender_ == 'FEMALE':
pokemon.gender = 'F'
elif m := nickname_definition.search(line):
[nickname] = m.groups()
pokemon.nickname = nickname
elif m := item_definition.search(line):
[item_] = m.groups()
pokemon.item = item_.replace("_", " ").title()
elif m := ball_definition.search(line):
[ball] = m.groups()
pokemon.attributes['Ball'] = ball.replace("_", " ").title()
elif m := ability_definition.search(line):
[ability] = m.groups()
pokemon.attributes['Ability'] = ability.replace("_", " ").title()
elif m := friendship_definition.search(line):
[friendship] = m.groups()
pokemon.attributes['Happiness'] = friendship
elif m := shiny_definition.search(line):
[shiny] = m.groups()
if shiny == 'TRUE':
pokemon.attributes['Shiny'] = 'Yes'
elif shiny == 'FALSE':
pokemon.attributes['Shiny'] = 'No'
elif m := ivs_definition.search(line):
[hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()]
stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed}
pokemon.attributes['IVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items())
elif m := evs_definition.search(line):
[hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()]
stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed}
pokemon.attributes['EVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items() if value != '0')
elif m := move_definition.search(line):
[move] = m.groups()
pokemon.moves.append(move.replace("_", " ").title())
elif m := nature_definition.search(line):
[nature] = m.groups()
pokemon.nature = nature.replace("_", " ").title()
except Exception as e:
print(f"{line_no}: {e}")
if __name__ == '__main__':
try:
[argv0, trainers_in_path, out_path] = sys.argv
except:
print(f"usage: python3 {sys.argv[0]} <trainers.h> <out>")
print("trainers.h path: src/data/trainers.h")
print("trainers.party output path: src/data/trainers.party")
else:
with open(trainers_in_path, "r") as source, open(out_path, 'w') as output:
parse_trainers(source, output)

View File

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

View File

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

View File

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

View File

@ -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];

View File

@ -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)
{

View File

@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

@ -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:

View File

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

View File

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

View File

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

View File

@ -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"

View File

@ -1282,6 +1282,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
{
.name = _("防弹"),
.description = COMPOUND_STRING("防守球和弹类招式"),
.breakable = TRUE,
.aiRating = 7,
},

View File

@ -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,

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -31,22 +31,35 @@ SINGLE_BATTLE_TEST("Oblivious prevents Captivate")
}
}
SINGLE_BATTLE_TEST("Oblivious prevents Taunt")
SINGLE_BATTLE_TEST("Oblivious prevents Taunt (Gen6+)")
{
u32 gen = 0;
PARAMETRIZE { gen = GEN_5; }
PARAMETRIZE { gen = GEN_6; }
GIVEN {
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);
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -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 {

View File

@ -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")

View File

@ -56,11 +56,21 @@ SINGLE_BATTLE_TEST("Supersweet Syrup can not further lower opponents evasion if
TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
TURN { MOVE(opponent, MOVE_SWEET_SCENT); }
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);

View File

@ -59,7 +59,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: Considers ShouldSwitch and GetMos
// Switching in trapper is an advanced feature of ShouldSwitch that requires GetMostSuitableMonToSwitchInto to also return a specific mon; this passing means the AI can use both in prediction
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); }

View File

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

View File

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

View File

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

View File

@ -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.

View File

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

View File

@ -38,6 +38,7 @@ SINGLE_BATTLE_TEST("Booster Energy will activate Quark Drive after Electric Terr
SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunlight ends")
{
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 {

View File

@ -338,3 +338,26 @@ SINGLE_BATTLE_TEST("Eject Pack does not activate if mon is switched in due to Ej
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
}
}
DOUBLE_BATTLE_TEST("Eject Pack will trigger on the fastest mon at the end of the turn")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_SYRUP_BOMB, MOVE_EFFECT_SYRUP_BOMB) == TRUE);
PLAYER(SPECIES_WOBBUFFET) { Speed(1); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT) { Speed(10); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_SYRUP_BOMB, target: playerLeft);
MOVE(opponentRight, MOVE_SYRUP_BOMB, target: playerRight);
SEND_OUT(playerRight, 2);
}
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SYRUP_BOMB_SPEED_DROP, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SYRUP_BOMB_SPEED_DROP, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
}
}

View File

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

View File

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

View File

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

View File

@ -30,7 +30,6 @@ SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consume
PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_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);

View File

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

View File

@ -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

View File

@ -12,15 +12,14 @@ SINGLE_BATTLE_TEST("Fickle Beam deals double damage 30% of the time")
PASSES_RANDOMLY(30, 100, RNG_FICKLE_BEAM);
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]);

View File

@ -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!");

View File

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

View File

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

View File

@ -149,9 +149,10 @@ DOUBLE_BATTLE_TEST("Powder fails if target is already affected by Powder")
}
}
SINGLE_BATTLE_TEST("Powder fails if the target is Grass type")
SINGLE_BATTLE_TEST("Powder fails if the target is Grass type (Gen6+)")
{
GIVEN {
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 {

View File

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

View File

@ -43,14 +43,16 @@ SINGLE_BATTLE_TEST("Toxic can't bad poison a poison or steel type")
}
}
SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type")
SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type (Gen6+)")
{
u32 species;
u32 species, gen;
bool32 hit;
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);

View File

@ -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 {

View File

@ -21,19 +21,27 @@ SINGLE_BATTLE_TEST("Thunder Shock inflicts paralysis")
}
}
SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type")
SINGLE_BATTLE_TEST("Thunder Shock cannot paralyze an Electric-type (Gen6+)")
{
u32 gen = 0;
PARAMETRIZE { gen = GEN_5; }
PARAMETRIZE { gen = GEN_6; }
GIVEN {
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);
}

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -22,11 +22,25 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
}
}
SINGLE_BATTLE_TEST("Sleep: Spore doesn't affect grass types (Gen 6+)")
SINGLE_BATTLE_TEST("Sleep: Spore affects grass types (Gen1-5)")
{
GIVEN {
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 {

View File

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