Merge branch '_RHH/master' into _RHH/upcoming

This commit is contained in:
Eduardo Quezada 2024-08-20 08:00:05 -04:00
commit 6ac18a6fe5
84 changed files with 1708 additions and 320 deletions

View File

@ -23,9 +23,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using as a base?
options:
- 1.9.0 (Latest release)
- 1.9.1 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
- 1.9.0
- 1.8.6
- 1.8.5
- 1.8.4

View File

@ -23,9 +23,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using as a base?
options:
- 1.9.0 (Latest release)
- 1.9.1 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
- 1.9.0
- 1.8.6
- 1.8.5
- 1.8.4

View File

@ -23,9 +23,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using as a base?
options:
- 1.9.0 (Latest release)
- 1.9.1 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
- 1.9.0
- 1.8.6
- 1.8.5
- 1.8.4

View File

@ -1,6 +1,7 @@
# Pokeemerald-Expansion Changelogs
## 1.9.x
- **[Version 1.9.1](docs/changelogs/1.9.x/1.9.1.md) - 🧹 Bugfix Release**
- **[Version 1.9.0](docs/changelogs/1.9.x/1.9.0.md) - ✨ Feature Release**
## 1.8.x

View File

@ -327,7 +327,7 @@ If this works, then proceed to [Installation](#installation). Otherwise, ask for
libpng is now installed.
Continue to [Installing pkg-config (macOS)](#installing-pkg-config-macos) if **pkg-config is not installed**. Otherwise, continue to [Installing devkitARM (macOS)](#installing-devkitarm-macos) if **devkitARM is not installed**.
If both pkg-config and devkitARM are already installed, go to [Choosing where to store pokeemerald Expansion (macOS)](#choosing-where-to-store-pokeemerald-expansion-macos).
### Installing pkg-config (macOS)
@ -541,7 +541,7 @@ If this works, then proceed to [Installation](#installation). Otherwise, ask for
> ```
> Where *\<folder where pokeemerald-expansion is to be stored>* is the path of the folder [where you chose to store pokeemerald Expansion](#Choosing-where-to-store-pokeemerald-expansion-WSL1). Then run the `git clone` command again.
</details>
Now you're ready to build pokeemerald Expansion.
## Build pokeemerald Expansion

View File

@ -9,7 +9,7 @@ pokeemerald-expansion is a decomp hack base project based off pret's [pokeemeral
If you use pokeemerald-expansion in your hack, please add RHH (Rom Hacking Hideout) to your credits list. Optionally, you can list the version used, so it can help players know what features to expect.
You can phrase it as the following:
```
Based off RHH's pokeemerald-expansion 1.9.0 https://github.com/rh-hideout/pokeemerald-expansion/
Based off RHH's pokeemerald-expansion 1.9.1 https://github.com/rh-hideout/pokeemerald-expansion/
```
## What features are included?

View File

@ -7150,7 +7150,7 @@ BattleScript_TargetFormChangeWithStringNoPopup::
BattleScript_BattlerFormChangeWithStringEnd3::
pause 5
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpScripting
flushtextbox
handleformchange BS_SCRIPTING, 0
handleformchange BS_SCRIPTING, 1
@ -7754,6 +7754,15 @@ BattleScript_CheekPouchActivates::
copybyte gBattlerAttacker, sSAVED_BATTLER
return
BattleScript_PickupActivates::
pause 5
tryrecycleitem BattleScript_PickupActivatesEnd
call BattleScript_AbilityPopUp
printstring STRINGID_XFOUNDONEY
waitmessage B_WAIT_TIME_LONG
BattleScript_PickupActivatesEnd:
end3
BattleScript_HarvestActivates::
pause 5
tryrecycleitem BattleScript_HarvestActivatesEnd
@ -8001,7 +8010,7 @@ BattleScript_DeltaStreamActivates::
end3
BattleScript_ProtosynthesisActivates::
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpScripting
printstring STRINGID_SUNLIGHTACTIVATEDABILITY
waitmessage B_WAIT_TIME_MED
printstring STRINGID_STATWASHEIGHTENED

View File

@ -45,7 +45,7 @@ BattleScript_UseItemMessage:
printfromtable gTrainerUsedItemStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_ItemRestoreHPRet:
bichalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE

View File

@ -59,6 +59,6 @@ def rellocate_follower_graphics():
#os.popen('cp followers/' + name + '.png followers/' + name + '/follower.png')
#os.remove('followers/' + name + '.png')
#print(pth)
#subprocess.run(["tools/gbagfx/gbagfx " + name +".png " + name + "_normal.pal'" + str(count) + "'"])
#subprocess.run(["tools/gbagfx/gbagfx " + name +".png " + name + "_normal.pal'" + str(count) + "'"])
rellocate_follower_graphics()

View File

@ -12,10 +12,10 @@
- [How to add a new move](./how_to_new_move.md)
- [How to add a new trainer class](./how_to_trainer_class.md)
- [How to add a new Pokémon]()
- [v1.9.0](./how_to_new_pokemon_1_9_0.md)
- [v1.8.0](./how_to_new_pokemon_1_8_0.md)
- [v1.7.0](./how_to_new_pokemon_1_7_0.md)
- [v1.6.0](./how_to_new_pokemon_1_6_0.md)
- [v1.9.x](./how_to_new_pokemon_1_9_0.md)
- [v1.8.x](./how_to_new_pokemon_1_8_0.md)
- [v1.7.x](./how_to_new_pokemon_1_7_0.md)
- [v1.6.x](./how_to_new_pokemon_1_6_0.md)
- [How to use the Testing System](./how_to_testing_system.md)
- [Changelog](./CHANGELOG.md)
- [1.9.x]()

View File

@ -21,14 +21,14 @@ AI: Check Bad Move / Try to Faint / Check Viability. The name of each flag is ju
* Sequence Switching
## `COMPETITIVE_PARTY_SYNTAX != TRUE` / Not Found
If you are not using competitive syntax parties, instead access the trainer data directly in [`src/data/trainers.h`](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/trainers.h), and add flags like so, typed exactly the same as the flag names themselves:
If you are not using competitive syntax parties, instead access the trainer data directly in [`src/data/trainers.h`](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/trainers.h), and add flags like so, typed exactly the same as the flag names themselves:
`.aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY`
# What AI Flags does pokeemerald-expansion have?
This section lists all of expansions AI Flags and briefly describes the effect they have on the AIs behaviour. In all cases, please check the corresponding function or surrounding code around their implementation for more details. Some of these functions are vanilla, some share a name with vanilla but have been modified to varying degrees, and some are completely new.
## `AI_FLAG_CHECK_BAD_MOVE`
The AI will avoid using moves that are likely to fail in the current situation. This flag helps prevent the AI from making ineffective choices, such as using moves into immunities, into invulnerable states, or when the moves are otherwise hindered by abilities, terrain, or status conditions.
The AI will avoid using moves that are likely to fail in the current situation. This flag helps prevent the AI from making ineffective choices, such as using moves into immunities, into invulnerable states, or when the moves are otherwise hindered by abilities, terrain, or status conditions.
## `AI_FLAG_TRY_TO_FAINT`
AI will prioritize KOing the player if able rather than using status moves. Will prioritize using a move that can OHKO the player. If the player can KO the AIs mon and the AIs mon is slower, prioritize priority moves (this does not prevent the AI from switching out instead).
@ -58,12 +58,12 @@ AI will generally behave more recklessly. This AI enables the following behaviou
* Prioritize Explosion moves
## `AI_FLAG_PREFER_STRONGEST_MOVE`
Adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
Adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
Keep in mind that this is a weaker form of `AI_FLAG_TRY_TO_FAINT` at scoring OHKOs as it does not take into account who is attacking first, it does however handle 2HKOs.
## `AI_FLAG_PREFER_BATON_PASS`
AI prefers raising its own stats if it has >= 60% HP, as well as Ingrain, Aqua Ring, and Protect. Prioritizes Baton Bass if the mon is rooted (Ingrain) or has the Aqua Ring effect, and doesnt if it has been Leech Seeded.
AI prefers raising its own stats if it has >= 60% HP, as well as Ingrain, Aqua Ring, and Protect. Prioritizes Baton Bass if the mon is rooted (Ingrain) or has the Aqua Ring effect, and doesnt if it has been Leech Seeded.
## `AI_FLAG_DOUBLE_BATTLE`
This flag is automatically set in double battles, and controls much of the doubles-specific scoring. Ill summarize some of its scoring as follows:
@ -83,7 +83,7 @@ With respect to the AIs mons, in doubles:
In both singles and doubles:
* Prioritizes not using moves that require the user fainting (Destiny Bond, Explosion etc.) and healing moves while on >= 70% HP.
* Prioritize not using moves that require the user fainting or losing significant HP (Belly Drum etc) while between 30% and 70% HP
* Prioritize not using setup moves (Light Screen etc.) and Bide while on <= 30% HP
* Prioritize not using setup moves (Light Screen etc.) and Bide while on <= 30% HP
With respect to the players mons:
* Prioritize not using many status moves (stat buffs, Poison, Pain Split) if the player has between 30% and 70% HP
@ -96,7 +96,7 @@ AI prioritizes setting up field effects (Trick Room, Rain Dance, etc.) and side
AI does not understand ability suppression (Mold Breaker etc., weather suppression (Air Lock etc.), redirection abilities (Lightningrod etc.) being temporarily removed due to move effects (Sky Drop etc.), or item suppression (Magic Room etc.) and will ignore them. This is a handicap flag.
## `AI_FLAG_WILL_SUICIDE`
AI prioritizes self destruction moves (Explosion, Memento).
AI prioritizes self destruction moves (Explosion, Memento).
## `AI_FLAG_PREFER_STATUS_MOVES`
AI gets a score bonus for status moves. This should be combined with `AI_FLAG_CHECK_BAD_MOVE` to prevent using only status moves.
@ -127,7 +127,7 @@ Affects when the AI chooses to switch. AI will make smarter decisions about when
Marks the last Pokemon in the party as the Ace Pokemon. It will not be used unless it is the last one remaining, or is forced to be switched in (Roar, U-Turn with 1 mon remaining, etc.)
## `AI_FLAG_OMNISCIENT`
AI has full knowledge of player moves, abilities, and hold items, and can use this knowledge when making decisions.
AI has full knowledge of player moves, abilities, and hold items, and can use this knowledge when making decisions.
## `AI_FLAG_SMART_MON_CHOICES`
Affects what the AI chooses to send out after a switch. AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are handled separately. Automatically included when `AI_FLAG_SMART_SWITCHING` is enabled.
@ -148,7 +148,7 @@ And will choose mons after a mid-battle switch prioritizing the following criter
* Has Baton Pass
## `AI_FLAG_CONSERVATIVE`
AI always assumes it will roll the lowest possible result when comparing damage in scoring.
AI always assumes it will roll the lowest possible result when comparing damage in scoring.
## `AI_FLAG_SEQUENCE_SWITCHING`
AI will always switch out after a KO in exactly party order as defined in the trainer data (ie. slot 1, then 2, then 3, etc.). The AI will never switch out mid-battle unless forced to (Roar etc.). If the AI uses a move that requires a switch where it makes a decision about what to send in (U-Turn etc.), it will always switch out into the lowest available party index.

View File

@ -92,5 +92,5 @@
* Court Change by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3160
* Item Effects
* Utility Umbrella, by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/2835
**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.5.1...expansion/1.5.2

View File

@ -9,8 +9,8 @@
## CRITICAL FIX, please update to avoid the issues detailed down below:
- Fixed memory corruption when handling trigger sprites by @SBird1337 in https://github.com/rh-hideout/pokeemerald-expansion/pull/3238
- This had the posibility of manifesting in weird ways, like camera and music changes, NPC duplication and more. If you've had this issue in the past, we ***heavily*** recommend you update to this version of the expansion.
- Thank you @Bassoonian for helping us pinpointing the issue.
- Thank you @Bassoonian for helping us pinpointing the issue.
![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/26b9b984-c5db-4dac-85f7-5fc4e95a32ce) ![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/d490eb30-ce54-4b90-bb2e-79c2e9bb50ac)

View File

@ -41,7 +41,7 @@
* Clodsire
* Crocalor
* Dolliv
* Dudunsparce
* Dudunsparce
* Esparthra
### Fixed
* Multiple Pokémon graphical fixes by @katykat5099 in https://github.com/rh-hideout/pokeemerald-expansion/pull/3805

View File

@ -0,0 +1,140 @@
# Version 1.9.1
```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.9.1`.
```
## 🌋 *REFACTORS* 🌋
* Removed `ENDTURN_RETALIATE` in [#5182](https://github.com/rh-hideout/pokeemerald-expansion/pull/5182)
* Removed `ENDTURN_WEATHER_FORM` and `allowedToChangeFormInWeather` in [#5171](https://github.com/rh-hideout/pokeemerald-expansion/pull/5171)
## 🧬 General 🧬
### Added
* Move Relearner UI now displays move category by @kittenchilly in [#5081](https://github.com/rh-hideout/pokeemerald-expansion/pull/5081)
### Fixed
* Fixes wrong padding field in `SpeciesInfo` struct by @AlexOn1ine in [#5139](https://github.com/rh-hideout/pokeemerald-expansion/pull/5139)
* Fixed specific tiles changing to PC tiles when using Box Link/Debug PC option by @cawtds in [#5141](https://github.com/rh-hideout/pokeemerald-expansion/pull/5141)
## 🐉 Pokémon 🐉
### Fixed
* Fixed stray transparent pixels in Urshifu sprites by @hedara90 in [#5071](https://github.com/rh-hideout/pokeemerald-expansion/pull/5071)
* Fixed `bufferspeciesname` not working for species IDs over 1023 by @SBird1337 in [#5088](https://github.com/rh-hideout/pokeemerald-expansion/pull/5088)
* Fixed overworld Pokémon breaking for species IDs above 1535 by @hedara90, @mrgriffin and
@SarnPoke in [#5179](https://github.com/rh-hideout/pokeemerald-expansion/pull/5179)
* Fixed overworld palettes for multiple species by @hedara90 in [#5107](https://github.com/rh-hideout/pokeemerald-expansion/pull/5107)
* Dialga Origin (Normal and Shiny)
* Palkia Origin (Normal and Shiny)
* Giratina Origin (shiny palette fixed by @hedara90 in [#5108](https://github.com/rh-hideout/pokeemerald-expansion/pull/5108))
* Xerneas Neutral/Active (Normal and Shiny)
* Enamorus Incarnate/Therian (Normal and Shiny)
* Fixed/added missing Pokémon sprites and palettes by @Cafeei in [#5126](https://github.com/rh-hideout/pokeemerald-expansion/pull/5126)
* Overworld:
* Shiny Sneasler, Morelul, Bounsweet, Bruxish, Guzzlord, Regieleki, Zacian, Zamazenta
* Hisuian Zorua sprite
* Shiny Summer Sawsbuck
* Shiny Galarian Yamask, Darumaka, Zigzagoon, Zapdos, Ponyta, Rapidash, Slowpoke, Farfetch'd, Weezing, Mr. Mime, Articuno, Moltres, Slowking, Stunfisk, Darmanitan
* Shiny Hisuian Sneasel, Qwilfish, Samurott,
* Battle sprites:
* Shiny Sneasler, Cursola, Pincurchin, Runerigus
* Shiny Galarian Yamask, Darumaka
* Fixed Unown Overworld follower sprites by Sarn by @hedara90 in [#5146](https://github.com/rh-hideout/pokeemerald-expansion/pull/5146)
## ⚔️ Battle General ⚔️
### Changed
* Set new animation particles by default to off by @AlexOn1ine in [#5161](https://github.com/rh-hideout/pokeemerald-expansion/pull/5161)
### Fixed
* Fixed speed ties by @mrgriffin in [#4780](https://github.com/rh-hideout/pokeemerald-expansion/pull/4780)
* Cleanup by @hedara90 in [#5092](https://github.com/rh-hideout/pokeemerald-expansion/pull/5092)
* Fixed Defiant/Competitive not working after the battler enters the field with a Court Changed Sticky Web on its side of the field by @PhallenTree in [#5093](https://github.com/rh-hideout/pokeemerald-expansion/pull/5093)
* Fixed `trainerproc` not properly parsing line markers, which caused erroring lines to be offset by @mrgriffin in [#5122](https://github.com/rh-hideout/pokeemerald-expansion/pull/5122)
* Fixed initial Zigzagoon battle being able to use a Gimmick by @AlexOn1ine in [#5129](https://github.com/rh-hideout/pokeemerald-expansion/pull/5129)
* Fixed incorrect rounding when `maxHP` is lower than 16 by @hedara90 in [#5183](https://github.com/rh-hideout/pokeemerald-expansion/pull/5183)
* This caused these Pokémon to not be hurt by Sandstorm/Hail.
* Fixes UB in `Cmd_averagestats` by @mrgriffin and @AlexOn1ine in [#5191](https://github.com/rh-hideout/pokeemerald-expansion/pull/5191)
## 🤹 Moves 🤹
### Added
* Added move animations for multiple moves by @TheTrueSadfish in [#5159](https://github.com/rh-hideout/pokeemerald-expansion/pull/5159)
* Spin Out, Mortal Spin, Fillet Away, Flower Trick, Make It Rain, Shed Tail, Hyper Drill, Twin Beam, Comeuppance, Blood Moon, Fickle Beam, Thunder Clap, Hard Press, Dragon Cheer, Malignant Chain.
* Purple chains by ogwon on Discord, beam by @TheTrueSadfish and livra on Discord.
### Changed
* Adjusted Raging Bull's animation to include Brick Break's wall break effect by @TheTrueSadfish in [#5159](https://github.com/rh-hideout/pokeemerald-expansion/pull/5159)
### Fixed
* Fixed non-grass Ivy Cudgel breaking battle UI by @hedara90 in [#5117](https://github.com/rh-hideout/pokeemerald-expansion/pull/5117)
* Fixes Stomping Tantrum effect not doubling power in certain situations by @AlexOn1ine in [#5140](https://github.com/rh-hideout/pokeemerald-expansion/pull/5140)
* Fixed Fickle Beam's description by @PhallenTree in [#5093](https://github.com/rh-hideout/pokeemerald-expansion/pull/5093)
* Fixed Revelation Dance interactions with Z-Move, Roost and typeless mons by @PhallenTree in [#5133](https://github.com/rh-hideout/pokeemerald-expansion/pull/5133)
* Fixes Poltergeist missing its accuracy check by @AlexOn1ine in [#5168](https://github.com/rh-hideout/pokeemerald-expansion/pull/5168)
* Fixed Fickle Beam not showing its message by @TheTrueSadfish in [#5159](https://github.com/rh-hideout/pokeemerald-expansion/pull/5159)
* Fixed Retaliate not working correctly if the party member fainted via passive damage during end of turn by @hedara90 in [#5182](https://github.com/rh-hideout/pokeemerald-expansion/pull/5182)
* Fixed Flame Burst's passive damage being based off current HP rather than Max HP by @hedara90 in [#5182](https://github.com/rh-hideout/pokeemerald-expansion/pull/5182)
* Fixed using Population Bomb with Loaded Dice printing garbage text by @kittenchilly in [#5195](https://github.com/rh-hideout/pokeemerald-expansion/pull/5195)
## 🎭 Abilities 🎭
### Added
* Added in-battle effect of Pickup by @PhallenTree in [#5170](https://github.com/rh-hideout/pokeemerald-expansion/pull/5170)
### Fixed
* Fixes Purifying Salt not halving damage for dynamic move types by @AlexOn1ine in [#5145](https://github.com/rh-hideout/pokeemerald-expansion/pull/5145)
* Fixed Dancer-called moves not changing their type based on the new user by @PhallenTree in [#5133](https://github.com/rh-hideout/pokeemerald-expansion/pull/5133)
* Fixed Ice Face not regenerating after switching in during Hail/Snow by @hedara90 in [#5171](https://github.com/rh-hideout/pokeemerald-expansion/pull/5171)
* Fixed Wind Rider not activating when switched in while Tailwind is active on the user's side of the field activation and tests by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/5207
## 🧶 Items 🧶
### Added
* Added Dowsing Machine's expanded name by @kittenchilly in [#5134](https://github.com/rh-hideout/pokeemerald-expansion/pull/5134)
### Fixed
* Fixes Booster Energy not increasing speed by @AlexOn1ine in [#5167](https://github.com/rh-hideout/pokeemerald-expansion/pull/5167)
## 🤖 Battle AI 🤖
### Changed
* Adjusted AI calculation for Triple Kick Effect by @AlexOn1ine in [#5127](https://github.com/rh-hideout/pokeemerald-expansion/pull/5127)
### Fixed
* Fix Switch AI Bug: AI never switching out when it could be OHKO'd by @Pawkkie in [#5089](https://github.com/rh-hideout/pokeemerald-expansion/pull/5089)
* Adds missing AI checks for poltergeist by @AlexOn1ine in [#5189](https://github.com/rh-hideout/pokeemerald-expansion/pull/5189)
## 🧹 Other Cleanup 🧹
* `IsValidForBattle` function formatting by @AlexOn1ine in [#5085](https://github.com/rh-hideout/pokeemerald-expansion/pull/5085)
* Opportunist/Mirror Herb cleanup by @AlexOn1ine in [#5120](https://github.com/rh-hideout/pokeemerald-expansion/pull/5120)
* Cleanup by @AlexOn1ine in [#5158](https://github.com/rh-hideout/pokeemerald-expansion/pull/5158)
* Remove trailing whitespace (master) by @AsparagusEduardo in [#5174](https://github.com/rh-hideout/pokeemerald-expansion/pull/5174)
## 🧪 Test Runner 🧪
### Added
* Added missing Move Effect TODO tests - Volume C by @AsparagusEduardo in [#5094](https://github.com/rh-hideout/pokeemerald-expansion/pull/5094)
* Added multiple missing ability TODO tests by @AsparagusEduardo in [#5163](https://github.com/rh-hideout/pokeemerald-expansion/pull/5163)
* Added missing Guard/Power split tests by @mrgriffin and @AlexOn1ine in [#5191](https://github.com/rh-hideout/pokeemerald-expansion/pull/5191)
* Added missing Harvest and Pickup tests by @PhallenTree in [#5170](https://github.com/rh-hideout/pokeemerald-expansion/pull/5170)
* Added missing Round tests by @hedara90 in https://github.com/rh-hideout/pokeemerald-expansion/pull/5196
* Added missing Wind Rider tests by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/5207
### Changed
* Fixed G-Max Replenish not considering Gen 5+ Pickup by @PhallenTree in [#5170](https://github.com/rh-hideout/pokeemerald-expansion/pull/5170)
### Fixed
* Fixed `RandomUniformExcept` not being exclusive on the higher boundary by @PhallenTree in [#5170](https://github.com/rh-hideout/pokeemerald-expansion/pull/5170)
## 📚 Documentation 📚
* Added guide to running documentation website locally by @AsparagusEduardo in [#5059](https://github.com/rh-hideout/pokeemerald-expansion/pull/5059)
* How to docs and fixes to be added to the mdbook documentation site by @anrichtait in [#5070](https://github.com/rh-hideout/pokeemerald-expansion/pull/5070)
* Improved 1.8 ⇒ 1.9 non-Competitive syntax migration instructions by @mrgriffin in [#5079](https://github.com/rh-hideout/pokeemerald-expansion/pull/5079)
## 📦 Branch Synchronisation 📦
### pret
* 5th of August in [#5098](https://github.com/rh-hideout/pokeemerald-expansion/pull/5098)
* Fixed bottom half of Mt. Pyre not being labeled in PokeNav by @fdeblasio in [pret#2018](https://github.com/pret/pokeemerald/pull/2018)
* 7th of August in [#5116](https://github.com/rh-hideout/pokeemerald-expansion/pull/5116)
* Changed type1 and type2 to be consistent by @pkmnsnfrn in [pret#2021](https://github.com/pret/pokeemerald/pull/2021)
* 14th of August in [#5165](https://github.com/rh-hideout/pokeemerald-expansion/pull/5165)
* Fix type for offset in MapConnection by @GriffinRichards in [pret#2011](https://github.com/pret/pokeemerald/pull/2011)
### Followers
* 7th of August in [#5110](https://github.com/rh-hideout/pokeemerald-expansion/pull/5110)
* Fixed expanded OW IDs by @pkmnsnfrn in [aarant#38](https://github.com/aarant/pokeemerald/pull/38)
* Fix two small text errors in follower dialogue by @Bassoonian in [aarant#39](https://github.com/aarant/pokeemerald/pull/39)
**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.0...expansion/1.9.1
## New Contributors
* @TheTrueSadfish made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/5159
<!--Last PR: 5196-->

View File

@ -122,7 +122,10 @@
### Fixed
* N/A
## 📦 Pret merges 📦
## 📦 Branch Synchronisation 📦
### pret's base pokeemerald
* N/A
### merrp/aarant's followers
* N/A

View File

@ -384,7 +384,7 @@ Edit [src/data/text/species_names.h](https://github.com/rh-hideout/pokeemerald-e
const u8 gSpeciesNames[][POKEMON_NAME_LENGTH + 1] = {
[SPECIES_NONE] = _("??????????"),
[SPECIES_BULBASAUR] = _("Bulbasaur"),
...
...
[SPECIES_ENAMORUS] = _("Enamorus"),
+ [SPECIES_MEWTHREE] = _("Mewthree"),
};
@ -482,7 +482,7 @@ Append to [src/data/pokemon/pokedex_text.h](https://github.com/rh-hideout/pokeem
"winter. According to legend, this\n"
"Pokémon's love gives rise to the\n"
"budding of fresh life across the land.");
+const u8 gMewthreePokedexText[] = _(
+ "The rumors became true.\n"
+ "This is Mews final form.\n"
@ -509,7 +509,7 @@ Edit [src/data/pokemon/pokedex_entries.h](https://github.com/rh-hideout/pokeemer
.trainerScale = 296,
.trainerOffset = 1,
},
+ [NATIONAL_DEX_MEWTHREE] =
+ {
+ .categoryName = _("NEW SPECIES"),
@ -553,7 +553,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera
NATIONAL_DEX_DUGTRIO,
...
};
const u16 gPokedexOrder_Height[] =
{
...
@ -598,7 +598,7 @@ Edit [src/data/pokemon/species_info.h](https://github.com/rh-hideout/pokeemerald
},
+ [SPECIES_MEWTHREE] =
+ {
+ {
+ .baseHP = 106,
+ .baseAttack = 150,
+ .baseDefense = 70,

View File

@ -170,7 +170,7 @@ Edit [src/data/pokemon/species_info.h](https://github.com/rh-hideout/pokeemerald
},
+ [SPECIES_MEWTHREE] =
+ {
+ {
+ .baseHP = 106,
+ .baseAttack = 150,
+ .baseDefense = 70,
@ -234,7 +234,7 @@ That's all the basic fields present in vanilla emerald, so now let's take a look
{
...
[SPECIES_MEWTHREE] =
{
{
...
.isLegendary = TRUE,
.allPerfectIVs = TRUE,
@ -309,7 +309,7 @@ Lastly, we add the cry to our species entry
{
...
[SPECIES_MEWTHREE] =
{
{
...
.isLegendary = TRUE,
.allPerfectIVs = TRUE,
@ -388,7 +388,7 @@ Now we can add the number and entry to our Mewthree:
{
...
[SPECIES_MEWTHREE] =
{
{
...
.cryId = CRY_MEWTHREE,
+ .natDexNum = NATIONAL_DEX_MEWTHREE,
@ -409,7 +409,7 @@ Now we can add the number and entry to our Mewthree:
```
![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f)
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
`height` and `weight` are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).
@ -443,7 +443,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera
NATIONAL_DEX_DUGTRIO,
...
};
const u16 gPokedexOrder_Height[] =
{
...
@ -560,7 +560,7 @@ Now that we have all the external data ready, we just need to add it to `gSpecie
{
...
[SPECIES_MEWTHREE] =
{
{
...
.pokemonScale = 256,
.pokemonOffset = 0,
@ -612,7 +612,7 @@ Let's explain each of these:
- Used to define what Y position of the back sprite. When working with the animation debug menu, we recommend aligning the back sprite to the white background, as it was designed to properyly align with the real battle layout.
- `backAnimId`:
- Like `frontAnimId` except for the back sprites and them being a single frame. The IDs listed [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h) are used to represent 3 different animations that happen based on the the Pokémon's nature.
- `PALETTES`
- `PALETTES`
- This macro was created to handle both regular and shiny palettes of a Pokémon. It just needs the species suffix to call the corresponding palette.
```c
#define PALETTES(pal) \
@ -650,7 +650,7 @@ We're almost there just a bit left!
{
...
[SPECIES_MEWTHREE] =
{
{
...
.abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
.bodyColor = BODY_COLOR_PURPLE,
@ -745,7 +745,7 @@ Again, we need to register the learnset in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
PALETTES(Mewthree),
ICON(Mewthree, 2),
@ -846,7 +846,7 @@ Once more, we need to register the learnset in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
FOOTPRINT(Mewthree)
.levelUpLearnset = sMewthreeLevelUpLearnset,
@ -869,7 +869,7 @@ Edit `gSpeciesInfo`:
{
...
[SPECIES_MEWTWO] =
{
{
...
FOOTPRINT(Mewtwo)
.isLegendary = TRUE,

View File

@ -181,7 +181,7 @@ Edit [src/data/pokemon/species_info.h](https://github.com/rh-hideout/pokeemerald
},
+ [SPECIES_MEWTHREE] =
+ {
+ {
+ .baseHP = 106,
+ .baseAttack = 150,
+ .baseDefense = 70,
@ -245,7 +245,7 @@ That's all the basic fields present in vanilla emerald, so now let's take a look
{
...
[SPECIES_MEWTHREE] =
{
{
...
.isLegendary = TRUE,
.allPerfectIVs = TRUE,
@ -320,7 +320,7 @@ Lastly, we add the cry to our species entry
{
...
[SPECIES_MEWTHREE] =
{
{
...
.isLegendary = TRUE,
.allPerfectIVs = TRUE,
@ -399,7 +399,7 @@ Now we can add the number and entry to our Mewthree:
{
...
[SPECIES_MEWTHREE] =
{
{
...
.cryId = CRY_MEWTHREE,
+ .natDexNum = NATIONAL_DEX_MEWTHREE,
@ -420,7 +420,7 @@ Now we can add the number and entry to our Mewthree:
```
![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f)
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
`height` and `weight` are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).
@ -454,7 +454,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera
NATIONAL_DEX_DUGTRIO,
...
};
const u16 gPokedexOrder_Height[] =
{
...
@ -571,7 +571,7 @@ Now that we have all the external data ready, we just need to add it to `gSpecie
{
...
[SPECIES_MEWTHREE] =
{
{
...
.pokemonScale = 256,
.pokemonOffset = 0,
@ -651,7 +651,7 @@ We're almost there just a bit left!
{
...
[SPECIES_MEWTHREE] =
{
{
...
.abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
.bodyColor = BODY_COLOR_PURPLE,
@ -746,7 +746,7 @@ Again, we need to register the learnset in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
PALETTES(Mewthree),
ICON(Mewthree, 2),
@ -847,7 +847,7 @@ Once more, we need to register the learnset in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
FOOTPRINT(Mewthree)
.levelUpLearnset = sMewthreeLevelUpLearnset,
@ -870,7 +870,7 @@ Edit `gSpeciesInfo`:
{
...
[SPECIES_MEWTWO] =
{
{
...
FOOTPRINT(Mewtwo)
.isLegendary = TRUE,

View File

@ -176,7 +176,7 @@ Edit [src/data/pokemon/species_info.h](https://github.com/rh-hideout/pokeemerald
},
+ [SPECIES_MEWTHREE] =
+ {
+ {
+ .baseHP = 106,
+ .baseAttack = 150,
+ .baseDefense = 70,
@ -240,7 +240,7 @@ That's all the basic fields present in vanilla emerald, so now let's take a look
{
...
[SPECIES_MEWTHREE] =
{
{
...
.isLegendary = TRUE,
.allPerfectIVs = TRUE,
@ -315,7 +315,7 @@ Lastly, we add the cry to our species entry
{
...
[SPECIES_MEWTHREE] =
{
{
...
.isLegendary = TRUE,
.allPerfectIVs = TRUE,
@ -394,7 +394,7 @@ Now we can add the number and entry to our Mewthree:
{
...
[SPECIES_MEWTHREE] =
{
{
...
.cryId = CRY_MEWTHREE,
+ .natDexNum = NATIONAL_DEX_MEWTHREE,
@ -415,7 +415,7 @@ Now we can add the number and entry to our Mewthree:
```
![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f)
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
`height` and `weight` are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).
@ -449,7 +449,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera
NATIONAL_DEX_DUGTRIO,
...
};
const u16 gPokedexOrder_Height[] =
{
...
@ -566,7 +566,7 @@ Now that we have all the external data ready, we just need to add it to `gSpecie
{
...
[SPECIES_MEWTHREE] =
{
{
...
.pokemonScale = 256,
.pokemonOffset = 0,
@ -646,7 +646,7 @@ We're almost there just a bit left!
{
...
[SPECIES_MEWTHREE] =
{
{
...
.abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
.bodyColor = BODY_COLOR_PURPLE,
@ -745,7 +745,7 @@ Again, we need to register the learnset in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
PALETTES(Mewthree),
ICON(Mewthree, 2),
@ -846,7 +846,7 @@ Once more, we need to register the learnset in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
FOOTPRINT(Mewthree)
.levelUpLearnset = sMewthreeLevelUpLearnset,
@ -869,7 +869,7 @@ Edit `gSpeciesInfo`:
{
...
[SPECIES_MEWTWO] =
{
{
...
FOOTPRINT(Mewtwo)
.isLegendary = TRUE,
@ -1097,7 +1097,7 @@ And finally, in `gSpeciesInfo`:
{
...
[SPECIES_MEWTHREE] =
{
{
...
FOOTPRINT(Mewthree)
+ OVERWORLD(

View File

@ -1,11 +1,11 @@
# How to use the Testing System
## Running Tests
To run all the tests use:
`make check -j`
To run specific tests, e.g. Spikes ones, use:
`make check TESTS='Spikes'`
To build a ROM (pokemerald-test.elf) that can be opened in mgba to view specific tests, e.g. Spikes ones, use:
To run all the tests use:
`make check -j`
To run specific tests, e.g. Spikes ones, use:
`make check TESTS='Spikes'`
To build a ROM (pokemerald-test.elf) that can be opened in mgba to view specific tests, e.g. Spikes ones, use:
`make pokeemerald-test.elf TESTS='Spikes'`
## How to Write Tests
@ -48,21 +48,21 @@ SINGLE_BATTLE_TEST("Stun Spore inflicts paralysis")
STATUS_ICON(opponent, paralysis: TRUE); // 4.
}
}
```
```
The `ASSUMPTIONS` block documents that Stun Spore has `EFFECT_PARALYZE`.
If Stun Spore did not have that effect it would cause the tests in the file to be skipped. We write our tests like this so that hackers can change the effects of moves without causing tests to fail.
The `ASSUMPTIONS` block documents that Stun Spore has `EFFECT_PARALYZE`.
If Stun Spore did not have that effect it would cause the tests in the file to be skipped. We write our tests like this so that hackers can change the effects of moves without causing tests to fail.
`SINGLE_BATTLE_TEST` defines the name of the test. Related tests should start with the same prefix, e.g. Stun Spore tests should start with "Stun Spore", this allows just the Stun Spore-related tests to be run with:
`make check TESTS='Stun Spore'`
`SINGLE_BATTLE_TEST` defines the name of the test. Related tests should start with the same prefix, e.g. Stun Spore tests should start with "Stun Spore", this allows just the Stun Spore-related tests to be run with:
`make check TESTS='Stun Spore'`
`GIVEN` initializes the parties, `PLAYER` and `OPPONENT` add a Pokémon to their respective parties. They can both accept a block which further customizes the Pokémon's stats, moves, item, ability, etc.
`GIVEN` initializes the parties, `PLAYER` and `OPPONENT` add a Pokémon to their respective parties. They can both accept a block which further customizes the Pokémon's stats, moves, item, ability, etc.
`WHEN` describes the turns, and `TURN` describes the choices made in a single turn. `MOVE` causes the player to use Stun Spore and adds the move to the Pokémon's moveset if an explicit Moves was not specified.
Pokémon that are not mentioned in a `TURN` use Celebrate.
The test runner rigs the RNG so that unless otherwise specified, moves always hit, never critical hit, always activate their secondary effects, and always roll the same damage modifier.
`WHEN` describes the turns, and `TURN` describes the choices made in a single turn. `MOVE` causes the player to use Stun Spore and adds the move to the Pokémon's moveset if an explicit Moves was not specified.
Pokémon that are not mentioned in a `TURN` use Celebrate.
The test runner rigs the RNG so that unless otherwise specified, moves always hit, never critical hit, always activate their secondary effects, and always roll the same damage modifier.
`SCENE` describes the player-visible output of the battle. In this case `ANIMATION` checks that the Stun Spore animation played, `MESSAGE` checks the paralysis message was shown, and `STATUS_ICON` checks that the opponent's HP bar shows a PRZ icon.
`SCENE` describes the player-visible output of the battle. In this case `ANIMATION` checks that the Stun Spore animation played, `MESSAGE` checks the paralysis message was shown, and `STATUS_ICON` checks that the opponent's HP bar shows a PRZ icon.
### Example 2
As a second example, to manually test that Stun Spore does not effect Grass-types you might:
@ -90,8 +90,8 @@ SINGLE_BATTLE_TEST("Stun Spore does not affect Grass-types")
}
}
```
The `ASSUME` commands are documenting the reasons why Stun Spore does not affect Oddish, namely that Stun Spore is a powder move, and Oddish is a Grass-type. These `ASSUME` statements function similarly to the ones in `ASSUMPTIONS` but apply only to the one test.
NOT inverts the meaning of a `SCENE` check, so applying it to `ANIMATION` requires that the Stun Spore animation does not play. `MESSAGE` checks that the message was shown.
The `ASSUME` commands are documenting the reasons why Stun Spore does not affect Oddish, namely that Stun Spore is a powder move, and Oddish is a Grass-type. These `ASSUME` statements function similarly to the ones in `ASSUMPTIONS` but apply only to the one test.
NOT inverts the meaning of a `SCENE` check, so applying it to `ANIMATION` requires that the Stun Spore animation does not play. `MESSAGE` checks that the message was shown.
The checks in `SCENE` are ordered, so together this says "The doesn't affect message is shown, and the Stun Spore animation does not play at any time before that". Normally you would only test one or the other, or even better, just `NOT STATUS_ICON(opponent, paralysis: TRUE);` to say that Oddish was not paralyzed without specifying the exact outputs which led to that.
### Example 3
@ -131,9 +131,9 @@ SINGLE_BATTLE_TEST("Meditate raises Attack", s16 damage)
}
```
`PARAMETRIZE` causes a test to run multiple times, once per `PARAMETRIZE` block (e.g. once with `raiseAttack = FALSE` and once with `raiseAttack = TRUE`).
The `HP_BAR` command's `captureDamage` causes the change in HP to be stored in a variable, and the variable chosen is `results[i].damage`.
`results[i]` contains all the variables defined at the end of `SINGLE_BATTLE_TEST`, `i` is the current `PARAMETRIZE` index.
`PARAMETRIZE` causes a test to run multiple times, once per `PARAMETRIZE` block (e.g. once with `raiseAttack = FALSE` and once with `raiseAttack = TRUE`).
The `HP_BAR` command's `captureDamage` causes the change in HP to be stored in a variable, and the variable chosen is `results[i].damage`.
`results[i]` contains all the variables defined at the end of `SINGLE_BATTLE_TEST`, `i` is the current `PARAMETRIZE` index.
`FINALLY` runs after the last parameter has finished, and uses `EXPECT_MUL_EQ` to check that the second battle deals 1.5× the damage of the first battle (with a small tolerance to account for rounding).
You might notice that all the tests check the outputs the player could see rather than the internal battle state. e.g. the Meditate test could have used `gBattleMons[B_POSITION_OPPONENT_LEFT].hp` instead of using `HP_BAR` to capture the damage. This is a deliberate choice, by checking what the player can observe the tests are more robust to refactoring, e.g. if `gBattleMons` got moved into `gBattleStruct` then any test that used it would need to be updated.
@ -144,8 +144,8 @@ The overworld is not available, so it is only possible to test commands which do
## REFERENCE
### `ASSUME`
`ASSUME(cond)`
Causes the test to be skipped if `cond` is false. Used to document any prerequisites of the test, e.g. to test Burn reducing the Attack of a Pokémon we can observe the damage of a physical attack with and without the burn. To document that this test assumes the attack is physical we can use:
`ASSUME(cond)`
Causes the test to be skipped if `cond` is false. Used to document any prerequisites of the test, e.g. to test Burn reducing the Attack of a Pokémon we can observe the damage of a physical attack with and without the burn. To document that this test assumes the attack is physical we can use:
`ASSUME(gMovesInfo[MOVE_WHATEVER].category == DAMAGE_CATEGORY_PHYSICAL);`
### `ASSUMPTIONS`
@ -164,16 +164,16 @@ ASSUMPTIONS
```
### `SINGLE_BATTLE_TEST`
`SINGLE_BATTLE_TEST(name, results...)` and `DOUBLE_BATTLE_TEST(name, results...)`
Define single- and double- battles. The names should start with the name of the mechanic being tested so that it is easier to run all the related tests. `results` contains variable declarations to be placed into the `results` array which is available in tests using `PARAMETRIZE` commands.
`SINGLE_BATTLE_TEST(name, results...)` and `DOUBLE_BATTLE_TEST(name, results...)`
Define single- and double- battles. The names should start with the name of the mechanic being tested so that it is easier to run all the related tests. `results` contains variable declarations to be placed into the `results` array which is available in tests using `PARAMETRIZE` commands.
The main differences for doubles are:
- Move targets sometimes need to be explicit.
- Instead of `player` and `opponent` there is `playerLeft`, `playerRight`, `opponentLeft`, and `opponentRight`.
### `AI_SINGLE_BATTLE_TEST`
`AI_SINGLE_BATTLE_TEST(name, results...)` and `AI_DOUBLE_BATTLE_TEST(name, results...)`
`AI_SINGLE_BATTLE_TEST(name, results...)` and `AI_DOUBLE_BATTLE_TEST(name, results...)`
Define battles where opponent mons are controlled by AI, the same that runs
when battling regular Trainers. The flags for AI should be specified by the `AI_FLAGS` command.
when battling regular Trainers. The flags for AI should be specified by the `AI_FLAGS` command.
The rules remain the same as with the `SINGLE` and `DOUBLE` battle tests with some differences:
- opponent's action is specified by the `EXPECT_MOVE` / `EXPECT_SEND_OUT` / `EXPECT_SWITCH` commands
- we don't control what opponent actually does, instead we make sure the opponent does what we expect it to do
@ -182,7 +182,7 @@ The rules remain the same as with the `SINGLE` and `DOUBLE` battle tests with so
### `KNOWN_FAILING`
`KNOWN_FAILING;`
Marks a test as not passing due to a bug. If there is an issue number associated with the bug it should be included in a comment. If the test passes the developer will be notified to remove `KNOWN_FAILING`.
Marks a test as not passing due to a bug. If there is an issue number associated with the bug it should be included in a comment. If the test passes the developer will be notified to remove `KNOWN_FAILING`.
For example:
```
SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
@ -192,7 +192,7 @@ SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
}
```
### `PARAMETRIZE`
`PARAMETERIZE { parameter; }`
`PARAMETERIZE { parameter; }`
Runs a test multiple times. `i` will be set to which parameter is running, and `results` will contain an entry for each parameter, e.g.:
```
SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage)
@ -246,7 +246,7 @@ Contains the initial state of the parties before the battle.
## `RNGSeed`
`RNGSeed(seed)`
Explicitly sets the RNG seed. Try to avoid using this because it is a very fragile tool.
Explicitly sets the RNG seed. Try to avoid using this because it is a very fragile tool.
Example:
```
GIVEN {
@ -256,10 +256,10 @@ GIVEN {
```
### `FLAG_SET`
`FLAG_SET(flagId)`
Sets the specified flag. Can currently only set one flag at a time.
Cleared between parameters and at the end of the test.
Example:
`FLAG_SET(flagId)`
Sets the specified flag. Can currently only set one flag at a time.
Cleared between parameters and at the end of the test.
Example:
```
GIVEN {
FLAG_SET(FLAG_SYS_EXAMPLE_FLAG);
@ -268,8 +268,8 @@ GIVEN {
```
### `PLAYER` and `OPPONENT`
`PLAYER(species)` and `OPPONENT(species`
Adds the species to the player's or opponent's party respectively.
`PLAYER(species)` and `OPPONENT(species`
Adds the species to the player's or opponent's party respectively.
The Pokémon can be further customized with the following functions:
- `Gender(MON_MALE | MON_FEMALE)`
- `Nature(nature)`
@ -280,14 +280,14 @@ The Pokémon can be further customized with the following functions:
- `Moves(moves...)`
- `Friendship(friendship)`
- `Status1(status1)`
For example to create a level 42 Wobbuffet that is poisoned:
`PLAYER(SPECIES_WOBBUFFET) { Level(42); Status1(STATUS1_POISON); }`
**Note if Speed is specified for any Pokémon then it must be specified for all Pokémon.**
For example to create a level 42 Wobbuffet that is poisoned:
`PLAYER(SPECIES_WOBBUFFET) { Level(42); Status1(STATUS1_POISON); }`
**Note if Speed is specified for any Pokémon then it must be specified for all Pokémon.**
**Note if Moves is specified then MOVE will not automatically add moves to the moveset.**
### `AI_FLAGS`
`AI_FLAGS(flags)`
Specifies which AI flags are run during the test. Has use only for AI tests.
`AI_FLAGS(flags)`
Specifies which AI flags are run during the test. Has use only for AI tests.
The most common combination is `AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT)` which is the general 'smart' AI.
### `WHEN`
@ -300,8 +300,8 @@ The most common combination is `AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_
Contains the choices that battlers make during the battle.
### `TURN`
`TURN { ... }`
Groups the choices made by the battlers on a single turn. If Speeds have not been explicitly specified then the order of the `MOVE` commands in the `TURN` will be used to infer the Speeds of the Pokémon, e.g.:
`TURN { ... }`
Groups the choices made by the battlers on a single turn. If Speeds have not been explicitly specified then the order of the `MOVE` commands in the `TURN` will be used to infer the Speeds of the Pokémon, e.g.:
```
// player's speed will be greater than opponent's speed.
TURN { MOVE(player, MOVE_SPLASH); MOVE(opponent, MOVE_SPLASH); }
@ -311,7 +311,7 @@ Groups the choices made by the battlers on a single turn. If Speeds have not bee
The inference process is naive, if your test contains anything that modifies the speed of a battler you should specify them explicitly.
### `MOVE`
`MOVE(battler, move | moveSlot:, [megaEvolve:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])`
`MOVE(battler, move | moveSlot:, [megaEvolve:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])`
Used when the battler chooses Fight. Either the move ID (e.g. `MOVE_TACKLE` or move slot must be specified.
- `megaEvolve: TRUE` causes the battler to Mega Evolve if able
- `hit: FALSE` causes the move to miss
@ -325,35 +325,35 @@ Used when the battler chooses Fight. Either the move ID (e.g. `MOVE_TACKLE` or m
If the battler does not have an explicit Moves specified the moveset will be populated based on the `MOVE`s it uses.
### `FORCED_MOVE`
`FORCED_MOVE(battler)`
`FORCED_MOVE(battler)`
Used when the battler chooses Fight and then their move is chosen for them, e.g. when affected by Encore.
```
FORCED_MOVE(player);
```
### `SWITCH`
`SWITCH(battler, partyIndex)`
`SWITCH(battler, partyIndex)`
Used when the battler chooses Switch.
```
SWITCH(player, 1);
```
### `SKIP_TURN`
`SKIP_TURN(battler)`
`SKIP_TURN(battler)`
Used when the battler cannot choose an action, e.g. when locked into Thrash.
```
SKIP_TURN(player);
```
### `SEND_OUT`
`SEND_OUT(battler, partyIndex)`
`SEND_OUT(battler, partyIndex)`
Used when the battler chooses to switch to another Pokémon but not via Switch, e.g. after fainting or due to a U-turn.
```
SEND_OUT(player, 1);
```
### `USE_ITEM`
`USE_ITEM(battler, itemId, [partyIndex:], [move:])`
`USE_ITEM(battler, itemId, [partyIndex:], [move:])`
Used when the battler chooses to use an item from the Bag. The item ID (e.g. ITEM_POTION) must be specified, and party index and move slot if applicable, e.g:
```
USE_ITEM(player, ITEM_X_ATTACK);
@ -378,15 +378,15 @@ Contains an abridged description of the UI during the `THEN`. The order of the d
```
### `ABILITY_POPUP`
`ABILITY_POPUP(battler, [ability])`
Causes the test to fail if the battler's ability pop-up is not shown.
`ABILITY_POPUP(battler, [ability])`
Causes the test to fail if the battler's ability pop-up is not shown.
If specified, ability is the ability shown in the pop-up.
```
ABILITY_POPUP(opponent, ABILITY_MOLD_BREAKER);
```
### `ANIMATION`
`ANIMATION(type, animId, [battler], [target:])`
`ANIMATION(type, animId, [battler], [target:])`
Causes the test to fail if the animation does not play. A common use of this command is to check if a move was successful, e.g.:
```
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
@ -394,7 +394,7 @@ Causes the test to fail if the animation does not play. A common use of this com
`target` can only be specified for `ANIM_TYPE_MOVE`.
### `EXPERIENCE_BAR`
`EXPERIENCE_BAR(battler, [exp: | captureGainedExp:])`
`EXPERIENCE_BAR(battler, [exp: | captureGainedExp:])`
If `exp:` is used, causes the test to fail if that amount of experience is not gained, e.g.:
```
EXPERIENCE_BAR(player, exp: 0);
@ -404,11 +404,11 @@ If `captureGainedExp:` is used, causes the test to fail if the Experience bar do
u32 exp;
EXPERIENCE_BAR(player, captureGainedExp: &exp);
```
If none of the above are used, causes the test to fail if the Exp does not change at all.
If none of the above are used, causes the test to fail if the Exp does not change at all.
**Please note that due to nature of tests, this command is only usable in `WILD_BATTLE_TEST` and will fail elsewhere.**
### `HP_BAR`
`HP_BAR(battler, [damage: | hp: | captureDamage: | captureHP:])`
`HP_BAR(battler, [damage: | hp: | captureDamage: | captureHP:])`
If `hp:` or `damage:` are used, causes the test to fail if that amount of damage is not dealt, e.g.:
```
HP_BAR(player, hp: 0);
@ -422,9 +422,9 @@ If `captureDamage:` or `captureHP:` are used, causes the test to fail if the HP
If none of the above are used, causes the test to fail if the HP does not change at all.
### MESSAGE
`MESSAGE(pattern)`
Causes the test to fail if the message in pattern is not displayed.
Spaces in pattern match newlines (\n, \l, and \p) in the message.
`MESSAGE(pattern)`
Causes the test to fail if the message in pattern is not displayed.
Spaces in pattern match newlines (\n, \l, and \p) in the message.
Often used to check that a battler took its turn but it failed, e.g.:
```
MESSAGE("Wobbuffet used Dream Eater!");
@ -432,7 +432,7 @@ Often used to check that a battler took its turn but it failed, e.g.:
```
### `STATUS_ICON`
`STATUS_ICON(battler, status1 | none: | sleep: | poison: | burn: | freeze: | paralysis:, badPoison:)`
`STATUS_ICON(battler, status1 | none: | sleep: | poison: | burn: | freeze: | paralysis:, badPoison:)`
Causes the test to fail if the battler's status is not changed to the specified status.
```
STATUS_ICON(player, badPoison: TRUE);
@ -447,7 +447,7 @@ If the expected status icon is parametrized the corresponding `STATUS1` constant
```
### `NOT`
`NOT sceneCommand`
`NOT sceneCommand`
Causes the test to fail if the `SCENE` command succeeds before the following command succeeds.
```
// Our Wobbuffet does not Celebrate before the foe's.
@ -519,27 +519,27 @@ Contains code to run after the battle has finished. If the test is using `PARAME
Contains checks to run after all `PARAMETERIZE` commands have run. Prefer to write your checks in `THEN` where possible, because a failure in `THEN` will be tagged with which parameter it corresponds to.
### `EXPECT`
`EXPECT(cond)`
`EXPECT(cond)`
Causes the test to fail if `cond` is false.
### `EXPECT_XX`
`EXPECT_EQ(a, b)`
`a == b`
`EXPECT_EQ(a, b)`
`a == b`
`EXPECT_NE(a, b)`
`a != b`
`EXPECT_NE(a, b)`
`a != b`
`EXPECT_LT(a, b)`
`a < b`
`EXPECT_LT(a, b)`
`a < b`
`EXPECT_LE(a, b)`
`a <= b`
`EXPECT_LE(a, b)`
`a <= b`
`EXPECT_GT(a, b)`
`a > b`
`EXPECT_GT(a, b)`
`a > b`
`EXPECT_GE(a, b)`
`a >= b`
`EXPECT_GE(a, b)`
`a >= b`
Causes the test to fail if a and b compare incorrectly, e.g.
```
@ -547,7 +547,7 @@ Causes the test to fail if a and b compare incorrectly, e.g.
```
### `EXPECT_MUL_EQ`
`EXPECT_MUL_EQ(a, m, b)`
`EXPECT_MUL_EQ(a, m, b)`
Causes the test to fail if `a*m != b` (within a threshold), e.g.
```
// Expect results[0].damage * 1.5 == results[1].damage.
@ -557,7 +557,7 @@ Causes the test to fail if a and b compare incorrectly, e.g.
## Overworld Command Reference
### `OVERWORLD_SCRIPT`
`OVERWORLD_SCRIPT(instructions...)`
`OVERWORLD_SCRIPT(instructions...)`
Returns a pointer to a compiled overworld script. Cannot be used to initialize global `const` data, although the pointer **IS** to `const` data.
Note that each script command must be followed by a ;, e.g.:
```
@ -568,7 +568,7 @@ const u8 *myScript = OVERWORLD_SCRIPT(
```
### `RUN_OVERWORLD_SCRIPT`
`RUN_OVERWORLD_SCRIPT(instructions...)`
`RUN_OVERWORLD_SCRIPT(instructions...)`
Runs an overworld script in the immediate script context, which means that commands like `waitstate` are not supported.
```
RUN_OVERWORLD_SCRIPT(

View File

@ -12,13 +12,13 @@
* [Usage](#usage)
## Quick Summary
(Page contains out of date information, [new instructions for Sprites here](https://github.com/rh-hideout/pokeemerald-expansion/pull/3597).)
(Page contains out of date information, [new instructions for Sprites here](https://github.com/rh-hideout/pokeemerald-expansion/pull/3597).)
If you've done this before and just need a quick lookup, here's what files you need:
1. GFX into [graphics/trainers/front_pics](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/graphics/trainers/front_pics)
2. Palette into [graphics/trainers/palettes](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/graphics/trainers/palettes)
3. Register sprites to [include/graphics.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/graphics.h)
4. Point game to where graphic files are found: [src/data/graphics/trainers](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/graphics/trainers.h)
5. Add animation to: [src/data/trainer_graphics/front_pic_anims.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/trainer_graphics/front_pic_anims.h)
5. Add animation to: [src/data/trainer_graphics/front_pic_anims.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/trainer_graphics/front_pic_anims.h)
6. Add the trainer to all three structs in: [src/data/trainer_graphics/front_pic_table.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/trainer_graphics/front_pic_table.h)
7. Add trainer to [include/constants/trainers.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/trainers.h)

View File

@ -676,6 +676,7 @@ struct BattleStruct
u16 chosenItem[MAX_BATTLERS_COUNT];
u16 choicedMove[MAX_BATTLERS_COUNT];
u16 changedItems[MAX_BATTLERS_COUNT];
u8 canPickupItem;
u8 switchInBattlerCounter;
u8 arenaTurnCounter;
u8 turnSideTracker;
@ -763,7 +764,6 @@ struct BattleStruct
bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects.
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 allowedToChangeFormInWeather[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side, used by Ice Face.
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
u8 storedHealingWish:4; // Each battler as a bit.
u8 storedLunarDance:4; // Each battler as a bit.

View File

@ -295,6 +295,7 @@ extern const u8 BattleScript_WeakArmorActivates[];
extern const u8 BattleScript_FellStingerRaisesStat[];
extern const u8 BattleScript_SnowWarningActivatesHail[];
extern const u8 BattleScript_SnowWarningActivatesSnow[];
extern const u8 BattleScript_PickupActivates[];
extern const u8 BattleScript_HarvestActivates[];
extern const u8 BattleScript_ImposterActivates[];
extern const u8 BattleScript_SelectingNotAllowedMoveAssaultVest[];

View File

@ -231,6 +231,8 @@ bool32 TryRoomService(u32 battler);
void BufferStatChange(u32 battler, u8 statId, u8 stringId);
bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget);
u16 GetUsedHeldItem(u32 battler);
bool32 PickupHasValidTarget(u32 battler);
bool32 CantPickupItem(u32 battler);
bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags);
u32 GetBattlerMoveTargetType(u32 battler, u32 move);
bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move);

View File

@ -271,7 +271,7 @@
#define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF)
#define OBJ_EVENT_GFX_MON_BASE 0x200 // 512
#define OBJ_EVENT_GFX_SPECIES_BITS 11
#define OBJ_EVENT_GFX_SPECIES_BITS 12 // This will need to be updated when NUM_SPECIES is > ~3.5k
#define OBJ_EVENT_GFX_SPECIES_MASK ((1 << OBJ_EVENT_GFX_SPECIES_BITS) - 1)
// Used to call a specific species' follower graphics. Useful for static encounters.

View File

@ -173,12 +173,14 @@ enum RandomTag
RNG_G_MAX_BEFUDDLE,
RNG_G_MAX_REPLENISH,
RNG_G_MAX_SNOOZE,
RNG_HARVEST,
RNG_HITS,
RNG_HOLD_EFFECT_FLINCH,
RNG_INFATUATION,
RNG_LOADED_DICE,
RNG_METRONOME,
RNG_PARALYSIS,
RNG_PICKUP,
RNG_POISON_POINT,
RNG_POISON_TOUCH,
RNG_RAMPAGE_TURNS,

View File

@ -328,7 +328,7 @@
*
* MOVE(battler, move | moveSlot:, [gimmick:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])
* Used when the battler chooses Fight. Either the move ID or move slot
* must be specified. gimmick: GIMMICK_MEGA causes the battler to Mega
* must be specified. gimmick: GIMMICK_MEGA causes the battler to Mega
* Evolve if able, hit: FALSE causes the move to miss, criticalHit: TRUE
* causes the move to land a critical hit, target: is used in double
* battles to choose the target (when necessary), and allowed: FALSE is

View File

@ -81,5 +81,5 @@ for file in pories_to_check:
raw = re.sub("script %s[ \n]*\{[ \n]*finditem\((.*)\)[ \n]*\}[ \n]*" % unused, "", raw)
with open(file, "w") as f2:
f2.write(raw)
print("Done!")

View File

@ -37,7 +37,7 @@ for line in lines:
comment_split = line.split('//')
if move and IsCommaMissing(comment_split[0]):
line = comment_split[0].removesuffix('\n') + ',' + line[len(comment_split[0]):-1] + '\n'
moves_info_lines.append(line)

View File

@ -731,7 +731,7 @@ $(FLDEFFGFXDIR)/secret_power_tree.4bpp: %.4bpp: %.png
$(FLDEFFGFXDIR)/record_mix_lights.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 4 -mheight 1
$(POKEMONGFXDIR)/question_mark/overworld.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 4 -mheight 4

View File

@ -1566,9 +1566,9 @@ static u32 GetSwitchinStatusDamage(u32 battler)
if ((status & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns
AI_DATA->switchinCandidate.battleMon.status1 += STATUS1_TOXIC_TURN(1);
statusDamage = maxHP / 16;
statusDamage *= AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_COUNTER >> 8;
if (statusDamage == 0)
statusDamage = 1;
statusDamage *= AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_COUNTER >> 8;
}
}

View File

@ -482,7 +482,7 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
return TRUE;
break;
case EFFECT_POLTERGEIST:
if (AI_DATA->items[battlerDef] == ITEM_NONE)
if (AI_DATA->items[battlerDef] == ITEM_NONE || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || battlerDefAbility == ABILITY_KLUTZ)
return TRUE;
break;
case EFFECT_FIRST_TURN_ONLY:
@ -2513,7 +2513,7 @@ u32 GetBattlerSecondaryDamage(u32 battlerId)
bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability)
{
if ((BattlerAffectedBySandstorm(battler, ability) || BattlerAffectedByHail(battler, ability))
&& gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 16)
&& gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16))
return TRUE;
return FALSE;
@ -2522,7 +2522,7 @@ bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability)
bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, u32 ability)
{
if (GetBattlerSecondaryDamage(battler) != 0
&& gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 16)
&& gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16))
return TRUE;
return FALSE;
}

View File

@ -42,35 +42,35 @@ const struct SpriteTemplate gOutrageFlameSpriteTemplate =
.callback = AnimOutrageFlame,
};
static const union AnimCmd sAnim_DreepyMissileOpponent_0[] =
static const union AnimCmd sAnim_DreepyMissileOpponent_0[] =
{
ANIMCMD_FRAME(0, 0, .hFlip = TRUE),
ANIMCMD_END,
};
const union AnimCmd *const gAnims_DreepyMissileOpponent[] =
const union AnimCmd *const gAnims_DreepyMissileOpponent[] =
{
sAnim_DreepyMissileOpponent_0,
};
static const union AnimCmd sAnim_DreepyMissilePlayer_0[] =
static const union AnimCmd sAnim_DreepyMissilePlayer_0[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END,
};
const union AnimCmd *const gAnims_DreepyMissilePlayer[] =
const union AnimCmd *const gAnims_DreepyMissilePlayer[] =
{
sAnim_DreepyMissilePlayer_0,
};
static const union AnimCmd sAnim_DreepyMissileNotDrag_0[] =
static const union AnimCmd sAnim_DreepyMissileNotDrag_0[] =
{
ANIMCMD_FRAME(0, 0, .hFlip = TRUE, .vFlip = TRUE),
ANIMCMD_END,
};
const union AnimCmd *const gAnims_DreepyMissileOpponentNotDrag[] =
const union AnimCmd *const gAnims_DreepyMissileOpponentNotDrag[] =
{
sAnim_DreepyMissileNotDrag_0,
};

View File

@ -33,7 +33,7 @@ static void AnimSkyAttackBird_Step(struct Sprite *);
static void AnimTask_AnimateGustTornadoPalette_Step(u8);
static void AnimTask_LoadWindstormBackground_Step(u8 taskId);
const struct SpriteTemplate gEllipticalGustCenteredSpriteTemplate =
const struct SpriteTemplate gEllipticalGustCenteredSpriteTemplate =
{
.tileTag = ANIM_TAG_GUST,
.paletteTag = ANIM_TAG_GUST,

View File

@ -414,7 +414,7 @@ u32 UnpackSelectedBattlePalettes(s16 selector)
bool8 anim1 = (selector >> 5) & 1;
bool8 anim2 = (selector >> 6) & 1;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gAnimMoveIndex);
switch (moveTarget)
{
case MOVE_TARGET_BOTH:
@ -431,7 +431,7 @@ u32 UnpackSelectedBattlePalettes(s16 selector)
}
break;
}
return GetBattlePalettesMask(battleBackground, attacker, target, attackerPartner, targetPartner, anim1, anim2);
}

View File

@ -2136,7 +2136,7 @@ static void CalcDomeMonStats(const struct TrainerMon *fmon, int level, u8 ivs, i
{
int evs[NUM_STATS];
int i;
for (i = 0; i < NUM_STATS; i++)
{
if (fmon->ev != NULL)
@ -2144,7 +2144,7 @@ static void CalcDomeMonStats(const struct TrainerMon *fmon, int level, u8 ivs, i
else
evs[i] = 0;
}
if (fmon->species == SPECIES_SHEDINJA)
{
stats[STAT_HP] = 1;
@ -2199,7 +2199,7 @@ static void CreateDomeOpponentMon(u8 monPartyId, u16 tournamentTrainerId, u8 tou
u8 fixedIv = GetDomeTrainerMonIvs(tournamentTrainerId); // BUG: Using the wrong ID. As a result, all Pokémon have ivs of 3.
#endif
u8 level = SetFacilityPtrsGetLevel();
CreateFacilityMon(&gFacilityTrainerMons[DOME_MONS[tournamentTrainerId][tournamentMonId]],
level, fixedIv, otId, 0, &gEnemyParty[monPartyId]);
}
@ -4446,7 +4446,7 @@ static void DisplayTrainerInfoOnCard(u8 flags, u8 trainerTourneyId)
else
allocatedArray[j] = 0;
}
allocatedArray[NUM_STATS] += allocatedArray[STAT_HP];
for (j = 0; j < NUM_NATURE_STATS; j++)
{

View File

@ -406,7 +406,7 @@ static void SetPlayerAndOpponentParties(void)
u8 monLevel;
u16 monId;
u8 ivs;
if (gSaveBlock2Ptr->frontier.lvlMode == FRONTIER_LVL_TENT)
{
gFacilityTrainerMons = gSlateportBattleTentMons;
@ -428,7 +428,7 @@ static void SetPlayerAndOpponentParties(void)
{
monId = gSaveBlock2Ptr->frontier.rentalMons[i].monId;
ivs = gSaveBlock2Ptr->frontier.rentalMons[i].ivs;
CreateFacilityMon(&gFacilityTrainerMons[monId], monLevel, ivs, OT_ID_PLAYER_ID, FLAG_FRONTIER_MON_FACTORY, &gPlayerParty[i]);
SetMonData(&gPlayerParty[i], MON_DATA_PERSONALITY,
&gSaveBlock2Ptr->frontier.rentalMons[i].personality);

View File

@ -1761,7 +1761,7 @@ static void CreateFrontierFactorySelectableMons(u8 firstMonId)
ivs = GetFactoryMonFixedIV(challengeNum + 1, FALSE);
else
ivs = GetFactoryMonFixedIV(challengeNum, FALSE);
CreateFacilityMon(&gFacilityTrainerMons[monId],
level, ivs, otId, FLAG_FRONTIER_MON_FACTORY,
&sFactorySelectScreen->mons[i + firstMonId].monData);

View File

@ -3114,8 +3114,6 @@ static void BattleStartClearSetData(void)
gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
gBattleStruct->itemLost[B_SIDE_OPPONENT][i].originalItem = GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM);
gPartyCriticalHits[i] = 0;
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_PLAYER] = FALSE;
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_OPPONENT] = FALSE;
}
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
@ -3221,6 +3219,7 @@ void SwitchInClearSetData(u32 battler)
gBattleStruct->lastMoveFailed &= ~(1u << battler);
gBattleStruct->palaceFlags &= ~(1u << battler);
gBattleStruct->boosterEnergyActivates &= ~(1u << battler);
gBattleStruct->canPickupItem &= ~(1u << battler);
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
@ -4039,6 +4038,11 @@ void BattleTurnPassed(void)
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
gBattleMainFunc = HandleTurnActionSelectionState;
if (gSideTimers[B_SIDE_PLAYER].retaliateTimer > 0)
gSideTimers[B_SIDE_PLAYER].retaliateTimer--;
if (gSideTimers[B_SIDE_OPPONENT].retaliateTimer > 0)
gSideTimers[B_SIDE_OPPONENT].retaliateTimer--;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
BattleScriptExecute(BattleScript_PalacePrintFlavorText);
else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->arenaTurnCounter == 0)
@ -5114,6 +5118,7 @@ static void TurnValuesCleanUp(bool8 var0)
if (gDisableStructs[i].rechargeTimer == 0)
gBattleMons[i].status2 &= ~STATUS2_RECHARGE;
}
gBattleStruct->canPickupItem &= ~(1u << i);
}
if (gDisableStructs[i].substituteHP == 0)

View File

@ -3491,7 +3491,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
&& GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) != ABILITY_MAGIC_GUARD)
{
gBattleScripting.savedBattler = BATTLE_PARTNER(gBattlerTarget);
gBattleMoveDamage = gBattleMons[BATTLE_PARTNER(gBattlerTarget)].hp / 16;
gBattleMoveDamage = gBattleMons[BATTLE_PARTNER(gBattlerTarget)].maxHP / 16;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattlescriptCurrInstr = BattleScript_MoveEffectFlameBurst;
@ -7281,19 +7281,32 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
gDisableStructs[battler].truantSwitchInHack = 0;
for (i = 0; i < gBattlersCount; i++)
{
if (i != battler
&& GetBattlerAbility(i) == ABILITY_TRACE
&& AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0))
return TRUE;
}
if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE))
return TRUE;
else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0))
return TRUE;
for (i = 0; i < gBattlersCount; i++)
{
if (i == battler)
continue;
switch (GetBattlerAbility(i))
{
case ABILITY_TRACE:
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0))
return TRUE;
break;
case ABILITY_FORECAST:
case ABILITY_FLOWER_GIFT:
case ABILITY_ICE_FACE:
case ABILITY_PROTOSYNTHESIS:
if (AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, i, 0, 0, 0))
return TRUE;
break;
}
}
gDisableStructs[battler].stickyWebDone = FALSE;
gDisableStructs[battler].spikesDone = FALSE;
gDisableStructs[battler].toxicSpikesDone = FALSE;
@ -8060,6 +8073,7 @@ static void Cmd_removeitem(void)
gBattleStruct->usedHeldItems[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)] = itemId; // Remember if switched out
gBattleMons[battler].item = ITEM_NONE;
gBattleStruct->canPickupItem |= (1u << battler);
CheckSetUnburden(battler);
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battler].item), &gBattleMons[battler].item);
@ -14845,7 +14859,10 @@ static void Cmd_tryrecycleitem(void)
u16 *usedHeldItem;
usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerAttacker]][GetBattlerSide(gBattlerAttacker)];
if (gCurrentMove == MOVE_NONE && GetBattlerAbility(gBattlerAttacker) == ABILITY_PICKUP)
usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerTarget]][GetBattlerSide(gBattlerTarget)];
else
usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerAttacker]][GetBattlerSide(gBattlerAttacker)];
if (*usedHeldItem != ITEM_NONE && gBattleMons[gBattlerAttacker].item == ITEM_NONE)
{
gLastUsedItem = *usedHeldItem;
@ -15657,17 +15674,26 @@ static void Cmd_swapstatstages(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static u16 *GetBattlerStat(struct BattlePokemon *battler, u32 stat)
{
switch (stat)
{
case STAT_ATK: return &battler->attack;
case STAT_DEF: return &battler->defense;
case STAT_SPATK: return &battler->spAttack;
case STAT_SPDEF: return &battler->spDefense;
default: return NULL;
}
}
static void Cmd_averagestats(void)
{
CMD_ARGS(u8 stat);
u8 stat = cmd->stat;
u16 atkStat = *(u16 *)((&gBattleMons[gBattlerAttacker].attack) + (stat - 1));
u16 defStat = *(u16 *)((&gBattleMons[gBattlerTarget].attack) + (stat - 1));
u16 average = (atkStat + defStat) / 2;
*(u16 *)((&gBattleMons[gBattlerAttacker].attack) + (stat - 1)) = average;
*(u16 *)((&gBattleMons[gBattlerTarget].attack) + (stat - 1)) = average;
u16 *stat1 = GetBattlerStat(&gBattleMons[gBattlerAttacker], cmd->stat);
u16 *stat2 = GetBattlerStat(&gBattleMons[gBattlerTarget], cmd->stat);
u16 avg = (*stat1 + *stat2) / 2;
*stat1 = *stat2 = avg;
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -1940,7 +1940,7 @@ void IncrementRematchStepCounter(void)
#if FREE_MATCH_CALL == FALSE
if (!HasAtLeastFiveBadges())
return;
if (IsVsSeekerEnabled())
return;

View File

@ -1669,8 +1669,6 @@ enum
ENDTURN_PSYCHIC_TERRAIN,
ENDTURN_ION_DELUGE,
ENDTURN_FAIRY_LOCK,
ENDTURN_RETALIATE,
ENDTURN_WEATHER_FORM,
ENDTURN_STATUS_HEAL,
ENDTURN_RAINBOW,
ENDTURN_SEA_OF_FIRE,
@ -2163,25 +2161,6 @@ u8 DoFieldEndTurnEffects(void)
}
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_RETALIATE:
if (gSideTimers[B_SIDE_PLAYER].retaliateTimer > 0)
gSideTimers[B_SIDE_PLAYER].retaliateTimer--;
if (gSideTimers[B_SIDE_OPPONENT].retaliateTimer > 0)
gSideTimers[B_SIDE_OPPONENT].retaliateTimer--;
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_WEATHER_FORM:
for (i = 0; i < gBattlersCount; i++)
{
if (AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, i, 0, 0, 0))
{
effect++;
break;
}
}
if (effect == 0)
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_STATUS_HEAL:
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
@ -2399,6 +2378,8 @@ u8 DoBattlerEndTurnEffects(void)
{
gBattleScripting.battler = battler;
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
BattleScriptExecute(BattleScript_DamagingWeather);
effect++;
}
@ -2409,7 +2390,7 @@ u8 DoBattlerEndTurnEffects(void)
&& !(gStatuses3[battler] & STATUS3_HEAL_BLOCK))
{
gBattleScripting.battler = battler;
gBattleMoveDamage = -1 * (GetNonDynamaxMaxHP(battler) / 16);
gBattleMoveDamage = -1 * max(1, GetNonDynamaxMaxHP(battler) / 16);
BattleScriptExecute(BattleScript_IceBodyHeal);
effect++;
}
@ -2423,6 +2404,8 @@ u8 DoBattlerEndTurnEffects(void)
{
gBattleScripting.battler = battler;
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
BattleScriptExecute(BattleScript_DamagingWeather);
effect++;
}
@ -3657,13 +3640,14 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
else
{
gMultiHitCounter = gMovesInfo[gCurrentMove].strikeCount;
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0)
if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_DARTS
&& CanTargetPartner(gBattlerAttacker, gBattlerTarget)
&& TargetFullyImmuneToCurrMove(gBattlerAttacker, gBattlerTarget))
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget);
}
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0)
}
else if (B_BEAT_UP >= GEN_5 && gMovesInfo[gCurrentMove].effect == EFFECT_BEAT_UP)
{
@ -3893,21 +3877,6 @@ static const u16 sWeatherFlagsInfo[][3] =
[ENUM_WEATHER_FOG] = {B_WEATHER_FOG_TEMPORARY, B_WEATHER_FOG_PERMANENT, HOLD_EFFECT_NONE},
};
static void ShouldChangeFormInWeather(u32 battler)
{
int i;
int side = GetBattlerSide(battler);
struct Pokemon *party = GetSideParty(side);
for (i = 0; i < PARTY_SIZE; i++)
{
if (GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_EISCUE_NOICE_FACE)
gBattleStruct->allowedToChangeFormInWeather[i][side] = TRUE;
else
gBattleStruct->allowedToChangeFormInWeather[i][side] = FALSE;
}
}
bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility)
{
u16 battlerAbility = GetBattlerAbility(battler);
@ -3921,7 +3890,6 @@ bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility)
else if (B_ABILITY_WEATHER < GEN_6 && viaAbility && !(gBattleWeather & sWeatherFlagsInfo[weatherEnumId][1]))
{
gBattleWeather = (sWeatherFlagsInfo[weatherEnumId][0] | sWeatherFlagsInfo[weatherEnumId][1]);
ShouldChangeFormInWeather(battler);
return TRUE;
}
else if (!(gBattleWeather & (sWeatherFlagsInfo[weatherEnumId][0] | sWeatherFlagsInfo[weatherEnumId][1])))
@ -3931,7 +3899,6 @@ bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility)
gWishFutureKnock.weatherDuration = 8;
else
gWishFutureKnock.weatherDuration = 5;
ShouldChangeFormInWeather(battler);
return TRUE;
}
return FALSE;
@ -4798,6 +4765,19 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect++;
}
break;
case ABILITY_WIND_RIDER:
if (!gSpecialStatuses[battler].switchInAbilityDone
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
{
gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
effect++;
}
break;
case ABILITY_DESOLATE_LAND:
if (TryChangeBattleWeather(battler, ENUM_WEATHER_SUN_PRIMAL, TRUE))
{
@ -4960,6 +4940,17 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect++;
}
break;
case ABILITY_ICE_FACE:
if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL | B_WEATHER_SNOW)
&& gBattleMons[battler].species == SPECIES_EISCUE_NOICE_FACE
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
{
// TODO: Convert this to a proper FORM_CHANGE type.
gBattleMons[battler].species = SPECIES_EISCUE_ICE_FACE;
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3);
effect++;
}
break;
}
break;
case ABILITYEFFECT_ENDTURN:
@ -4968,8 +4959,19 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
gBattlerAttacker = battler;
switch (gLastUsedAbility)
{
case ABILITY_PICKUP:
if (gBattleMons[battler].item == ITEM_NONE
&& gBattleStruct->changedItems[battler] == ITEM_NONE // Will not inherit an item
&& PickupHasValidTarget(battler))
{
gBattlerTarget = RandomUniformExcept(RNG_PICKUP, 0, gBattlersCount - 1, CantPickupItem);
gLastUsedItem = GetUsedHeldItem(gBattlerTarget);
BattleScriptPushCursorAndCallback(BattleScript_PickupActivates);
effect++;
}
break;
case ABILITY_HARVEST:
if ((IsBattlerWeatherAffected(battler, B_WEATHER_SUN) || Random() % 2 == 0)
if ((IsBattlerWeatherAffected(battler, B_WEATHER_SUN) || RandomPercentage(RNG_HARVEST, 50))
&& gBattleMons[battler].item == ITEM_NONE
&& gBattleStruct->changedItems[battler] == ITEM_NONE // Will not inherit an item
&& ItemId_GetPocket(GetUsedHeldItem(battler)) == POCKET_BERRIES)
@ -6266,17 +6268,17 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITYEFFECT_ON_WEATHER: // For ability effects that activate when the battle weather changes.
battler = gBattlerAbility = gBattleScripting.battler;
gLastUsedAbility = GetBattlerAbility(battler);
switch (gLastUsedAbility)
{
case ABILITY_FORECAST:
case ABILITY_FLOWER_GIFT:
if ((IsBattlerWeatherAffected(battler, gBattleWeather)
|| gBattleWeather == B_WEATHER_NONE
|| !WEATHER_HAS_EFFECT) // Air Lock active
&& TryBattleFormChange(battler, FORM_CHANGE_BATTLE_WEATHER))
|| gBattleWeather == B_WEATHER_NONE
|| !WEATHER_HAS_EFFECT) // Air Lock active
&& TryBattleFormChange(battler, FORM_CHANGE_BATTLE_WEATHER))
{
gBattleScripting.battler = battler;
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3);
effect++;
}
@ -6284,11 +6286,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_ICE_FACE:
if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL | B_WEATHER_SNOW)
&& gBattleMons[battler].species == SPECIES_EISCUE_NOICE_FACE
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)])
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
{
// TODO: Convert this to a proper FORM_CHANGE type.
gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)] = FALSE;
gBattleScripting.battler = battler;
gBattleMons[battler].species = SPECIES_EISCUE_ICE_FACE;
BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3);
effect++;
@ -6299,7 +6300,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
{
gDisableStructs[battler].weatherAbilityDone = TRUE;
PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler));
gBattlerAbility = gBattleScripting.battler = battler;
gBattleScripting.battler = battler;
BattleScriptPushCursorAndCallback(BattleScript_ProtosynthesisActivates);
effect++;
}
@ -11411,6 +11412,25 @@ u16 GetUsedHeldItem(u32 battler)
return gBattleStruct->usedHeldItems[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)];
}
bool32 CantPickupItem(u32 battler)
{
// Used by RandomUniformExcept() for RNG_PICKUP
if (battler == gBattlerAttacker && gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK))
return TRUE;
return !(IsBattlerAlive(battler) && GetUsedHeldItem(battler) && gBattleStruct->canPickupItem & (1u << battler));
}
bool32 PickupHasValidTarget(u32 battler)
{
u32 i;
for (i = 0; i < gBattlersCount; i++)
{
if (!CantPickupItem(i))
return TRUE;
}
return FALSE;
}
bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags)
{
if (gBattleWeather & weatherFlags && WEATHER_HAS_EFFECT)

View File

@ -84,7 +84,7 @@ static void FormChangeTimeUpdate()
{
struct Pokemon *mon = &gPlayerParty[i];
u16 targetSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_TIME_OF_DAY, 0);
if (targetSpecies != SPECIES_NONE)
{
SetMonData(mon, MON_DATA_SPECIES, &targetSpecies);

View File

@ -5,7 +5,7 @@
const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] =
{
[GIMMICK_NONE] = {0},
[GIMMICK_MEGA] =
[GIMMICK_MEGA] =
{
.triggerSheet = &sSpriteSheet_MegaTrigger,
.triggerPal = &sSpritePalette_MegaTrigger,
@ -15,7 +15,7 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] =
.CanActivate = CanMegaEvolve,
.ActivateGimmick = ActivateMegaEvolution,
},
[GIMMICK_Z_MOVE] =
[GIMMICK_Z_MOVE] =
{
.triggerSheet = &sSpriteSheet_ZMoveTrigger,
.triggerPal = &sSpritePalette_ZMoveTrigger,
@ -23,7 +23,7 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] =
.CanActivate = CanUseZMove,
.ActivateGimmick = ActivateZMove,
},
[GIMMICK_ULTRA_BURST] =
[GIMMICK_ULTRA_BURST] =
{
.triggerSheet = &sSpriteSheet_BurstTrigger,
.triggerPal = &sSpritePalette_BurstTrigger,
@ -31,7 +31,7 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] =
.CanActivate = CanUltraBurst,
.ActivateGimmick = ActivateUltraBurst,
},
[GIMMICK_DYNAMAX] =
[GIMMICK_DYNAMAX] =
{
.triggerSheet = &sSpriteSheet_DynamaxTrigger,
.triggerPal = &sSpritePalette_DynamaxTrigger,

View File

@ -17470,7 +17470,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.name = COMPOUND_STRING("Decorate"),
.description = COMPOUND_STRING(
"The user sharply raises\n"
"the target's Atk and Sp.Atk"),
"the target's Atk and Sp.Atk."),
.effect = EFFECT_DECORATE,
.power = 0,
.type = TYPE_FAIRY,
@ -19739,7 +19739,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.name = HANDLE_EXPANDED_MOVE_NAME("ColisinCours", "Collision Course"),
.description = COMPOUND_STRING(
"Prehistoric explosion that's\n"
"stronger if supereffective."),
"stronger if super effective."),
.effect = EFFECT_COLLISION_COURSE,
.power = 100,
.type = TYPE_FIGHTING,
@ -19758,7 +19758,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.name = HANDLE_EXPANDED_MOVE_NAME("ElectroDrift", "Electro Drift"),
.description = COMPOUND_STRING(
"Futuristic electricity. It's\n"
"stronger if supereffective."),
"stronger if super effective."),
.effect = EFFECT_COLLISION_COURSE,
.power = 100,
.type = TYPE_ELECTRIC,

View File

@ -239,7 +239,7 @@ extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_HoOh;
// Begin pokemon event objects
extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_PokeBall;
extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Follower;
extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Bard;
extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Hipster;
extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Trader;

View File

@ -1493,7 +1493,7 @@ static s16 ReallocSpriteTiles(struct Sprite *sprite, u32 byteSize)
{
i = -1;
}
sprite->invisible = wasVisible;
return i;
}
@ -1510,7 +1510,7 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid,
bool32 oldInvisible;
if (tag == TAG_NONE)
tag = COMP_OW_TILE_TAG_BASE + uuid;
if (sprite)
{
oldInvisible = sprite->invisible;
@ -1547,7 +1547,7 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid,
{
FieldEffectFreeTilesIfUnused(oldTiles);
}
if (sprite)
{
sprite->sheetTileStart = tileStart;
@ -1566,7 +1566,7 @@ u16 LoadSheetGraphicsInfo(const struct ObjectEventGraphicsInfo *info, u16 uuid,
{
sprite->oam.tileNum = sprite->sheetTileStart;
sprite->usingSheet = FALSE;
}
else if (sprite && !sprite->sheetTileStart && sprite->oam.size != info->oam->size)
{
@ -1925,7 +1925,7 @@ static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool32 shiny)
spritePalette.data = gSpeciesInfo[species].overworldShinyPalette;
else
spritePalette.data = gSpeciesInfo[species].overworldPalette;
// Check if pal data must be decompressed
if (IsLZ77Data(spritePalette.data, PLTT_SIZE_4BPP, PLTT_SIZE_4BPP))
{

View File

@ -977,8 +977,8 @@ static bool32 IsBuildingPCTile(u32 tileId)
static bool32 IsPlayerHousePCTile(u32 tileId)
{
return gMapHeader.mapLayout->secondaryTileset == &gTileset_BrendansMaysHouse
&& (tileId == METATILE_BrendansMaysHouse_BrendanPC_On
return gMapHeader.mapLayout->secondaryTileset == &gTileset_BrendansMaysHouse
&& (tileId == METATILE_BrendansMaysHouse_BrendanPC_On
|| tileId == METATILE_BrendansMaysHouse_BrendanPC_Off
|| tileId == METATILE_BrendansMaysHouse_MayPC_On
|| tileId == METATILE_BrendansMaysHouse_MayPC_Off);

View File

@ -211,7 +211,7 @@ void SaveObjectEvents(void)
gSaveBlock1Ptr->objectEvents[i].graphicsId = (graphicsId >> 8) | (graphicsId << 8);
gSaveBlock1Ptr->objectEvents[i].spriteId = 127; // magic number
// To avoid crash on vanilla, save follower as inactive
if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER)
if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER)
gSaveBlock1Ptr->objectEvents[i].active = FALSE;
}
}

View File

@ -550,7 +550,7 @@ static void ShowMapNamePopUpWindow(void)
if (OW_POPUP_GENERATION == GEN_5)
{
AddTextPrinterParameterized(mapNamePopUpWindowId, FONT_SHORT, mapDisplayHeader, 8, 2, TEXT_SKIP_DRAW, NULL);
if (OW_POPUP_BW_TIME_MODE != OW_POPUP_BW_TIME_NONE)
{
RtcCalcLocalTime();
@ -622,7 +622,7 @@ static void LoadMapNamePopUpWindowBg(void)
if (OW_POPUP_GENERATION == GEN_5)
{
popUpThemeId = sRegionMapSectionId_To_PopUpThemeIdMapping_BW[regionMapSectionId];
switch (popUpThemeId)
switch (popUpThemeId)
{
// add additional gen 5-style pop-up themes as cases here
case MAPPOPUP_THEME_BW_DEFAULT:

View File

@ -487,7 +487,7 @@ static void Task_HandleScrollingMultichoiceInput(u8 taskId)
{
bool32 done = FALSE;
s32 input = ListMenu_ProcessInput(gTasks[taskId].data[0]);
switch (input)
{
case LIST_HEADER:

View File

@ -466,7 +466,7 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item)
#define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_)
/* Give or create a mon to either player or opponent
/* Give or create a mon to either player or opponent
*/
void ScrCmd_createmon(struct ScriptContext *ctx)
{

View File

@ -581,7 +581,7 @@ bool32 IsVsSeekerEnabled(void)
{
if (I_VS_SEEKER_CHARGING == 0)
return FALSE;
return (CheckBagHasItem(ITEM_VS_SEEKER, 1));
}

View File

@ -37,7 +37,7 @@ SINGLE_BATTLE_TEST("Anger Point does not trigger when already at maximum Attack
MESSAGE("Primeape cut its own HP and maximized ATTACK!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
MESSAGE("A critical hit!");
NONE_OF {
NONE_OF {
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Primeape's Anger Point maxed its Attack!");
@ -64,7 +64,7 @@ SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit
MESSAGE("Primeape made a SUBSTITUTE!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
MESSAGE("A critical hit!");
NONE_OF {
NONE_OF {
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Primeape's Anger Point maxed its Attack!");

View File

@ -127,7 +127,7 @@ SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent s
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ability);
ABILITY_POPUP(opponent, ability);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
@ -186,7 +186,7 @@ SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body and
else{
ANIMATION(ANIM_TYPE_MOVE, move, player);
NONE_OF {
ABILITY_POPUP(opponent, ability);
ABILITY_POPUP(opponent, ability);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
MESSAGE("Foe Metang's Clear Body prevents stat loss!");

View File

@ -397,3 +397,25 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform back when it uses a move that f
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_CASTFORM);
}
}
SINGLE_BATTLE_TEST("Forecast transforms Castform when Cloud Nine ability user leaves the field")
{
u32 species = 0, ability = 0;
PARAMETRIZE { species = SPECIES_PSYDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
GIVEN {
PLAYER(SPECIES_CASTFORM) { Ability(ABILITY_FORECAST); }
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_FORECAST);
MESSAGE("Castform transformed!");
}
}

View File

@ -116,7 +116,7 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Cramorant in Gorging paralyzes the target if
}
SINGLE_BATTLE_TEST("(Gulp Missile) triggers even if the user is fainted by opposing mon")
{
{
GIVEN {
PLAYER(SPECIES_CRAMORANT) { HP(1); MaxHP(250); Ability(ABILITY_GULP_MISSILE); }
PLAYER(SPECIES_WOBBUFFET);

View File

@ -1,18 +1,264 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Harvest has a 50% chance to restore a Berry at the end of the turn");
TO_DO_BATTLE_TEST("Harvest always restores a Berry in Sunlight");
TO_DO_BATTLE_TEST("Harvest doesn't always restore a Berry if Cloud Nine/Air Lock is on the field");
TO_DO_BATTLE_TEST("Harvest restores a Berry even after being switched out and back in");
TO_DO_BATTLE_TEST("Harvest restores a Berry consumed by Fling");
TO_DO_BATTLE_TEST("Harvest restores a Berry consumed by Natural Gift");
ASSUMPTIONS
{
ASSUME(gItemsInfo[ITEM_SITRUS_BERRY].holdEffect == HOLD_EFFECT_RESTORE_PCT_HP);
ASSUME(I_SITRUS_BERRY_HEAL >= GEN_4);
ASSUME(gMovesInfo[MOVE_SUNNY_DAY].effect == EFFECT_SUNNY_DAY);
}
SINGLE_BATTLE_TEST("Harvest has a 50% chance to restore a Berry at the end of the turn")
{
PASSES_RANDOMLY(1, 2, RNG_HARVEST);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Harvest always restores a Berry in Sunlight")
{
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Harvest doesn't always restore a Berry if Cloud Nine/Air Lock is on the field")
{
PASSES_RANDOMLY(1, 2, RNG_HARVEST);
GIVEN {
PLAYER(SPECIES_GOLDUCK) { Ability(ABILITY_CLOUD_NINE); }
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Harvest restores a Berry even after being switched out and back in")
{
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 0); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Harvest restores a Berry consumed by Fling")
{
ASSUME(gMovesInfo[MOVE_FLING].effect == EFFECT_FLING);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_FLING); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, opponent);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Harvest restores a Berry consumed by Natural Gift")
{
ASSUME(gMovesInfo[MOVE_NATURAL_GIFT].effect == EFFECT_NATURAL_GIFT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_NATURAL_GIFT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_NATURAL_GIFT, opponent);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
}
}
TO_DO_BATTLE_TEST("Harvest only works once per turn"); // Check for berries that are consumed immediately, like Pecha Berry
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when destroyed by Incinerate");
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when knocked off by Knock Off");
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when eaten by Bug Bite/Pluck");
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry that's collected via Pickup");
TO_DO_BATTLE_TEST("Harvest order is affected by speed");
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when transfered to another Pokémon");
TO_DO_BATTLE_TEST("Harvest can restore a Berry that was transferred from another Pokémon");
TO_DO_BATTLE_TEST("Harvest can only restore the newest berry consumed that was transferred from another Pokémon instead of its original Berry");
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when destroyed by Incinerate")
{
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
ASSUME(MoveHasAdditionalEffect(MOVE_INCINERATE, MOVE_EFFECT_INCINERATE));
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_INCINERATE); MOVE(opponent, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_INCINERATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when knocked off by Knock Off")
{
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_KNOCK_OFF); MOVE(opponent, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when eaten by Bug Bite/Pluck")
{
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
ASSUME(MoveHasAdditionalEffect(MOVE_BUG_BITE, MOVE_EFFECT_BUG_BITE));
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); MOVE(opponent, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry that's collected via Pickup")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Speed(50); Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_EXEGGUTOR) { Speed(10); Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
DOUBLE_BATTLE_TEST("Harvest order is affected by speed")
{
GIVEN {
PLAYER(SPECIES_EXEGGUTOR) { Speed(2); Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_EXEGGUTOR) { Speed(10); Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
} WHEN {
TURN { MOVE(playerRight, MOVE_BULLDOZE); MOVE(playerLeft, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLDOZE, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, playerLeft);
ABILITY_POPUP(opponentLeft, ABILITY_HARVEST);
ABILITY_POPUP(playerLeft, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponentLeft->item, ITEM_SITRUS_BERRY);
EXPECT_EQ(playerLeft->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when transfered to another Pokémon")
{
ASSUME(gMovesInfo[MOVE_TRICK].effect == EFFECT_TRICK);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_TRICK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Harvest can restore a Berry that was transferred from another Pokémon")
{
ASSUME(gMovesInfo[MOVE_TRICK].effect == EFFECT_TRICK);
GIVEN {
PLAYER(SPECIES_TORKOAL) { Ability(ABILITY_DROUGHT); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); HP(100); MaxHP(500); }
} WHEN {
TURN { MOVE(opponent, MOVE_TRICK); MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
} THEN {
EXPECT_GT(opponent->hp, opponent->maxHP / 2); // eats 2 Sitrus
}
}
SINGLE_BATTLE_TEST("Harvest can only restore the newest berry consumed that was transferred from another Pokémon instead of its original Berry")
{
ASSUME(gMovesInfo[MOVE_TRICK].effect == EFFECT_TRICK);
ASSUME(gItemsInfo[ITEM_APICOT_BERRY].holdEffect == HOLD_EFFECT_SP_DEFENSE_UP);
GIVEN {
PLAYER(SPECIES_TORKOAL) { Ability(ABILITY_DROUGHT); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); HP(100); MaxHP(500); Item(ITEM_APICOT_BERRY); }
} WHEN {
TURN { MOVE(opponent, MOVE_TRICK); MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
ABILITY_POPUP(opponent, ABILITY_HARVEST);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
} THEN {
EXPECT_GT(opponent->hp, opponent->maxHP / 2); // eats 2 Sitrus
}
}

View File

@ -1,13 +1,21 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS {
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
}
SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
{
u32 move;
PARAMETRIZE { move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GLALIE) { Ability(ABILITY_ICE_BODY); }
} WHEN {
TURN { MOVE(player, MOVE_HAIL); MOVE(opponent, MOVE_SKILL_SWAP); }
TURN { MOVE(player, move); MOVE(opponent, MOVE_SKILL_SWAP); }
} SCENE {
NONE_OF { HP_BAR(player); }
}
@ -15,11 +23,14 @@ SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
SINGLE_BATTLE_TEST("Ice Body recovers 1/16th of Max HP in hail.")
{
u32 move;
PARAMETRIZE { move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_GLALIE) { Ability(ABILITY_ICE_BODY); HP(1); MaxHP(100); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_HAIL); }
TURN { MOVE(opponent, move); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_BODY);
HP_BAR(player, damage: -(100 / 16));
@ -27,4 +38,17 @@ SINGLE_BATTLE_TEST("Ice Body recovers 1/16th of Max HP in hail.")
}
}
TO_DO_BATTLE_TEST("Sand Rush doesn't recover HP if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Ice Body doesn't recover HP if Cloud Nine/Air Lock is on the field")
{
u32 move;
PARAMETRIZE { move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_GLALIE) { Ability(ABILITY_ICE_BODY); HP(1); MaxHP(100); }
OPPONENT(SPECIES_GOLDUCK) { Ability(ABILITY_CLOUD_NINE); }
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_ICE_BODY);
}
}

View File

@ -1,9 +1,137 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Ice Face blocks physical moves, changing Eiscue into its Noice Face form"); // Include Special move in test
TO_DO_BATTLE_TEST("Ice Face is restored if hail or snow begins while Noice Face Eiscue is out");
TO_DO_BATTLE_TEST("Ice Face is restored if Noice Face Eiscue is sent in while hail or snow is active");
TO_DO_BATTLE_TEST("Ice Face is not restored if Eiscue changes into Noice Face form while there's already hail");
TO_DO_BATTLE_TEST("Ice Face form change persists after switching out");
TO_DO_BATTLE_TEST("Ice Face doesn't transform Eiscue if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Ice Face blocks physical moves, changing Eiscue into its Noice Face form")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_EISCUE);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
}
}
SINGLE_BATTLE_TEST("Ice Face does not block special moves, Eiscue stays in Ice Face form")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_EMBER].category == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_EISCUE);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_ICE_FACE);
}
}
SINGLE_BATTLE_TEST("Ice Face is restored if hail or snow begins while Noice Face Eiscue is out")
{
u32 move;
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_HAIL; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
PLAYER(SPECIES_EISCUE);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { MOVE(opponent, move); }
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
}
}
SINGLE_BATTLE_TEST("Ice Face is restored if Noice Face Eiscue is sent in while hail or snow is active")
{
u32 move;
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_HAIL; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
PLAYER(SPECIES_EISCUE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
TURN { SWITCH(player, 1); MOVE(opponent, move); }
TURN { SWITCH(player, 0); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
}
}
SINGLE_BATTLE_TEST("Ice Face is not restored if Eiscue changes into Noice Face form while there's already hail or snow")
{
u32 move;
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_HAIL; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
PLAYER(SPECIES_EISCUE) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); MOVE(opponent, MOVE_TACKLE); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
MESSAGE("Eiscue used Celebrate!");
MESSAGE("Eiscue fainted!");
}
}
SINGLE_BATTLE_TEST("Ice Face form change persists after switching out")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_EISCUE) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_CELEBRATE); }
TURN { SWITCH(player, 0); MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
MESSAGE("Eiscue fainted!");
}
}
SINGLE_BATTLE_TEST("Ice Face doesn't transform Eiscue if Cloud Nine/Air Lock is on the field")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_EISCUE) { HP(1); }
OPPONENT(SPECIES_RAYQUAZA) { Ability(ABILITY_AIR_LOCK); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_FACE);
MESSAGE("Eiscue transformed!");
MESSAGE("Eiscue fainted!");
}
}

View File

@ -13,5 +13,5 @@ SINGLE_BATTLE_TEST("Magic Guard prevents recoil damage to the user")
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_EDGE, player);
HP_BAR(opponent);
NOT HP_BAR(player);
}
}
}

View File

@ -0,0 +1,295 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gItemsInfo[ITEM_SITRUS_BERRY].holdEffect == HOLD_EFFECT_RESTORE_PCT_HP);
ASSUME(I_SITRUS_BERRY_HEAL >= GEN_4);
}
SINGLE_BATTLE_TEST("Pickup grants an item used by another Pokémon")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
} THEN {
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant the user their item")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant another Pokémon's popped Air Balloon")
{
ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON);
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_AIR_BALLOON); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Air Balloon!");
}
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an item not used that turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
TURN { SWITCH(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an item after its holder faints")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_MEMENTO); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an used item if holder is replaced")
{
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(300); HP(151); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(300); HP(151); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 0); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, opponent);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an item if it destroyed the item with Incinerate")
{
ASSUME(MoveHasAdditionalEffect(MOVE_INCINERATE, MOVE_EFFECT_INCINERATE));
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_INCINERATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_INCINERATE, player);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an item if it knocked off that item")
{
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_KNOCK_OFF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an item if the user eats it with Bug Bite/Pluck")
{
ASSUME(MoveHasAdditionalEffect(MOVE_BUG_BITE, MOVE_EFFECT_BUG_BITE));
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup doesn't grant an used item if its user already restored it")
{
ASSUME(gMovesInfo[MOVE_RECYCLE].effect == EFFECT_RECYCLE);
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_RECYCLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_RECYCLE, opponent);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
}
} THEN {
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Pickup restores an item that has been Flinged")
{
ASSUME(gMovesInfo[MOVE_FLING].effect == EFFECT_FLING);
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(opponent, MOVE_FLING); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, opponent);
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
} THEN {
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
}
}
SINGLE_BATTLE_TEST("Pickup restores an item that was used by Natural Gift")
{
ASSUME(gMovesInfo[MOVE_NATURAL_GIFT].effect == EFFECT_NATURAL_GIFT);
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(opponent, MOVE_NATURAL_GIFT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_NATURAL_GIFT, opponent);
ABILITY_POPUP(player, ABILITY_PICKUP);
MESSAGE("Zigzagoon found one Sitrus Berry!");
} THEN {
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
}
}
DOUBLE_BATTLE_TEST("Pickup triggers based on Speed order")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Speed(1); Ability(ABILITY_PICKUP); }
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_ZIGZAGOON) { Speed(50); Ability(ABILITY_PICKUP); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
ABILITY_POPUP(opponentRight, ABILITY_PICKUP);
NOT ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
} THEN {
EXPECT_EQ(opponentRight->item, ITEM_SITRUS_BERRY);
EXPECT_EQ(playerLeft->item, ITEM_NONE);
}
}
DOUBLE_BATTLE_TEST("Pickup grants a random item used by another Pokémon")
{
PASSES_RANDOMLY(1, 3, RNG_PICKUP);
ASSUME(gItemsInfo[ITEM_WHITE_HERB].holdEffect == HOLD_EFFECT_RESTORE_STATS);
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BULLDOZE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLDOZE, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
} THEN {
EXPECT_EQ(playerLeft->item, ITEM_SITRUS_BERRY);
}
}
DOUBLE_BATTLE_TEST("Pickup doesn't trigger more than once per turn")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { HP(1); Ability(ABILITY_PICKUP); }
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BULLDOZE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLDOZE, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
NOT ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
} THEN {
EXPECT_EQ(playerLeft->item, ITEM_NONE);
EXPECT_GT(playerLeft->hp, 1);
EXPECT_LT(playerLeft->hp, playerLeft->maxHP/2);
}
}

View File

@ -1,6 +1,10 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS {
ASSUME(gMovesInfo[MOVE_RAIN_DANCE].effect == EFFECT_RAIN_DANCE);
}
SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain")
{
GIVEN {
@ -11,8 +15,18 @@ SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain")
} SCENE {
ABILITY_POPUP(player, ABILITY_RAIN_DISH);
MESSAGE("Ludicolo's Rain Dish restored its HP a little!");
HP_BAR(player, damage: -(100 / 16));
HP_BAR(player, damage: -(100 / 16));
}
}
TO_DO_BATTLE_TEST("Rain Dish doesn't recover HP if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Rain Dish doesn't recover HP if Cloud Nine/Air Lock is on the field")
{
GIVEN {
PLAYER(SPECIES_LUDICOLO) { Ability(ABILITY_RAIN_DISH); HP(1); MaxHP(100); }
OPPONENT(SPECIES_GOLDUCK) { Ability(ABILITY_CLOUD_NINE); }
} WHEN {
TURN { MOVE(opponent, MOVE_RAIN_DANCE); }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_RAIN_DISH);
}
}

View File

@ -0,0 +1,127 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_TAILWIND].effect == EFFECT_TAILWIND);
ASSUME(gMovesInfo[MOVE_TAILWIND].windMove == TRUE);
}
SINGLE_BATTLE_TEST("Wind Rider raises Attack by one stage if it sets up Tailwind")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
} WHEN {
TURN { MOVE(opponent, MOVE_TAILWIND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponent);
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Bramblin's Attack rose!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}
DOUBLE_BATTLE_TEST("Wind Rider raises Attack by one stage if Tailwind is setup by its partner")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_TAILWIND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft);
ABILITY_POPUP(opponentRight, ABILITY_WIND_RIDER);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Foe Bramblin's Attack rose!");
} THEN {
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Wind Rider doesn't raise Attack if opponent sets up Tailwind")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
} WHEN {
TURN { MOVE(player, MOVE_TAILWIND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, player);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Bramblin's Attack rose!");
}
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Wind Rider raises Attack by one stage if switched into Tailwind on its side of the field")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
} WHEN {
TURN { MOVE(opponent, MOVE_TAILWIND); }
TURN { SWITCH(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponent);
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Bramblin's Wind Rider raised its Attack!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Wind Rider activates when it's no longer effected by Neutralizing Gas")
{
GIVEN {
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
} WHEN {
TURN { MOVE(opponent, MOVE_TAILWIND); }
TURN { SWITCH(player, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing Gas filled the area!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponent);
SWITCH_OUT_MESSAGE("Weezing");
MESSAGE("The effects of Neutralizing Gas wore off!");
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Bramblin's Wind Rider raised its Attack!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Wind Rider absorbs Wind moves and raises Attack by one stage")
{
ASSUME(gMovesInfo[MOVE_GUST].windMove == TRUE);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
} WHEN {
TURN { MOVE(player, MOVE_GUST); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUST, player);
HP_BAR(opponent);
}
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Bramblin's Attack rose!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}

View File

@ -13,7 +13,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Mirror Coat against specia
ASSUME(gSpeciesInfo[SPECIES_GROVYLE].baseSpAttack == 85);
ASSUME(gSpeciesInfo[SPECIES_GROVYLE].baseAttack == 65);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
PLAYER(SPECIES_GROVYLE) { Level(20); Moves(MOVE_ENERGY_BALL); }
PLAYER(SPECIES_GROVYLE) { Level(20); Moves(MOVE_ENERGY_BALL); }
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_TACKLE, MOVE_MIRROR_COAT); }
} WHEN {
TURN { MOVE(player, MOVE_ENERGY_BALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_MIRROR_COAT : MOVE_TACKLE); }
@ -32,7 +32,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Counter against physical a
ASSUME(gSpeciesInfo[SPECIES_MARSHTOMP].baseAttack == 85);
ASSUME(gSpeciesInfo[SPECIES_MARSHTOMP].baseSpAttack == 60);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
PLAYER(SPECIES_MARSHTOMP) { Level(20); Moves(MOVE_WATERFALL); }
PLAYER(SPECIES_MARSHTOMP) { Level(20); Moves(MOVE_WATERFALL); }
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_TACKLE, MOVE_COUNTER); }
} WHEN {
TURN { MOVE(player, MOVE_WATERFALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_COUNTER : MOVE_TACKLE); }
@ -49,7 +49,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will prioritize Revenge if slower")
GIVEN {
ASSUME(gMovesInfo[MOVE_REVENGE].effect == EFFECT_REVENGE);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
PLAYER(SPECIES_GROVYLE) { Level(20); Speed(4); Moves(MOVE_ENERGY_BALL); }
PLAYER(SPECIES_GROVYLE) { Level(20); Speed(4); Moves(MOVE_ENERGY_BALL); }
OPPONENT(SPECIES_CASTFORM) { Level(19); Speed(3); Moves(MOVE_TACKLE, MOVE_REVENGE); }
} WHEN {
TURN { MOVE(player, MOVE_ENERGY_BALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_REVENGE : MOVE_TACKLE); }
@ -83,7 +83,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI prefers high damage moves at the expens
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
PLAYER(SPECIES_PSYDUCK) { Level(5); Moves(MOVE_TACKLE); }
PLAYER(SPECIES_PSYDUCK) { Level(5); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_THUNDER, MOVE_THUNDERBOLT); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_THUNDER : MOVE_THUNDERBOLT); }

View File

@ -1234,7 +1234,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Replenish recycles allies' berries 50\% of t
GIVEN {
ASSUME(gMovesInfo[MOVE_G_MAX_REPLENISH].argument == MAX_EFFECT_RECYCLE_BERRIES);
PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); }
PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); }
PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); Ability(ABILITY_THICK_FAT); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); }
} WHEN {

View File

@ -646,7 +646,7 @@ SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pok
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUBBLE, gimmick: GIMMICK_TERA);
TURN { MOVE(player, MOVE_BUBBLE, gimmick: GIMMICK_TERA);
MOVE(opponent, MOVE_EMBER); }
} SCENE {
MESSAGE("Greninja used Bubble!");

View File

@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves do not retain priority")
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE);
TURN { MOVE(opponent, MOVE_TACKLE);
MOVE(player, MOVE_QUICK_ATTACK, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
@ -597,7 +597,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Searing Sunraze Smash ignores the target's abilitie
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEARING_SUNRAZE_SMASH, player);
HP_BAR(opponent);
MESSAGE("A critical hit!");
}
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Revelation Dance always transforms into Breakneck Blitz")

View File

@ -69,7 +69,7 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Mycelium Might")
} WHEN {
TURN { MOVE(opponent, MOVE_SPORE); MOVE(player, MOVE_SPORE); }
} SCENE {
if (item == ITEM_ABILITY_SHIELD) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, opponent);

View File

@ -0,0 +1,22 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_GUARD_SPLIT].effect == EFFECT_GUARD_SPLIT);
}
SINGLE_BATTLE_TEST("Guard Split averages users and targets Def and Sp. Def stats")
{
GIVEN {
PLAYER(SPECIES_BULBASAUR);
OPPONENT(SPECIES_IVYSAUR);
} WHEN {
TURN { MOVE(player, MOVE_GUARD_SPLIT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUARD_SPLIT, player);
} THEN {
EXPECT_EQ(player->defense, opponent->defense);
EXPECT_EQ(player->spDefense, opponent->spDefense);
}
}

View File

@ -10,7 +10,7 @@ ASSUMPTIONS
DOUBLE_BATTLE_TEST("Heal Bell cures the entire party")
{
u32 move;
PARAMETRIZE { move = MOVE_HEAL_BELL; }
PARAMETRIZE { move = MOVE_AROMATHERAPY; }
@ -39,10 +39,10 @@ DOUBLE_BATTLE_TEST("Heal Bell cures the entire party")
DOUBLE_BATTLE_TEST("Heal Bell does not cure soundproof partners")
{
u32 ability;
PARAMETRIZE { ability = ABILITY_SCRAPPY; }
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; }
ASSUME(B_HEAL_BELL_SOUNDPROOF != GEN_5);
GIVEN {
@ -65,10 +65,10 @@ DOUBLE_BATTLE_TEST("Heal Bell does not cure soundproof partners")
SINGLE_BATTLE_TEST("Heal Bell cures inactive soundproof Pokemon")
{
u32 ability;
PARAMETRIZE { ability = ABILITY_SCRAPPY; }
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; }
ASSUME(B_HEAL_BELL_SOUNDPROOF >= GEN_5);
GIVEN {
@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Heal Bell cures inactive soundproof Pokemon")
SINGLE_BATTLE_TEST("Heal Bell cures a soundproof user")
{
{
ASSUME(B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_8);
GIVEN {

View File

@ -90,7 +90,7 @@ SINGLE_BATTLE_TEST("Knock Off does not remove items through Substitute")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); };
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE);
TURN { MOVE(opponent, MOVE_SUBSTITUTE);
MOVE(player, MOVE_KNOCK_OFF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
@ -112,7 +112,7 @@ SINGLE_BATTLE_TEST("Recycle cannot recover an item removed by Knock Off")
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF);
MESSAGE("Wobbuffet knocked off Foe Wobbuffet's Leftovers!");
MESSAGE("Foe Wobbuffet used Recycle!");
MESSAGE("But it failed!");
} THEN {

View File

@ -0,0 +1,22 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_POWER_SPLIT].effect == EFFECT_POWER_SPLIT);
}
SINGLE_BATTLE_TEST("Power Split averages user and targets Atk and Sp. Atk stats")
{
GIVEN {
PLAYER(SPECIES_BULBASAUR);
OPPONENT(SPECIES_IVYSAUR);
} WHEN {
TURN { MOVE(player, MOVE_POWER_SPLIT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_SPLIT, player);
} THEN {
EXPECT_EQ(player->attack, opponent->attack);
EXPECT_EQ(player->spAttack, opponent->spAttack);
}
}

View File

@ -0,0 +1,137 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_RETALIATE].effect == EFFECT_RETALIATE);
}
SINGLE_BATTLE_TEST("Retaliate doubles in base power the turn after an ally faints")
{
s16 damage[2];
GIVEN {
PLAYER(SPECIES_WYNAUT) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
TURN { MOVE(player, MOVE_RETALIATE); }
TURN { MOVE(player, MOVE_RETALIATE); }
} SCENE {
HP_BAR(opponent, captureDamage: &damage[0]);
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
}
}
SINGLE_BATTLE_TEST("Retaliate doubles in base power the turn after an ally faints (opponent)")
{
s16 damage[2];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); SEND_OUT(opponent, 1); }
TURN { MOVE(opponent, MOVE_RETALIATE); }
TURN { MOVE(opponent, MOVE_RETALIATE); }
} SCENE {
HP_BAR(player, captureDamage: &damage[0]);
HP_BAR(player, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
}
}
DOUBLE_BATTLE_TEST("Retaliate works with passive damage")
{
s16 damage[2];
u32 move;
u32 move2 = MOVE_CELEBRATE;
struct BattlePokemon *moveTarget = playerLeft;
PARAMETRIZE { move = MOVE_TOXIC; moveTarget = playerLeft; }
PARAMETRIZE { move = MOVE_POISON_POWDER; moveTarget = playerLeft; }
PARAMETRIZE { move = MOVE_WILL_O_WISP; moveTarget = playerLeft; }
#if B_USE_FROSTBITE == TRUE
PARAMETRIZE { move = MOVE_ICE_BEAM; moveTarget = playerLeft; }
#endif
PARAMETRIZE { move = MOVE_SANDSTORM; moveTarget = playerLeft; }
PARAMETRIZE { move = MOVE_HAIL; moveTarget = playerLeft; }
PARAMETRIZE { move = MOVE_LEECH_SEED; moveTarget = playerLeft; }
PARAMETRIZE { move = MOVE_MAGMA_STORM; moveTarget = playerLeft; }
PARAMETRIZE { move = MOVE_FLAME_BURST; moveTarget = playerRight; }
PARAMETRIZE { move = MOVE_FIRE_PLEDGE; moveTarget = playerRight; move2 = MOVE_GRASS_PLEDGE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP);
#if B_USE_FROSTBITE == TRUE
ASSUME(gMovesInfo[MOVE_ICE_BEAM].additionalEffects[0].moveEffect == MOVE_EFFECT_FREEZE_OR_FROSTBITE);
#endif
ASSUME(gMovesInfo[MOVE_SANDSTORM].effect == EFFECT_SANDSTORM);
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
ASSUME(gMovesInfo[MOVE_LEECH_SEED].effect == EFFECT_LEECH_SEED);
ASSUME(gMovesInfo[MOVE_MAGMA_STORM].additionalEffects[0].moveEffect == MOVE_EFFECT_WRAP);
ASSUME(gMovesInfo[MOVE_FLAME_BURST].additionalEffects[0].moveEffect == MOVE_EFFECT_FLAME_BURST);
PLAYER(SPECIES_WYNAUT) { Ability(ABILITY_SHADOW_TAG); HP(18); }
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CLEFABLE) { Ability(ABILITY_MAGIC_GUARD); Level(1); }
OPPONENT(SPECIES_CLEFABLE) { Ability(ABILITY_MAGIC_GUARD); }
} WHEN {
TURN { MOVE(opponentRight, move2, target: moveTarget); MOVE(opponentLeft, move, target: moveTarget); MOVE(playerLeft, MOVE_CELEBRATE); SEND_OUT(playerLeft, 2); }
TURN { MOVE(opponentRight, MOVE_CELEBRATE, target: moveTarget); MOVE(playerLeft, MOVE_RETALIATE, target: opponentRight); }
TURN { MOVE(opponentRight, MOVE_CELEBRATE, target: moveTarget); MOVE(playerLeft, MOVE_RETALIATE, target: opponentRight); }
} SCENE {
if (move != MOVE_FLAME_BURST)
MESSAGE("Wynaut used Celebrate!");
HP_BAR(opponentRight, captureDamage: &damage[0]);
HP_BAR(opponentRight, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
}
}
SINGLE_BATTLE_TEST("Retaliate works with Perish Song")
{
s16 damage[2];
GIVEN {
ASSUME(gMovesInfo[MOVE_PERISH_SONG].effect == EFFECT_PERISH_SONG);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KOMMO_O) { Ability(ABILITY_SOUNDPROOF); }
} WHEN {
TURN { MOVE(opponent, MOVE_PERISH_SONG); }
TURN { MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
TURN { MOVE(player, MOVE_RETALIATE); }
TURN { MOVE(player, MOVE_RETALIATE); }
} SCENE {
HP_BAR(opponent, captureDamage: &damage[0]);
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
}
}
SINGLE_BATTLE_TEST("Retaliate works with self-inflicted fainting")
{
s16 damage[2];
GIVEN {
ASSUME(gMovesInfo[MOVE_HEALING_WISH].effect == EFFECT_HEALING_WISH);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HEALING_WISH); SEND_OUT(player, 1); }
TURN { MOVE(player, MOVE_RETALIATE); }
TURN { MOVE(player, MOVE_RETALIATE); }
} SCENE {
HP_BAR(opponent, captureDamage: &damage[0]);
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
}
}

View File

@ -37,7 +37,7 @@ SINGLE_BATTLE_TEST("Revelation Dance changes its type depending on the user's 1s
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
HP_BAR(opponent);
MESSAGE("It's not very effective…");
}
}
}
}
}
@ -142,7 +142,7 @@ SINGLE_BATTLE_TEST("Revelation Dance becomes Normal type if used by a Typeless P
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
HP_BAR(opponent);
MESSAGE("It's not very effective…");
MESSAGE("It's not very effective…");
}
}
}

View File

@ -0,0 +1,115 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_ROUND].effect == EFFECT_ROUND);
}
DOUBLE_BATTLE_TEST("Round allows other battlers which also selected the moves to immediately use the move, ignoring turn order")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LAGGING_TAIL].holdEffect == HOLD_EFFECT_LAGGING_TAIL);
ASSUME(gMovesInfo[MOVE_IRON_HEAD].additionalEffects[0].moveEffect == MOVE_EFFECT_FLINCH);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LAGGING_TAIL); }
} WHEN {
TURN {
MOVE(playerRight, MOVE_CELEBRATE);
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
MOVE(playerLeft, MOVE_IRON_HEAD, target: opponentRight);
MOVE(opponentRight, MOVE_ROUND, target: playerLeft);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_HEAD, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_HEAD, playerLeft);
}
}
DOUBLE_BATTLE_TEST("Round usages beyond the first one has double base power")
{
s16 damage[2];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
MOVE(opponentRight, MOVE_ROUND, target: playerLeft);
}
} SCENE {
HP_BAR(playerLeft, captureDamage: &damage[0]);
HP_BAR(playerLeft, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
}
}
DOUBLE_BATTLE_TEST("Round still preserves the turn order outside of the other Round users moving immediately")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
MOVE(playerRight, MOVE_CELEBRATE);
MOVE(playerLeft, MOVE_CELEBRATE);
MOVE(opponentRight, MOVE_ROUND, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
}
}
DOUBLE_BATTLE_TEST("Round still preserves the turn order outside of the other Round users moving immediately with switch")
{
KNOWN_FAILING; // #5148
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
SWITCH(playerRight, 2);
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
MOVE(playerLeft, MOVE_CELEBRATE);
MOVE(opponentRight, MOVE_ROUND, target: playerLeft);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
}
}
DOUBLE_BATTLE_TEST("Round causes opposing pokemon to use Round immediately")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LAGGING_TAIL].holdEffect == HOLD_EFFECT_LAGGING_TAIL);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LAGGING_TAIL); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_ROUND, target: opponentLeft); MOVE(playerLeft, MOVE_CELEBRATE, target: opponentRight); MOVE(opponentRight, MOVE_ROUND, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, playerRight);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
}
}

View File

@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Sleep Talk fails if not asleep")
u32 status;
PARAMETRIZE { status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_NONE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Status1(status); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH); }
OPPONENT(SPECIES_WOBBUFFET);
@ -35,7 +35,7 @@ SINGLE_BATTLE_TEST("Sleep Talk fails if not asleep")
SINGLE_BATTLE_TEST("Sleep Talk works if user has Comatose")
{
GIVEN {
PLAYER(SPECIES_KOMALA) { Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH); }
OPPONENT(SPECIES_WOBBUFFET);

View File

@ -71,4 +71,4 @@ SINGLE_BATTLE_TEST("Telekinesis makes the target immune to Ground-type attacks")
TO_DO_BATTLE_TEST("Baton Pass passes Telekinesis' effect");
//Bulbapedia doesn't confirm what happens with Diglett, Dugtrio, Sandygast and Palossand, so it needs to be tested in-game.
TO_DO_BATTLE_TEST("Baton Pass removes Telekinesis' effect disappears if the switching-in mon is Mega Gengar");
TO_DO_BATTLE_TEST("Baton Pass removes Telekinesis' effect disappears if the switching-in mon is Mega Gengar");

View File

@ -70,3 +70,15 @@ DOUBLE_BATTLE_TEST("Hail deals damage based on turn order")
HP_BAR(playerRight);
}
}
SINGLE_BATTLE_TEST("Hail damage rounds properly when maxHP < 16")
{
GIVEN {
PLAYER(SPECIES_MAGIKARP) { Level(1); MaxHP(11); HP(11); }
OPPONENT(SPECIES_GLALIE);
} WHEN {
TURN { MOVE(opponent, MOVE_HAIL); }
} SCENE {
HP_BAR(player, damage: 1);
}
}

View File

@ -82,3 +82,15 @@ DOUBLE_BATTLE_TEST("Sandstorm deals damage based on turn order")
HP_BAR(playerRight);
}
}
SINGLE_BATTLE_TEST("Sandstorm damage rounds properly when maxHP < 16")
{
GIVEN {
PLAYER(SPECIES_MAGIKARP) { Level(1); MaxHP(11); HP(11); }
OPPONENT(SPECIES_SANDSLASH);
} WHEN {
TURN { MOVE(opponent, MOVE_SANDSTORM); }
} SCENE {
HP_BAR(player, damage: 1);
}
}

View File

@ -410,7 +410,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32
if (STATE->trials == 1)
{
u32 n = 0, i;
for (i = lo; i < hi; i++)
for (i = lo; i <= hi; i++)
if (!reject(i))
n++;
STATE->trials = n;

View File

@ -13,7 +13,7 @@ with open("./include/config/pokemon.h", "r") as file:
def parse_mon_name(name):
return re.sub(r'(?!^)([A-Z]+)', r'_\1', name).upper()
tm_moves = []
tutor_moves = []
@ -196,7 +196,7 @@ for move in tm_moves:
header += "// " + longest_move_name * "*" + " //\n"
header_print(tutor_title)
tutor_moves.sort() # alphabetically sort tutor moves for easier referencing
for move in tutor_moves:
for move in tutor_moves:
header_print("- " + move)
header += "// " + longest_move_name * "*" + " //\n"
header_print(universal_title)

View File

@ -673,14 +673,14 @@ int AsmFile::FindLastLineNumber(std::string& filename)
if (pos < 0)
RaiseError("line indicator for header file not found before `enum`");
pos++;
while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t')
pos++;
if (!IsAsciiDigit(m_buffer[pos]))
RaiseError("malformatted line indicator found before `enum`, expected line number");
unsigned n = 0;
int digit = 0;
while ((digit = ConvertDigit(m_buffer[pos++], 10)) != -1)
@ -715,7 +715,7 @@ int AsmFile::FindLastLineNumber(std::string& filename)
filename += c;
}
return n + linebreaks - 1;
}