25/10/25 Master to upcoming merge (#8041)

This commit is contained in:
Bassoonian 2025-10-26 00:35:59 +02:00 committed by GitHub
commit 776a2e762b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 1272 additions and 217 deletions

View File

@ -357,6 +357,90 @@
"contributions": [
"design"
]
},
{
"login": "Syreldar",
"name": "Enrico Drago",
"avatar_url": "https://avatars.githubusercontent.com/u/42327659?v=4",
"profile": "https://metin2.dev/index.php",
"contributions": [
"doc",
"userTesting"
]
},
{
"login": "Pyredrid",
"name": "Pyredrid",
"avatar_url": "https://avatars.githubusercontent.com/u/8324784?v=4",
"profile": "https://github.com/Pyredrid",
"contributions": [
"userTesting",
"maintenance"
]
},
{
"login": "mvit",
"name": "mv",
"avatar_url": "https://avatars.githubusercontent.com/u/128863?v=4",
"profile": "https://github.com/mvit",
"contributions": [
"code",
"design"
]
},
{
"login": "Mother-Of-Dragons",
"name": "Avara",
"avatar_url": "https://avatars.githubusercontent.com/u/31101124?v=4",
"profile": "https://github.com/Mother-Of-Dragons",
"contributions": [
"data"
]
},
{
"login": "Doesnty",
"name": "Doesnty",
"avatar_url": "https://avatars.githubusercontent.com/u/6163136?v=4",
"profile": "https://github.com/Doesnty",
"contributions": [
"design"
]
},
{
"login": "FosterProgramming",
"name": "FosterProgramming",
"avatar_url": "https://avatars.githubusercontent.com/u/178871164?v=4",
"profile": "https://github.com/FosterProgramming",
"contributions": [
"code"
]
},
{
"login": "Squeetz",
"name": "Squeetz",
"avatar_url": "https://avatars.githubusercontent.com/u/21145213?v=4",
"profile": "https://github.com/Squeetz",
"contributions": [
"maintenance"
]
},
{
"login": "ghostyboyy97",
"name": "ghostyboyy97",
"avatar_url": "https://avatars.githubusercontent.com/u/106448956?v=4",
"profile": "https://github.com/ghostyboyy97",
"contributions": [
"code"
]
},
{
"login": "HashtagMarky",
"name": "Marky",
"avatar_url": "https://avatars.githubusercontent.com/u/143505183?v=4",
"profile": "http://hashtagmarky.github.io",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@ -62,6 +62,19 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://tustin2121.github.io/"><img src="https://avatars.githubusercontent.com/u/794812?v=4?s=100" width="100px;" alt="tustin2121"/><br /><sub><b>tustin2121</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=tustin2121" title="Documentation">📖</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=tustin2121" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ddaretrogamer"><img src="https://avatars.githubusercontent.com/u/131238004?v=4?s=100" width="100px;" alt="Phantonomy"/><br /><sub><b>Phantonomy</b></sub></a><br /><a href="#design-Ddaretrogamer" title="Design">🎨</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://metin2.dev/index.php"><img src="https://avatars.githubusercontent.com/u/42327659?v=4?s=100" width="100px;" alt="Enrico Drago"/><br /><sub><b>Enrico Drago</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Syreldar" title="Documentation">📖</a> <a href="#userTesting-Syreldar" title="User Testing">📓</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Pyredrid"><img src="https://avatars.githubusercontent.com/u/8324784?v=4?s=100" width="100px;" alt="Pyredrid"/><br /><sub><b>Pyredrid</b></sub></a><br /><a href="#userTesting-Pyredrid" title="User Testing">📓</a> <a href="#maintenance-Pyredrid" title="Maintenance">🚧</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mvit"><img src="https://avatars.githubusercontent.com/u/128863?v=4?s=100" width="100px;" alt="mv"/><br /><sub><b>mv</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=mvit" title="Code">💻</a> <a href="#design-mvit" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Mother-Of-Dragons"><img src="https://avatars.githubusercontent.com/u/31101124?v=4?s=100" width="100px;" alt="Avara"/><br /><sub><b>Avara</b></sub></a><br /><a href="#data-Mother-Of-Dragons" title="Data">🔣</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Doesnty"><img src="https://avatars.githubusercontent.com/u/6163136?v=4?s=100" width="100px;" alt="Doesnty"/><br /><sub><b>Doesnty</b></sub></a><br /><a href="#design-Doesnty" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FosterProgramming"><img src="https://avatars.githubusercontent.com/u/178871164?v=4?s=100" width="100px;" alt="FosterProgramming"/><br /><sub><b>FosterProgramming</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=FosterProgramming" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Squeetz"><img src="https://avatars.githubusercontent.com/u/21145213?v=4?s=100" width="100px;" alt="Squeetz"/><br /><sub><b>Squeetz</b></sub></a><br /><a href="#maintenance-Squeetz" title="Maintenance">🚧</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ghostyboyy97"><img src="https://avatars.githubusercontent.com/u/106448956?v=4?s=100" width="100px;" alt="ghostyboyy97"/><br /><sub><b>ghostyboyy97</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ghostyboyy97" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://hashtagmarky.github.io"><img src="https://avatars.githubusercontent.com/u/143505183?v=4?s=100" width="100px;" alt="Marky"/><br /><sub><b>Marky</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=HashtagMarky" title="Code">💻</a></td>
</tr>
</tbody>
<tfoot>
<tr>

View File

@ -2368,8 +2368,8 @@
.4byte \jumpInstr
.endm
.macro jumpifleafguardprotected battler:req, jumpInstr:req
callnative BS_JumpIfLeafGuardProtected
.macro jumpifabilitypreventsrest battler:req, jumpInstr:req
callnative BS_JumpIfAbilityPreventsRest
.byte \battler
.4byte \jumpInstr
.endm

View File

@ -2482,7 +2482,7 @@
@ Hides any follower Pokémon if present, putting them into their Poké Ball; by default waits for their movement to finish.
.macro hidefollower wait=1
callnative ScrFunc_hidefollower
.byte SCR_OP_HIDEFOLLOWER
.2byte \wait
.endm

View File

@ -384,6 +384,7 @@ BattleScript_SaltCureExtraDamage::
printstring STRINGID_TARGETISHURTBYSALTCURE
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_ATTACKER
tryactivateitem BS_ATTACKER, ON_ITEM_HP_THRESHOLD
end2
BattleScript_EffectCorrosiveGas::
@ -2813,9 +2814,13 @@ BattleScript_EffectLightScreen::
BattleScript_EffectRest::
attackcanceler
.if B_LEAF_GUARD_PREVENTS_REST >= GEN_5
jumpifleafguardprotected BS_TARGET, BattleScript_LeafGuardPreventsRest
.endif
jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_RestIsAlreadyAsleep
jumpifability BS_ATTACKER, ABILITY_COMATOSE, BattleScript_RestIsAlreadyAsleep
jumpifuproarwakes BattleScript_RestCantSleep
jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_InsomniaProtects
jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_InsomniaProtects
jumpifability BS_ATTACKER, ABILITY_PURIFYING_SALT, BattleScript_InsomniaProtects
jumpifabilitypreventsrest BS_TARGET, BattleScript_AbilityPreventsRest
trysetrest
pause B_WAIT_TIME_SHORT
printfromtable gRestUsedStringIds
@ -2837,7 +2842,7 @@ BattleScript_RestIsAlreadyAsleep::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_LeafGuardPreventsRest::
BattleScript_AbilityPreventsRest::
pause B_WAIT_TIME_SHORT
printstring STRINGID_BUTITFAILED
waitmessage B_WAIT_TIME_LONG
@ -5378,9 +5383,8 @@ BattleScript_ToxicDebrisActivates::
printstring STRINGID_POISONSPIKESSCATTERED
waitmessage B_WAIT_TIME_LONG
BattleScript_ToxicDebrisRet:
copybyte sBATTLER, gBattlerTarget
copybyte gBattlerTarget, gBattlerAttacker
copybyte gBattlerAttacker, sBATTLER
restoretarget
restoreattacker
return
BattleScript_EarthEaterActivates::
@ -8068,14 +8072,28 @@ BattleScript_ActivateTeraformZero_RemoveWeather:
removeweather
printfromtable gWeatherEndsStringIds
waitmessage B_WAIT_TIME_LONG
jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZero_End
call BattleScript_ActivateWeatherAbilities
jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZeroEffects
BattleScript_ActivateTeraformZero_RemoveTerrain:
removeterrain
playanimation BS_ATTACKER, B_ANIM_RESTORE_BG
printfromtable gTerrainStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_ActivateTeraformZero_End:
BattleScript_ActivateTeraformZeroEffects:
saveattacker
savetarget
tryboosterenergy ON_ANY
resetterrainabilityflags
setbyte gBattlerAttacker, 0
BattleScript_ActivateTeraformZeroLoop:
copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1
activateterrainchangeabilities BS_TARGET
activateweatherchangeabilities BS_TARGET
addbyte gBattlerAttacker, 1
jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_ActivateTeraformZeroLoop
restoreattacker
restoretarget
BattleScript_ActivateTeraformZero_End:
end3
BattleScript_QuickClawActivation::

View File

@ -18,6 +18,7 @@ BattleFrontier_BattleDomePreBattleRoom_OnFrame:
BattleFrontier_BattleDomePreBattleRoom_EventScript_EnterRoom::
goto_if_eq VAR_0x8006, 1, BattleFrontier_BattleDomePreBattleRoom_EventScript_ReturnFromBattle
delay 1
frontier_set FRONTIER_DATA_RECORD_DISABLED, TRUE
setvar VAR_TEMP_0, 1
applymovement LOCALID_PLAYER, BattleFrontier_BattleDomePreBattleRoom_Movement_PlayerEnter

View File

@ -251,6 +251,7 @@ gScriptCmdTable::
script_cmd_table_entry SCR_OP_BUFFERITEMNAMEPLURAL ScrCmd_bufferitemnameplural, requests_effects=1 @ 0xe2
script_cmd_table_entry SCR_OP_DYNMULTICHOICE ScrCmd_dynmultichoice, requests_effects=1 @ 0xe3
script_cmd_table_entry SCR_OP_DYNMULTIPUSH ScrCmd_dynmultipush, requests_effects=1 @ 0xe4
script_cmd_table_entry SCR_OP_HIDEFOLLOWER ScrCmd_hidefollower, requests_effects=1 @ 0xe5
.if ALLOCATE_SCRIPT_CMD_TABLE
gScriptCmdTableEnd::

View File

@ -3,21 +3,40 @@
- [README](./README.md)
- [Installation](./INSTALL.md)
- [Setting up WSL1 (Legacy Portion)](./legacy_WSL1_INSTALL.md)
- [ChromeOS](./install/chromeos/CHROME_OS.md)
- [Linux]()
- [ARCH_LINUX](./install/linux/ARCH_LINUX.md)
- [DEBIAN](./install/linux/DEBIAN.md)
- [NIXOS](./install/linux/NIXOS.md)
- [OTHERS](./install/linux/OTHERS.md)
- [UBUNTU](./install/linux/UBUNTU.md)
- [macOS](./install/mac/MAC_OS.md)
- [Windows]()
- [CYGWIN](./install/windows/CYGWIN.md)
- [MSYS2](./install/windows/MSYS2.md)
- [WSL](./install/windows/WSL.md)
- [Run documentation site locally](local_mdbook/index.md)
- [Ubuntu WSL1/WSL2](local_mdbook/ubuntu_WSL.md)
- [Contributing](./CONTRIBUTING.md)
- [Styleguide and Principles](./STYLEGUIDE.md)
- [Credits](./CREDITS.md)
- [Tutorials]()
- [What are AI Flags?](tutorials/ai_flags.md)
- [How to add new AI Flags](tutorials/ai_logic.md)
- [How to add new battle script commands/macros](tutorials/how_to_battle_script_command_macro.md)
- [How to add a new move](tutorials/how_to_new_move.md)
- [How to add a new trainer class](tutorials/how_to_trainer_class.md)
- [How to add a new trainer class]()
- [How to add a new trainer front pic](tutorials/how_to_trainer_front_pic.md)
- [How to add a new trainer back pic](tutorials/how_to_trainer_back_pic.md)
- [How to add a new Pokémon](tutorials/how_to_new_pokemon.md)
- [v1.6.x and earlier](tutorials/how_to_new_pokemon_1_6_0.md)
- [How to use the Testing System](tutorials/how_to_testing_system.md)
- [How to add new Trainer Slides](tutorials/how_to_new_trainer_slide.md)
- [Day/Night System FAQ](tutorials/dns.md)
- [How to use the code entry system](tutorials/how_to_code_entry.md)
- [How to use Follower NPCs](tutorials/how_to_follower_npc.md)
- [Time-Based Encounters](tutorials/how_to_time_of_day_encounters.md)
- [How to use Trainer Party Pools](tutorials/how_to_trainer_party_pool.md)
- [Changelog](./CHANGELOG.md)
- [1.13.x]()
- [Version 1.13.2](changelogs/1.13.x/1.13.2.md)
@ -39,6 +58,7 @@
- [Version 1.10.2](changelogs/1.10.x/1.10.2.md)
- [Version 1.10.1](changelogs/1.10.x/1.10.1.md)
- [Version 1.10.0](changelogs/1.10.x/1.10.0.md)
- [Megaman Battle Network Style Names](./mmbn_style_names.md)
- [1.9.x]()
- [Version 1.9.4](changelogs/1.9.x/1.9.4.md)
- [Version 1.9.3](changelogs/1.9.x/1.9.3.md)
@ -87,4 +107,5 @@
- [Team Procedures]()
- [How to make an Expansion version](team_procedures/expansion_versions.md)
- [Release Schedule and Process](team_procedures/schedule.md)
- [Merge Checklist](team_procedures/merge_checklist.md)
- [Scope Guidelines](team_procedures/scope.md)

View File

@ -36,7 +36,6 @@ def proc_items(items):
s = s.replace('](README.md)', '](./)')
s = s.replace('](/INSTALL.md', '](INSTALL.md')
s = s.replace('](docs/', '](')
s = s.replace('](/docs/', '](/')
s = URL_RE.sub(handle_url, s)
item['Chapter']['content'] = ANCHOR_RE.sub(handle_anchor, s)
proc_items(item['Chapter']['sub_items'])

View File

@ -1,2 +1,2 @@
## Running documentation website locally
- [Ubuntu WSL1/WSL2](/docs/local_mdbook/ubuntu_WSL.md)
- [Ubuntu WSL1/WSL2](ubuntu_WSL.md)

View File

@ -7,7 +7,7 @@ This document is a guide for maintainers to account for all the reccomended step
# Checklist
## Is the branch's theoretical functionality in scope?
If you're not sure if a branch's functionality is [in scope](docs/team_procedures/scope.md), start a conversation on Discord to resolve.
If you're not sure if a branch's functionality is [in scope](scope.md), start a conversation on Discord to resolve.
## Does the branch successfully compile?
From `make clean`, the branch should locally compile.
@ -37,18 +37,18 @@ If you're not sure if something CAN be tested, start a discussion. Some contribu
If any new tests are `KNOWN_FAILING`, issues should be opened describing each of the `KNOWN_FAILING` tests and our understanding of why they fail.
## Does the branch meet our [config philosophy](/docs/STYLEGUIDE.md#config-philosophy)?
## Does the branch meet our [config philosophy](../STYLEGUIDE.md#config-philosophy)?
## Does the branch meet our [saves philosophy](/docs/STYLEGUIDE.md#saves-philosophy)?
## Does the branch meet our [saves philosophy](../STYLEGUIDE.md#saves-philosophy)?
## Does the submitted code follow the [styleguide](/docs/STYLEGUIDE.md)?
## Does the submitted code follow the [styleguide](../STYLEGUIDE.md)?
This applies to code that comes from other branches or games.
## Is the pull request appropriately labeled?
Without labels, the CHANGELOG will not be properly formatted. For specifically the `bugfix` label, an additional label, detailing what area the bug exists in is required.
## Is `pokeemerald-expansion` free from a merge freeze?
Our [release schedule](/docs/team_procedures/schedule.md) prevents us from merging Big Features and non-bugfixes within certain dates close to a release. Please use `/release` in the RHH Discord to clarify when these are occuring.
Our [release schedule](schedule.md) prevents us from merging Big Features and non-bugfixes within certain dates close to a release. Please use `/release` in the RHH Discord to clarify when these are occuring.
# Merging

View File

@ -53,4 +53,4 @@ This designation should be reserved for instances where an existing feature on `
Blocking issues or PRs can be deferred to future releases but should be discussed with the Maintainers that assigned the designation in the first place.
If a version's milestone does not have any issues or PRs assigned to it, that version should be [released](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/docs/team_procedures/expansion_versions.md) as close to the goal date as possible.
If a version's milestone does not have any issues or PRs assigned to it, that version should be [released](expansion_versions.md) as close to the goal date as possible.

View File

@ -23,8 +23,8 @@ When writing map scripts, `fadescreenswapbuffers` should be preferred over `fade
### Q: How do I make lightbulbs glow?
![Rustboro before adding lamp object events](/docs/tutorials/img/dns/without_lamp.png)
![Rustboro after adding lamp object events](/docs/tutorials/img/dns/with_lamp.png)
![Rustboro before adding lamp object events](img/dns/without_lamp.png)
![Rustboro after adding lamp object events](img/dns/with_lamp.png)
A: Making lamps glow is not part of the tileset itself. Instead, place certain object events on top of where you desire a glowing effect.
@ -48,7 +48,7 @@ on separate lines to mark those colors as being light-blended, i.e:
During the day time, these color indices appear as normal, but will be blended with either yellow or the 0 index at night. These indices should only be used for things you expect to light up. If you are using [porytiles](https://github.com/grunt-lucas/porytiles/wiki), palette overrides and using slight alterations to a color will aid you in avoiding color conflicts where the wrong index is assigned.
![Rustboro gym after light-blending the windows](/docs/tutorials/img/dns/window_lights.png)
![Rustboro gym after light-blending the windows](img/dns/window_lights.png)
The windows appear as normal during the day time (blue) and light up in the night. These use the default color.

View File

@ -2,7 +2,7 @@
*Written by Bivurnum*
*gif by ghoulslash*
![follower-npc](/docs/tutorials/img/follower_npc/follower-npc.gif)
![follower-npc](img/follower_npc/follower-npc.gif)
## Configs
The configs for follower NPCs can be found in [include/config/follower_npc.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/upcoming/include/config/follower_npc.h).

View File

@ -42,7 +42,7 @@ The main things that the Expansion changes are listed here.
# Useful resources
You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle.
![visualizer1](/docs/tutorials/img/add_pokemon/visualizer1.gif)
![visualizer1](img/add_pokemon/visualizer1.gif)
# The Data - Part 1
@ -73,7 +73,7 @@ We add this at the end so that no existing species change Id and so that we don'
Now, let's see how it looks in-game!
![visualizer2](/docs/tutorials/img/add_pokemon/visualizer2.png)
![visualizer2](img/add_pokemon/visualizer2.png)
Hmmm, something's not right...
@ -446,7 +446,7 @@ Now we can add the number and entry to our Mewthree:
},
};
```
![image](/docs/tutorials/img/add_pokemon/dex1.png)
![image](img/add_pokemon/dex1.png)
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
@ -497,7 +497,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera
...
};
```
![mGBA_lUBfmFEKUx](/docs/tutorials/img/add_pokemon/dex2.gif)
![mGBA_lUBfmFEKUx](img/add_pokemon/dex2.gif)
# The Graphics
@ -1058,7 +1058,7 @@ What this allows us to do is to be able to get all forms of a Pokémon in our co
For example, in the HGSS dex, it lets us browse between the entries of every form available.:
![hgssdex1](/docs/tutorials/img/add_pokemon/hgssdex1.png) ![image](/docs/tutorials/img/add_pokemon/hgssdex2.png)
![hgssdex1](img/add_pokemon/hgssdex1.png) ![image](img/add_pokemon/hgssdex2.png)
In addition, we have the `GET_BASE_SPECIES_ID` macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:
```c
@ -1089,7 +1089,7 @@ The second value is the target form, to which the Pokémon will change into.
Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in `include/constants/form_change_types.h`.
## 3. Gender differences
![gender_diffs](/docs/tutorials/img/add_pokemon/gender_diffs.gif)
![gender_diffs](img/add_pokemon/gender_diffs.gif)
You may have seen that there's a couple of duplicate fields with a "Female" suffix.
```diff
@ -1262,8 +1262,8 @@ Either way, you may also create custom animation tables and use them here approp
### How to add the Pokémon Object Events to map
In Porymap, select the object you want to set the sprite to. Then, change the field "Sprite" to use `OBJ_EVENT_GFX_SPECIES(SPECIES)`, replacing SPECIES with the name of the species you want to use. If you get a compiler error, it's because it used the species define as part of the macro, so it needs to match how you defined it all the way back in [Declare a species constant](#1-Declare-a-species-constant).
![charizard](/docs/tutorials/img/add_pokemon/charizard.png)
![overworld_data](/docs/tutorials/img/add_pokemon/overworld_data.gif)
![charizard](img/add_pokemon/charizard.png)
![overworld_data](img/add_pokemon/overworld_data.gif)
If you want to use their shiny and/or female versions, use one of the following macros:
- `OBJ_EVENT_GFX_SPECIES_SHINY(name)`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -405,7 +405,7 @@ $(RAYQUAZAGFXDIR)/scene_2/bg.4bpp: %.4bpp: %.png
$(GFX) $< $@ -num_tiles 313 -Wnum_tiles
$(RAYQUAZAGFXDIR)/scene_3/rayquaza.4bpp: %.4bpp: %.png
$(GFX) $< $@ -num_tiles 124 -Wnum_tiles
$(GFX) $< $@ -num_tiles 128 -Wnum_tiles
$(RAYQUAZAGFXDIR)/scene_4/streaks.4bpp: %.4bpp: %.png
$(GFX) $< $@ -num_tiles 19 -Wnum_tiles

View File

@ -720,7 +720,7 @@ struct BattleStruct
struct Illusion illusion[MAX_BATTLERS_COUNT];
u8 soulheartBattlerId;
u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles.
u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used.
u8 metronomeItemCounter[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used.
u8 quickClawBattlerId;
struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side)
u8 blunderPolicy:1; // should blunder policy activate
@ -781,6 +781,9 @@ struct BattleStruct
enum SubmoveState submoveAnnouncement:2;
u8 padding2:2;
u16 flingItem;
u8 incrementEchoedVoice:1;
u8 echoedVoiceCounter:3;
u8 padding3:4;
};
struct AiBattleData

View File

@ -158,7 +158,8 @@ struct DamageContext
u32 isCrit:1;
u32 randomFactor:1;
u32 updateFlags:1;
u32 padding1:2;
u32 isAnticipation:1;
u32 padding1:1;
u32 weather:16;
u32 fixedBasePower:8;
u32 padding2:8;
@ -251,7 +252,8 @@ bool32 HandleFaintedMonActions(void);
void TryClearRageAndFuryCutter(void);
enum MoveCanceller AtkCanceller_MoveSuccessOrder(struct BattleContext *ctx);
bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2);
bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility);
bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability);
bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag);
bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, u32 move, enum FunctionCallOption option);
bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, u32 move, u32 moveType, enum FunctionCallOption option);
u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, u32 special, u32 moveArg);
@ -320,6 +322,7 @@ u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pok
bool32 SetIllusionMon(struct Pokemon *mon, u32 battler);
u32 TryImmunityAbilityHealStatus(u32 battler, enum AbilityEffect caseID);
bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler);
uq4_12_t GetBadgeBoostModifier(void);
enum DamageCategory GetBattleMoveCategory(u32 move);
void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 CanFling(u32 battler);

View File

@ -19,7 +19,7 @@
#define B_LEVEL_UP_NOTIFICATION GEN_LATEST // In Gen9+, if the Pokémon gets enough experience to level up multiple times, the message is only displayed once.
// Stat settings
#define B_BADGE_BOOST GEN_LATEST // In Gen4+, Gym Badges no longer boost a Pokémon's stats.
#define B_BADGE_BOOST GEN_LATEST // In Gen4+, Gym Badges no longer boost a Pokémon's stats. (Gen2 does not include the additional boost to the type matching the gym the badge is from)
#define B_FRIENDSHIP_BOOST FALSE // In LGPE only, all stats except HP are boosted up to 10% based on Friendship. Unlike B_BADGE_BOOST, these boosts are accounted when calculating base stats.
#define B_MAX_LEVEL_EV_GAINS GEN_LATEST // In Gen5+, Lv100 Pokémon can obtain Effort Values normally.
#define B_RECALCULATE_STATS GEN_LATEST // In Gen5+, the stats of the Pokémon who participate in battle are recalculated at the end of each battle.

View File

@ -45,6 +45,7 @@ enum GenConfigTag
GEN_CONFIG_OBLIVIOUS_TAUNT,
GEN_CONFIG_TOXIC_NEVER_MISS,
GEN_CONFIG_PARALYZE_ELECTRIC,
GEN_CONFIG_BADGE_BOOST,
GEN_CONFIG_COUNT
};

View File

@ -48,6 +48,7 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_OBLIVIOUS_TAUNT] = B_OBLIVIOUS_TAUNT,
[GEN_CONFIG_TOXIC_NEVER_MISS] = B_TOXIC_NEVER_MISS,
[GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC,
[GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST
};
#if TESTING

View File

@ -793,6 +793,7 @@ u32 GetSpeciesBaseDefense(u16 species);
u32 GetSpeciesBaseSpAttack(u16 species);
u32 GetSpeciesBaseSpDefense(u16 species);
u32 GetSpeciesBaseSpeed(u16 species);
u32 GetSpeciesBaseStat(u16 species, u32 statIndex);
const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species);
const u16 *GetSpeciesTeachableLearnset(u16 species);
const u16 *GetSpeciesEggMoves(u16 species);

View File

@ -217,6 +217,7 @@ enum RandomTag
RNG_WRAP,
RNG_BALLTHROW_CRITICAL,
RNG_BALLTHROW_SHAKE,
RNG_PROTECT_FAIL,
RNG_PRESENT,
RNG_MAGNITUDE,
RNG_FISHING_BITE,

View File

@ -2428,5 +2428,7 @@ extern const u8 gText_Rename[]; // change nickname from summary screen
// Switch Caught Mon into Party
extern const u8 gText_CannotSendMonToBoxHM[];
extern const u8 gText_CannotSendMonToBoxActive[];
extern const u8 gText_CannotSendMonToBoxPartner[];
#endif // GUARD_STRINGS_H

View File

@ -5267,6 +5267,7 @@ case EFFECT_GUARD_SPLIT:
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
ADJUST_SCORE(WORST_EFFECT);
break;
}
case EFFECT_ELECTRIC_TERRAIN:
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN))

View File

@ -1725,6 +1725,14 @@ static void MoveSelectionDisplayMoveDescription(u32 battler)
u16 move = moveInfo->moves[gMoveSelectionCursor[battler]];
u16 pwr = GetMovePower(move);
u16 acc = GetMoveAccuracy(move);
enum DamageCategory cat = GetBattleMoveCategory(move);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))
{
pwr = GetMaxMovePower(move);
move = GetMaxMove(battler, move);
acc = 0;
}
u8 pwr_num[3], acc_num[3];
u8 cat_desc[7] = _("CAT: ");
@ -1758,7 +1766,7 @@ static void MoveSelectionDisplayMoveDescription(u32 battler)
if (gCategoryIconSpriteId == 0xFF)
gCategoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 38, 64, 1);
StartSpriteAnim(&gSprites[gCategoryIconSpriteId], GetBattleMoveCategory(move));
StartSpriteAnim(&gSprites[gCategoryIconSpriteId], cat);
CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_FULL);
}

View File

@ -173,6 +173,17 @@ static bool32 HandleEndTurnVarious(u32 battler)
gBattleStruct->hpBefore[i] = gBattleMons[i].hp;
}
if (gBattleStruct->incrementEchoedVoice)
{
if (gBattleStruct->echoedVoiceCounter < 4)
gBattleStruct->echoedVoiceCounter++;
gBattleStruct->incrementEchoedVoice = FALSE;
}
else
{
gBattleStruct->echoedVoiceCounter = 0;
}
return effect;
}
@ -764,9 +775,9 @@ static bool32 HandleEndTurnSaltCure(u32 battler)
{
s32 saltCureDamage = 0;
if (IS_BATTLER_ANY_TYPE(battler, TYPE_STEEL, TYPE_WATER))
saltCureDamage = gBattleMons[battler].maxHP / 4;
saltCureDamage = GetNonDynamaxMaxHP(battler) / 4;
else
saltCureDamage = gBattleMons[battler].maxHP / 8;
saltCureDamage = GetNonDynamaxMaxHP(battler) / 8;
SetPassiveDamageAmount(battler, saltCureDamage);
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE);
BattleScriptExecute(BattleScript_SaltCureExtraDamage);

View File

@ -2258,10 +2258,11 @@ static u8 CalcBarFilledPixels(s32 maxValue, s32 oldValue, s32 receivedValue, s32
for (i = 0; i < scale; i++)
pixelsArray[i] = 0;
// Safe Div, because 2vs1 battles can have maxValue 0.
if (maxValue < totalPixels)
pixels = (*currValue * totalPixels / maxValue) >> 8;
pixels = SAFE_DIV(*currValue * totalPixels, maxValue) >> 8;
else
pixels = *currValue * totalPixels / maxValue;
pixels = SAFE_DIV(*currValue * totalPixels, maxValue);
filledPixels = pixels;

View File

@ -3227,7 +3227,7 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy)
gLastHitBy[battler] = 0xFF;
gBattleStruct->lastTakenMove[battler] = 0;
gBattleStruct->sameMoveTurns[battler] = 0;
gBattleStruct->metronomeItemCounter[battler] = 0;
gBattleStruct->lastTakenMoveFrom[battler][0] = 0;
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
gBattleStruct->lastTakenMoveFrom[battler][2] = 0;
@ -3347,7 +3347,7 @@ const u8* FaintClearSetData(u32 battler)
gLastHitBy[battler] = 0xFF;
gBattleStruct->choicedMove[battler] = MOVE_NONE;
gBattleStruct->sameMoveTurns[battler] = 0;
gBattleStruct->metronomeItemCounter[battler] = 0;
gBattleStruct->lastTakenMove[battler] = MOVE_NONE;
gBattleStruct->lastTakenMoveFrom[battler][0] = 0;
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
@ -4798,7 +4798,7 @@ u32 GetBattlerTotalSpeedStat(u32 battler, enum Ability ability, enum HoldEffect
&& ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPEED, battler)
&& IsOnPlayerSide(battler))
{
speed = (speed * 110) / 100;
speed = uq4_12_multiply_by_int_half_down(GetBadgeBoostModifier(), speed);
}
// item effects

View File

@ -3789,7 +3789,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
}
break;
}
if (TryChangeBattleWeather(gBattlerAttacker, weather, FALSE))
if (TryChangeBattleWeather(gBattlerAttacker, weather, ABILITY_NONE))
{
gBattleCommunication[MULTISTRING_CHOOSER] = msg;
BattleScriptPush(gBattlescriptCurrInstr + 1);
@ -4747,7 +4747,15 @@ static void Cmd_getexp(void)
if (battler != 0xFF)
{
CopyMonLevelAndBaseStatsToBattleMon(battler, &gPlayerParty[*expMonId]);
if (gBattleMons[battler].volatiles.transformed)
{
gBattleMons[battler].level = GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL);
gBattleMons[battler].hp = GetMonData(&gPlayerParty[*expMonId], MON_DATA_HP);
}
else
{
CopyMonLevelAndBaseStatsToBattleMon(battler, &gPlayerParty[*expMonId]);
}
if (gBattleMons[battler].volatiles.powerTrick)
SWAP(gBattleMons[battler].attack, gBattleMons[battler].defense, temp);
}
@ -5817,6 +5825,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
}
break;
case EFFECT_EXPLOSION:
case EFFECT_MISTY_EXPLOSION:
if (!IsAbilityOnField(ABILITY_DAMP))
{
gBattleStruct->passiveHpUpdate[gBattlerAttacker] = 0;
@ -5845,7 +5854,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
BattleScriptCall(BattleScript_RapidSpinAway);
effect = TRUE;
@ -6906,9 +6915,9 @@ static void Cmd_moveend(void)
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker]
|| gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT
|| gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0;
gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0;
else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT)
gBattleStruct->sameMoveTurns[gBattlerAttacker]++;
gBattleStruct->metronomeItemCounter[gBattlerAttacker]++;
gBattleScripting.moveendState++;
break;
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
@ -6950,6 +6959,8 @@ static void Cmd_moveend(void)
gBattleMons[gBattlerAttacker].volatiles.charge = FALSE;
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
if (moveEffect == EFFECT_ECHOED_VOICE && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE))
gBattleStruct->incrementEchoedVoice = TRUE;
// check if Stellar type boost should be used up
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
@ -9575,7 +9586,7 @@ static void Cmd_setprotectlike(void)
if (gCurrentTurnActionNumber == (gBattlersCount - 1))
notLastTurn = FALSE;
if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn)
if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= RandomUniform(RNG_PROTECT_FAIL, 0, USHRT_MAX) && notLastTurn)
|| (protectMethod == PROTECT_WIDE_GUARD && B_WIDE_GUARD != GEN_5)
|| (protectMethod == PROTECT_QUICK_GUARD && B_QUICK_GUARD != GEN_5))
{
@ -9604,6 +9615,7 @@ static void Cmd_setprotectlike(void)
gDisableStructs[gBattlerAttacker].protectUses = 0;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECT_FAILED;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2;
}
gBattlescriptCurrInstr = cmd->nextInstr;
@ -9687,7 +9699,7 @@ static void Cmd_setfieldweather(void)
u8 battleWeatherId = cmd->weather;
if (!TryChangeBattleWeather(gBattlerAttacker, battleWeatherId, FALSE))
if (!TryChangeBattleWeather(gBattlerAttacker, battleWeatherId, ABILITY_NONE))
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
@ -11054,7 +11066,8 @@ static void Cmd_setfocusenergy(void)
}
else
{
if (GetGenConfig(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO) >= GEN_3)
if (GetGenConfig(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO) >= GEN_3
|| GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
gBattleMons[battler].volatiles.focusEnergy = TRUE;
else
gBattleMons[battler].volatiles.dragonCheer = TRUE;
@ -15037,12 +15050,13 @@ void BS_SetTerrain(void)
default:
break;
}
if (statusFlag)
if (gBattleStruct->isSkyBattle)
{
enum HoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker);
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses |= statusFlag;
gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else if (statusFlag)
{
TryChangeBattleTerrain(gBattlerAttacker, statusFlag);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
@ -17917,19 +17931,17 @@ void BS_JumpIfSpecies(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfLeafGuardProtected(void)
void BS_JumpIfAbilityPreventsRest(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (IsLeafGuardProtected(battler, GetBattlerAbility(battler)))
{
gBattlerAbility = battler;
u32 ability = GetBattlerAbility(battler);
if (B_LEAF_GUARD_PREVENTS_REST >= GEN_5 && IsLeafGuardProtected(battler, ability))
gBattlescriptCurrInstr = cmd->jumpInstr;
else if (IsShieldsDownProtected(battler, ability))
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_SetAttackerToStickyWebUser(void)

View File

@ -2519,7 +2519,7 @@ static enum MoveCanceller CancellerPPDeduction(struct BattleContext *ctx)
// For item Metronome, echoed voice
if (ctx->currentMove != gLastResultingMoves[ctx->battlerAtk] || WasUnableToUseMove(ctx->battlerAtk))
gBattleStruct->sameMoveTurns[ctx->battlerAtk] = 0;
gBattleStruct->metronomeItemCounter[ctx->battlerAtk] = 0;
if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct)
gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct;
@ -3162,24 +3162,24 @@ bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2
}
}
bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility)
bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability)
{
enum Ability battlerAbility = GetBattlerAbility(battler);
if (gBattleWeather & sBattleWeatherInfo[battleWeatherId].flag)
{
return FALSE;
}
else if (gBattleWeather & B_WEATHER_PRIMAL_ANY
&& battlerAbility != ABILITY_DESOLATE_LAND
&& battlerAbility != ABILITY_PRIMORDIAL_SEA
&& battlerAbility != ABILITY_DELTA_STREAM)
&& ability != ABILITY_DESOLATE_LAND
&& ability != ABILITY_PRIMORDIAL_SEA
&& ability != ABILITY_DELTA_STREAM)
{
return FALSE;
}
else if (GetGenConfig(GEN_CONFIG_ABILITY_WEATHER) < GEN_6 && viaAbility)
else if (GetGenConfig(GEN_CONFIG_ABILITY_WEATHER) < GEN_6 && ability != ABILITY_NONE)
{
gBattleWeather = sBattleWeatherInfo[battleWeatherId].flag;
for (u32 i = 0; i < gBattlersCount; i++)
gDisableStructs[i].weatherAbilityDone = FALSE;
return TRUE;
}
else
@ -3192,25 +3192,29 @@ bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbilit
gWishFutureKnock.weatherDuration = 8;
else
gWishFutureKnock.weatherDuration = 5;
for (u32 i = 0; i < gBattlersCount; i++)
gDisableStructs[i].weatherAbilityDone = FALSE;
return TRUE;
}
return FALSE;
}
static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u16 *timer)
bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag)
{
if ((!(gFieldStatuses & statusFlag) && (!gBattleStruct->isSkyBattle)))
if (gBattleStruct->isSkyBattle)
return FALSE;
if (!(gFieldStatuses & statusFlag))
{
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses |= statusFlag;
gDisableStructs[battler].terrainAbilityDone = FALSE;
for (u32 i = 0; i < gBattlersCount; i++)
gDisableStructs[i].terrainAbilityDone = FALSE;
if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_TERRAIN_EXTENDER)
*timer = gBattleTurnCounter + 8;
gFieldTimers.terrainTimer = gBattleTurnCounter + 8;
else
*timer = gBattleTurnCounter + 5;
gFieldTimers.terrainTimer = gBattleTurnCounter + 5;
gBattleScripting.battler = battler;
return TRUE;
}
@ -4013,8 +4017,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
case ABILITY_ANTICIPATION:
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
u32 types[3];
GetBattlerTypes(battler, FALSE, types);
struct DamageContext ctx = {0};
uq4_12_t modifier = UQ_4_12(1.0);
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (IsBattlerAlive(i) && !IsBattlerAlly(i, battler))
@ -4025,9 +4029,14 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
moveType = GetBattleMoveType(move);
if (GetTypeModifier(moveType, types[0]) >= UQ_4_12(2.0)
|| (types[0] != types[1] && GetTypeModifier(moveType, types[1]) >= UQ_4_12(2.0))
|| (types[2] != TYPE_MYSTERY && GetTypeModifier(moveType, types[2]) >= UQ_4_12(2.0))
ctx.battlerAtk = i;
ctx.battlerDef = battler;
ctx.move = move;
ctx.moveType = moveType;
ctx.isAnticipation = TRUE;
modifier = CalcTypeEffectivenessMultiplier(&ctx);
if (modifier >= UQ_4_12(2.0)
|| moveEffect == EFFECT_OHKO
|| moveEffect == EFFECT_SHEER_COLD)
{
@ -4158,7 +4167,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
}
break;
case ABILITY_DRIZZLE:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates);
effect++;
@ -4171,7 +4180,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
}
break;
case ABILITY_SAND_STREAM:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates);
effect++;
@ -4183,8 +4192,9 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
effect++;
}
break;
case ABILITY_ORICHALCUM_PULSE:
case ABILITY_DROUGHT:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates);
effect++;
@ -4197,12 +4207,12 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
}
break;
case ABILITY_SNOW_WARNING:
if (GetGenConfig(GEN_SNOW_WARNING) >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, TRUE))
if (GetGenConfig(GEN_SNOW_WARNING) >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow);
effect++;
}
else if (GetGenConfig(GEN_SNOW_WARNING) < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, TRUE))
else if (GetGenConfig(GEN_SNOW_WARNING) < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail);
effect++;
@ -4216,28 +4226,28 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
break;
case ABILITY_ELECTRIC_SURGE:
case ABILITY_HADRON_ENGINE:
if (TryChangeBattleTerrain(battler, STATUS_FIELD_ELECTRIC_TERRAIN, &gFieldTimers.terrainTimer))
if (TryChangeBattleTerrain(battler, STATUS_FIELD_ELECTRIC_TERRAIN))
{
BattleScriptPushCursorAndCallback(BattleScript_ElectricSurgeActivates);
effect++;
}
break;
case ABILITY_GRASSY_SURGE:
if (TryChangeBattleTerrain(battler, STATUS_FIELD_GRASSY_TERRAIN, &gFieldTimers.terrainTimer))
if (TryChangeBattleTerrain(battler, STATUS_FIELD_GRASSY_TERRAIN))
{
BattleScriptPushCursorAndCallback(BattleScript_GrassySurgeActivates);
effect++;
}
break;
case ABILITY_MISTY_SURGE:
if (TryChangeBattleTerrain(battler, STATUS_FIELD_MISTY_TERRAIN, &gFieldTimers.terrainTimer))
if (TryChangeBattleTerrain(battler, STATUS_FIELD_MISTY_TERRAIN))
{
BattleScriptPushCursorAndCallback(BattleScript_MistySurgeActivates);
effect++;
}
break;
case ABILITY_PSYCHIC_SURGE:
if (TryChangeBattleTerrain(battler, STATUS_FIELD_PSYCHIC_TERRAIN, &gFieldTimers.terrainTimer))
if (TryChangeBattleTerrain(battler, STATUS_FIELD_PSYCHIC_TERRAIN))
{
BattleScriptPushCursorAndCallback(BattleScript_PsychicSurgeActivates);
effect++;
@ -4338,21 +4348,21 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
}
break;
case ABILITY_DESOLATE_LAND:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN_PRIMAL, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN_PRIMAL, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_DesolateLandActivates);
effect++;
}
break;
case ABILITY_PRIMORDIAL_SEA:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN_PRIMAL, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN_PRIMAL, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_PrimordialSeaActivates);
effect++;
}
break;
case ABILITY_DELTA_STREAM:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_STRONG_WINDS, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_STRONG_WINDS, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_DeltaStreamActivates);
effect++;
@ -4398,13 +4408,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
effect++;
}
break;
case ABILITY_ORICHALCUM_PULSE:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, TRUE))
{
BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates);
effect++;
}
break;
case ABILITY_SUPREME_OVERLORD:
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
@ -5176,7 +5179,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
BattleScriptCall(BattleScript_BlockedByPrimalWeatherRet);
effect++;
}
else if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, TRUE))
else if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility))
{
gBattleScripting.battler = battler;
BattleScriptCall(BattleScript_SandSpitActivates);
@ -5230,7 +5233,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
if (!gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& TryChangeBattleTerrain(gBattlerTarget, STATUS_FIELD_GRASSY_TERRAIN, &gFieldTimers.terrainTimer))
&& TryChangeBattleTerrain(gBattlerTarget, STATUS_FIELD_GRASSY_TERRAIN))
{
BattleScriptCall(BattleScript_SeedSowerActivates);
effect++;
@ -5268,7 +5271,10 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
&& IsBattlerTurnDamaged(gBattlerTarget)
&& (gSideTimers[GetBattlerSide(gBattlerAttacker)].toxicSpikesAmount != 2))
{
SWAP(gBattlerAttacker, gBattlerTarget, i);
SaveBattlerTarget(gBattlerTarget);
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = gBattlerTarget;
gBattlerTarget = BATTLE_OPPOSITE(gBattlerAttacker);
BattleScriptCall(BattleScript_ToxicDebrisActivates);
effect++;
}
@ -6015,7 +6021,6 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abil
// Checks that apply to all non volatile statuses
if (abilityDef == ABILITY_COMATOSE
|| abilityDef == ABILITY_SHIELDS_DOWN
|| abilityDef == ABILITY_PURIFYING_SALT)
{
abilityAffected = TRUE;
@ -6030,6 +6035,11 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abil
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
else if (IsShieldsDownProtected(battlerDef, abilityDef))
{
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
else if ((sideBattler = IsFlowerVeilProtected(battlerDef)))
{
abilityAffected = TRUE;
@ -6528,11 +6538,11 @@ enum IronBallCheck
};
// Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics
bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum InverseBattleCheck checkInverse)
static bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum InverseBattleCheck checkInverse, bool32 isAnticipation)
{
if (holdEffect == HOLD_EFFECT_IRON_BALL)
return TRUE;
if (gFieldStatuses & STATUS_FIELD_GRAVITY)
if (gFieldStatuses & STATUS_FIELD_GRAVITY && isAnticipation == FALSE)
return TRUE;
if (B_ROOTED_GROUNDING >= GEN_4 && gBattleMons[battler].volatiles.root)
return TRUE;
@ -6553,7 +6563,7 @@ bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum Hol
bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum HoldEffect holdEffect)
{
return IsBattlerGroundedInverseCheck(battler, ability, holdEffect, NOT_INVERSE_BATTLE);
return IsBattlerGroundedInverseCheck(battler, ability, holdEffect, NOT_INVERSE_BATTLE, FALSE);
}
u32 GetMoveSlot(u16 *moves, u32 move)
@ -6953,10 +6963,10 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
break;
}
case EFFECT_ECHOED_VOICE:
// gBattleStruct->sameMoveTurns incremented in ppreduce
if (gBattleStruct->sameMoveTurns[battlerAtk] != 0 && GetMoveEffect(gLastResultingMoves[battlerAtk]) == EFFECT_ECHOED_VOICE)
// gBattleStruct->echoedVoiceCounter incremented in EndTurnVarious called by DoEndTurnEffects
if (gBattleStruct->echoedVoiceCounter != 0)
{
basePower += (basePower * gBattleStruct->sameMoveTurns[battlerAtk]);
basePower += (basePower * gBattleStruct->echoedVoiceCounter);
if (basePower > 200)
basePower = 200;
}
@ -7671,9 +7681,9 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
// The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battlerAtk) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1));
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPATK, battlerAtk) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1));
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
return uq4_12_multiply_by_int_half_down(modifier, atkStat);
}
@ -7851,11 +7861,11 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SNOW) && usesDefStat)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
// The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
// The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battlerDef) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1));
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPDEF, battlerDef) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1));
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
return uq4_12_multiply_by_int_half_down(modifier, defStat);
}
@ -8122,7 +8132,7 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff
{
case HOLD_EFFECT_METRONOME:
metronomeBoostBase = PercentToUQ4_12(GetBattlerHoldEffectParam(battlerAtk));
metronomeTurns = min(gBattleStruct->sameMoveTurns[battlerAtk], 5);
metronomeTurns = min(gBattleStruct->metronomeItemCounter[battlerAtk], 5);
// according to bulbapedia this is the "correct" way to calculate the metronome boost
// due to the limited domain of damage numbers it will never really matter whether this is off by one
return uq4_12_add(UQ_4_12(1.0), metronomeBoostBase * metronomeTurns);
@ -8445,7 +8455,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m
if (ctx->moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gBattleMons[ctx->battlerDef].volatiles.miracleEye && mod == UQ_4_12(0.0))
mod = UQ_4_12(1.0);
if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move))
if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move) && !ctx->isAnticipation)
mod = UQ_4_12(2.0);
if (ctx->moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(ctx->battlerDef, ctx->abilityDef, ctx->holdEffectDef) && mod == UQ_4_12(0.0))
mod = UQ_4_12(1.0);
@ -8453,7 +8463,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m
mod = UQ_4_12(2.0);
// B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon
if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect())
if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect() && !ctx->isAnticipation)
{
if (defType == TYPE_FLYING && mod >= UQ_4_12(2.0))
mod = UQ_4_12(1.0);
@ -8544,7 +8554,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageCont
if (B_GLARE_GHOST < GEN_4 && ctx->move == MOVE_GLARE && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GHOST))
modifier = UQ_4_12(0.0);
}
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, ctx->holdEffectDef, INVERSE_BATTLE) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, ctx->holdEffectDef, INVERSE_BATTLE, ctx->isAnticipation) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
{
modifier = UQ_4_12(0.0);
if (ctx->updateFlags && ctx->abilityDef == ABILITY_LEVITATE)
@ -8608,7 +8618,7 @@ uq4_12_t CalcTypeEffectivenessMultiplier(struct DamageContext *ctx)
if (ctx->move != MOVE_STRUGGLE && ctx->moveType != TYPE_MYSTERY)
{
modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier);
if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE)
if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE && !ctx->isAnticipation)
{
ctx->moveType = GetMoveArgType(ctx->move);
modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier);
@ -9347,9 +9357,17 @@ u32 TryImmunityAbilityHealStatus(u32 battler, enum AbilityEffect caseID)
return 0;
}
uq4_12_t GetBadgeBoostModifier(void)
{
if (GetGenConfig(GEN_CONFIG_BADGE_BOOST) < GEN_3)
return UQ_4_12(1.125);
else
return UQ_4_12(1.1);
}
bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler)
{
if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0)
if (GetGenConfig(GEN_CONFIG_BADGE_BOOST) <= GEN_3 && badgeFlag != 0)
{
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
return FALSE;
@ -10084,6 +10102,8 @@ void SetShellSideArmCategory(void)
statStage = gBattleMons[battlerDef].statStages[STAT_DEF];
targetDefStat *= gStatStageRatios[statStage][0];
targetDefStat /= gStatStageRatios[statStage][1];
if (targetDefStat == 0)
targetDefStat = 1;
physical = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * power * attackerAtkStat) / targetDefStat) / 50);
@ -10091,6 +10111,8 @@ void SetShellSideArmCategory(void)
statStage = gBattleMons[battlerDef].statStages[STAT_SPDEF];
targetSpDefStat *= gStatStageRatios[statStage][0];
targetSpDefStat /= gStatStageRatios[statStage][1];
if (targetSpDefStat == 0)
targetSpDefStat = 1;
special = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * power * attackerSpAtkStat) / targetSpDefStat) / 50);

View File

@ -9826,7 +9826,7 @@ const struct Item gItemsInfo[] =
"heals confusion\n"
"in battle."),
.pocket = POCKET_BERRIES,
.type = ITEM_USE_BAG_MENU,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.battleUsage = EFFECT_ITEM_CURE_STATUS,
.effect = gItemEffect_PersimBerry,

View File

@ -17103,6 +17103,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_SPECIAL,
.argument = { .damagePercentage = 50 },
.metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_8,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS,
.contestCategory = CONTEST_CATEGORY_CUTE,
@ -20171,6 +20172,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_SPECIAL,
.argument = { .damagePercentage = 50 },
.metronomeBanned = TRUE,
.battleAnimScript = gBattleAnimMove_Ruination,
},

View File

@ -925,6 +925,7 @@ static const struct FormChange sSilvallyFormChangeTable[] = {
#if P_FAMILY_MINIOR
static const struct FormChange sMiniorRedFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_RED},
@ -933,6 +934,7 @@ static const struct FormChange sMiniorRedFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorBlueFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_BLUE},
@ -941,6 +943,7 @@ static const struct FormChange sMiniorBlueFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorGreenFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_GREEN},
@ -949,6 +952,7 @@ static const struct FormChange sMiniorGreenFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorIndigoFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_INDIGO},
@ -957,6 +961,7 @@ static const struct FormChange sMiniorIndigoFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorOrangeFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_ORANGE},
@ -965,6 +970,7 @@ static const struct FormChange sMiniorOrangeFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorVioletFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_VIOLET},
@ -973,6 +979,7 @@ static const struct FormChange sMiniorVioletFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorYellowFormChangeTable[] = {
{FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_YELLOW},

View File

@ -1927,8 +1927,15 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati
x += MAP_OFFSET;
y += MAP_OFFSET;
SetSpritePosToOffsetMapCoords(&x, &y, 8, 16);
if (spriteTemplate.paletteTag != TAG_NONE)
if (spriteTemplate.paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC)
{
u32 paletteNum = LoadDynamicFollowerPaletteFromGraphicsId(graphicsId, &spriteTemplate);
spriteTemplate.paletteTag = GetSpritePaletteTagByPaletteNum(paletteNum);
}
else if (spriteTemplate.paletteTag != TAG_NONE)
{
LoadObjectEventPalette(spriteTemplate.paletteTag);
}
spriteId = CreateSpriteAtEnd(&spriteTemplate, x, y, 0);
if (spriteId != MAX_SPRITES)
@ -1942,6 +1949,9 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati
sprite->sVirtualObjId = virtualObjId;
sprite->sVirtualObjElev = elevation;
if (OW_GFX_COMPRESS && graphicsInfo->compressed)
spriteTemplate.tileTag = LoadSheetGraphicsInfo(graphicsInfo, graphicsId, sprite);
if (subspriteTables != NULL)
{
SetSubspriteTables(sprite, subspriteTables);
@ -6591,7 +6601,7 @@ bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent)
static u8 TryUpdateMovementActionOnStairs(struct ObjectEvent *objectEvent, u8 movementActionId)
{
if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER)
if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER || objectEvent->localId == OBJ_EVENT_ID_NPC_FOLLOWER)
return movementActionId; // handled separately
if (!ObjectMovingOnRockStairs(objectEvent, objectEvent->movementDirection))

View File

@ -229,7 +229,7 @@ ALIGNED(4) const u8 gFontSmallNarrowerLatinGlyphWidths[] = {
4, 5, 6, 7, 4, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 2, 4, 2,
4, 4, 4, 2, 2, 4, 4, 8, 2, 8, 5, 4, 4, 4, 4, 4,
4, 4, 4, 2, 2, 4, 4, 8, 2, 8, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 3, 4,
2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,

View File

@ -1719,8 +1719,7 @@ static void OverworldBasic(void)
|| bld0[1] != bld1[1]
|| bld0[2] != bld1[2])
{
UpdateAltBgPalettes(PALETTES_BG);
UpdatePalettesWithTime(PALETTES_ALL);
ApplyWeatherColorMapIfIdle(gWeatherPtr->colorMapIndex);
}
}
}
@ -1798,6 +1797,10 @@ void CB2_NewGame(void)
SetFieldVBlankCallback();
SetMainCallback1(CB1_Overworld);
SetMainCallback2(CB2_Overworld);
#if OW_USE_FAKE_RTC
// Wall clock now track local time so we set it to 10AM to match initial wall clock time
RtcCalcLocalTimeOffset(0, 10, 0, 0);
#endif
}
void CB2_WhiteOut(void)

View File

@ -273,6 +273,7 @@ static bool8 IsMonAllowedInMinigame(u8);
static void DisplayPartyPokemonDataToTeachMove(u8, u16);
static u8 CanTeachMove(struct Pokemon *, u16);
static void DisplayPartyPokemonBarDetail(u8, const u8 *, u8, const u8 *);
static void DisplayPartyPokemonBarDetailToFit(u8 windowId, const u8 *str, u8 color, const u8 *align, u32 width);
static void DisplayPartyPokemonLevel(u8, struct PartyMenuBox *);
static void DisplayPartyPokemonGender(u8, u16, u8 *, struct PartyMenuBox *);
static void DisplayPartyPokemonHP(u16 hp, u16 maxHp, struct PartyMenuBox *menuBox);
@ -1167,7 +1168,7 @@ static void DisplayPartyPokemonDataForMultiBattle(u8 slot)
StringCopy(gStringVar1, gMultiPartnerParty[actualSlot].nickname);
StringGet_Nickname(gStringVar1);
ConvertInternationalPlayerName(gStringVar1);
DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions);
DisplayPartyPokemonBarDetailToFit(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions, 50);
DisplayPartyPokemonLevel(gMultiPartnerParty[actualSlot].level, menuBox);
DisplayPartyPokemonGender(gMultiPartnerParty[actualSlot].gender, gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].nickname, menuBox);
DisplayPartyPokemonHP(gMultiPartnerParty[actualSlot].hp, gMultiPartnerParty[actualSlot].maxhp, menuBox);
@ -1511,11 +1512,21 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
case PARTY_ACTION_SEND_MON_TO_BOX:
{
u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr);
if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 2)
|| ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)))
if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1))
{
// Can't select if mon is currently on the field, or doesn't belong to you
// Can't select if mon is currently on the field
PlaySE(SE_FAILURE);
DisplayPartyMenuMessage(gText_CannotSendMonToBoxActive, FALSE);
ScheduleBgCopyTilemapToVram(2);
gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
}
else if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2))
{
// Can't select if mon doesn't belong to you
PlaySE(SE_FAILURE);
DisplayPartyMenuMessage(gText_CannotSendMonToBoxPartner, FALSE);
ScheduleBgCopyTilemapToVram(2);
gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
}
else if (DoesSelectedMonKnowHM((u8 *)slotPtr))
{

View File

@ -1692,32 +1692,10 @@ static u16 CalculateBoxMonChecksumReencrypt(struct BoxPokemon *boxMon)
return checksum;
}
#define CALC_STAT(base, iv, ev, statIndex, field) \
{ \
u8 baseStat = gSpeciesInfo[species].base; \
s32 n = (((2 * baseStat + iv + ev / 4) * level) / 100) + 5; \
n = ModifyStatByNature(nature, n, statIndex); \
if (B_FRIENDSHIP_BOOST == TRUE) \
n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100));\
SetMonData(mon, field, &n); \
}
void CalculateMonStats(struct Pokemon *mon)
{
s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL);
s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL);
s32 hpIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_HP_IV, NULL);
s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
s32 attackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_ATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_ATK_IV, NULL);
s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
s32 defenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_DEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_DEF_IV, NULL);
s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
s32 speedIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPEED) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPEED_IV, NULL);
s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
s32 spAttackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPATK_IV, NULL);
s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
s32 spDefenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPDEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
u8 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
s32 level = GetLevelFromMonExp(mon);
@ -1727,28 +1705,55 @@ void CalculateMonStats(struct Pokemon *mon)
SetMonData(mon, MON_DATA_LEVEL, &level);
bool32 hyperTrained[NUM_STATS]; //In a battle test, hyper training flag indicates a fixed stat
s32 iv[NUM_STATS];
s32 ev[NUM_STATS];
for (u32 i = 0; i < NUM_STATS; i++)
{
hyperTrained[i] = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + i);
iv[i] = GetMonData(mon, MON_DATA_HP_IV + i);
ev[i] = GetMonData(mon, MON_DATA_HP_EV + i);
if (hyperTrained[i])
{
#if TESTING
if (gMain.inBattle)
continue;
#endif
iv[i] = MAX_PER_STAT_IVS;
}
if (i == STAT_HP)
continue;
u8 baseStat = GetSpeciesBaseStat(species, i);
s32 n = (((2 * baseStat + iv[i] + ev[i] / 4) * level) / 100) + 5;
n = ModifyStatByNature(nature, n, i);
if (B_FRIENDSHIP_BOOST == TRUE)
n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100));
SetMonData(mon, MON_DATA_MAX_HP + i, &n);
}
#if TESTING
if (hyperTrained[STAT_HP] && gMain.inBattle)
return;
#endif
if (species == SPECIES_SHEDINJA)
{
newMaxHP = 1;
}
else
{
s32 n = 2 * GetSpeciesBaseHP(species) + hpIV;
newMaxHP = (((n + hpEV / 4) * level) / 100) + level + 10;
s32 n = 2 * GetSpeciesBaseHP(species) + iv[STAT_HP];
newMaxHP = (((n + ev[STAT_HP] / 4) * level) / 100) + level + 10;
}
gBattleScripting.levelUpHP = newMaxHP - oldMaxHP;
if (gBattleScripting.levelUpHP == 0)
gBattleScripting.levelUpHP = 1;
SetMonData(mon, MON_DATA_MAX_HP, &newMaxHP);
CALC_STAT(baseAttack, attackIV, attackEV, STAT_ATK, MON_DATA_ATK)
CALC_STAT(baseDefense, defenseIV, defenseEV, STAT_DEF, MON_DATA_DEF)
CALC_STAT(baseSpeed, speedIV, speedEV, STAT_SPEED, MON_DATA_SPEED)
CALC_STAT(baseSpAttack, spAttackIV, spAttackEV, STAT_SPATK, MON_DATA_SPATK)
CALC_STAT(baseSpDefense, spDefenseIV, spDefenseEV, STAT_SPDEF, MON_DATA_SPDEF)
// Since a pokemon's maxHP data could either not have
// been initialized at this point or this pokemon is
// just fainted, the check for oldMaxHP is important.
@ -3568,6 +3573,26 @@ u32 GetSpeciesBaseSpeed(u16 species)
return gSpeciesInfo[SanitizeSpeciesId(species)].baseSpeed;
}
u32 GetSpeciesBaseStat(u16 species, u32 statIndex)
{
switch (statIndex)
{
case STAT_HP:
return GetSpeciesBaseHP(species);
case STAT_ATK:
return GetSpeciesBaseAttack(species);
case STAT_DEF:
return GetSpeciesBaseDefense(species);
case STAT_SPEED:
return GetSpeciesBaseSpeed(species);
case STAT_SPATK:
return GetSpeciesBaseSpAttack(species);
case STAT_SPDEF:
return GetSpeciesBaseSpDefense(species);
}
return 0;
}
const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species)
{
const struct LevelUpMove *learnset = gSpeciesInfo[SanitizeSpeciesId(species)].levelUpLearnset;

View File

@ -585,7 +585,11 @@ static u32 LoopedTask_OpenRibbonsSummaryMenu(s32 state)
DecompressAndCopyTileDataToVram(1, sRibbonIconsSmall_Gfx, 0, 1, 0);
SetBgTilemapBuffer(1, menu->tilemapBuffers[1]);
FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 32, 20);
CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, BG_PLTT_ID(2), 5 * PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, BG_PLTT_ID(2), PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sRibbonIcons2_Pal, BG_PLTT_ID(3), PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sRibbonIcons3_Pal, BG_PLTT_ID(4), PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sRibbonIcons4_Pal, BG_PLTT_ID(5), PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sRibbonIcons5_Pal, BG_PLTT_ID(6), PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sMonInfo_Pal, BG_PLTT_ID(10), sizeof(sMonInfo_Pal));
CopyBgTilemapBufferToVram(1);
return LT_INC_AND_PAUSE;

View File

@ -348,6 +348,7 @@ void RtcCalcLocalTimeOffset(s32 days, s32 hours, s32 minutes, s32 seconds)
gLocalTime.hours = hours;
gLocalTime.minutes = minutes;
gLocalTime.seconds = seconds;
FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds);
RtcGetInfo(&sRtc);
RtcCalcTimeDifference(&sRtc, &gSaveBlock2Ptr->localTimeOffset, &gLocalTime);
}

View File

@ -3154,11 +3154,13 @@ bool8 Scrcmd_getobjectfacingdirection(struct ScriptContext *ctx)
return FALSE;
}
bool8 ScrFunc_hidefollower(struct ScriptContext *ctx)
bool8 ScrCmd_hidefollower(struct ScriptContext *ctx)
{
bool16 wait = VarGet(ScriptReadHalfword(ctx));
struct ObjectEvent *obj;
Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE);
if ((obj = ScriptHideFollower()) != NULL && wait)
{
sMovingNpcId = obj->localId;

View File

@ -1303,3 +1303,5 @@ const u8 gText_PM[] = _("PM");
const u8 gText_Relearn[] = _("{START_BUTTON} RELEARN"); // future note: don't decap this, because it mimics the summary screen BG graphics which will not get decapped
const u8 gText_Rename[] = _("RENAME");
const u8 gText_CannotSendMonToBoxHM[] = _("Cannot send that mon to the box,\nbecause it knows a HM move.{PAUSE_UNTIL_PRESS}");
const u8 gText_CannotSendMonToBoxActive[] = _("Cannot send an active battler\nto the box.{PAUSE_UNTIL_PRESS}");
const u8 gText_CannotSendMonToBoxPartner[] = _("Cannot send a mon that doesn't,\nbelong to you to the box.{PAUSE_UNTIL_PRESS}");

View File

@ -448,7 +448,7 @@ bool8 CheckForTrainersWantingBattle(void)
static u8 CheckTrainer(u8 objectEventId)
{
const u8 *scriptPtr, *trainerBattlePtr;
const u8 *trainerBattlePtr;
u8 numTrainers = 1;
u8 approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]);
@ -457,13 +457,13 @@ static u8 CheckTrainer(u8 objectEventId)
if (InTrainerHill() == TRUE)
{
trainerBattlePtr = scriptPtr = GetTrainerHillTrainerScript();
trainerBattlePtr = GetTrainerHillTrainerScript();
}
else
{
trainerBattlePtr = scriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);
trainerBattlePtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);
struct ScriptContext ctx;
if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, scriptPtr, &ctx))
if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, trainerBattlePtr, &ctx))
{
if (*ctx.scriptPtr == 0x5c) // trainerbattle
trainerBattlePtr = ctx.scriptPtr;
@ -511,7 +511,7 @@ static u8 CheckTrainer(u8 objectEventId)
}
gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = objectEventId;
gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = scriptPtr;
gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = trainerBattlePtr;
gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance;
InitTrainerApproachTask(&gObjectEvents[objectEventId], approachDistance - 1);
gNoOfApproachingTrainers++;
@ -970,13 +970,17 @@ u8 FldEff_HeartIcon(void)
return 0;
}
u8 FldEff_DoubleExclMarkIcon(void)
{
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53);
if (spriteId != MAX_SPRITES)
SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 2);
{
struct Sprite *sprite = &gSprites[spriteId];
SetIconSpriteData(sprite, FLDEFF_DOUBLE_EXCL_MARK_ICON, 2);
UpdateSpritePaletteByTemplate(&sSpriteTemplate_ExclamationQuestionMark, sprite);
}
return 0;
}
@ -986,7 +990,12 @@ u8 FldEff_XIcon(void)
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53);
if (spriteId != MAX_SPRITES)
SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 3);
{
struct Sprite *sprite = &gSprites[spriteId];
SetIconSpriteData(sprite, FLDEFF_X_ICON, 3);
UpdateSpritePaletteByTemplate(&sSpriteTemplate_ExclamationQuestionMark, sprite);
}
return 0;
}

View File

@ -692,13 +692,13 @@ void CB2_StartWallClock(void)
DecompressDataWithHeaderVram(gWallClockStart_Tilemap, (u16 *)BG_SCREEN_ADDR(7));
taskId = CreateTask(Task_SetClock_WaitFadeIn, 0);
gTasks[taskId].tHours = 10;
gTasks[taskId].tMinutes = 0;
gTasks[taskId].tHours = gLocalTime.hours;
gTasks[taskId].tMinutes = gLocalTime.minutes;
gTasks[taskId].tMoveDir = 0;
gTasks[taskId].tPeriod = 0;
gTasks[taskId].tPeriod = gTasks[taskId].tHours / 12;
gTasks[taskId].tMoveSpeed = 0;
gTasks[taskId].tMinuteHandAngle = 0;
gTasks[taskId].tHourHandAngle = 300;
gTasks[taskId].tMinuteHandAngle = gTasks[taskId].tMinutes * 6;
gTasks[taskId].tHourHandAngle = (gTasks[taskId].tHours % 12) * 30 + (gTasks[taskId].tMinutes / 10) * 5;
spriteId = CreateSprite(&sSpriteTemplate_MinuteHand, 120, 80, 1);
gSprites[spriteId].sTaskId = taskId;

View File

@ -16,6 +16,18 @@ SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a super-effe
}
}
SINGLE_BATTLE_TEST("Anticipation does not trigger even when a move is super effective on only 1 type")
{
GIVEN {
PLAYER(SPECIES_WHISCASH) { Ability(ABILITY_ANTICIPATION); }
OPPONENT(SPECIES_PIKACHU) { Moves(MOVE_CELEBRATE, MOVE_THUNDERBOLT); }
} WHEN {
TURN { }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION);
}
}
SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a One-hit KO move")
{
GIVEN {
@ -59,28 +71,21 @@ SINGLE_BATTLE_TEST("Anticipation doesn't consider Normalize into their effective
SINGLE_BATTLE_TEST("Anticipation doesn't consider Scrappy into their effectiveness (Gen5+)")
{
KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveType(MOVE_CLOSE_COMBAT) == TYPE_FIGHTING);
ASSUME(GetSpeciesType(SPECIES_EEVEE, 0) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_EEVEE, 1) == TYPE_NORMAL);
PLAYER(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); }
OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_TRICK_OR_TREAT, MOVE_SKILL_SWAP, MOVE_CELEBRATE); }
ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 0) == TYPE_STEEL);
ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 1) == TYPE_GHOST);
PLAYER(SPECIES_DOUBLADE) { Ability(ABILITY_ANTICIPATION); }
OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TRICK_OR_TREAT); MOVE(player, MOVE_SKILL_SWAP); }
TURN { MOVE(opponent, MOVE_SKILL_SWAP); }
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_ANTICIPATION);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK_OR_TREAT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent);
NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION);
}
}
SINGLE_BATTLE_TEST("Anticipation doesn't consider Gravity into their effectiveness (Gen5+)")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_SKARMORY);
OPPONENT(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); Moves(MOVE_EARTHQUAKE, MOVE_GRAVITY, MOVE_SCRATCH, MOVE_POUND); }

View File

@ -200,4 +200,23 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back when it uses a move that
}
}
DOUBLE_BATTLE_TEST("Flower Gift reverts Cherrim back after Teraform Zero clears weather")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_CHERRIM) { Ability(ABILITY_FLOWER_GIFT); }
OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_DROUGHT);
ABILITY_POPUP(playerRight, ABILITY_FLOWER_GIFT);
ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO);
ABILITY_POPUP(playerRight, ABILITY_FLOWER_GIFT);
} THEN {
EXPECT_EQ(playerRight->species, SPECIES_CHERRIM);
}
}
TO_DO_BATTLE_TEST("Flower Gift does not transform Cherrim back to normal when suppressed if Cherrim is Dynamaxed");

View File

@ -15,7 +15,7 @@ ASSUMPTIONS
ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP);
}
DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right target")
DOUBLE_BATTLE_TEST("Flower Veil prevents status on allied Grass-types - right target")
{
u32 move;
@ -39,7 +39,7 @@ DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right tar
}
}
DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - left target")
DOUBLE_BATTLE_TEST("Flower Veil prevents status on allied Grass-types - left target")
{
u32 move;

View File

@ -427,3 +427,22 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform when Cloud Nine ability user le
MESSAGE("Castform transformed!");
}
}
DOUBLE_BATTLE_TEST("Forecast reverts Castform back after Teraform Zero clears weather")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_CASTFORM) { Ability(ABILITY_FORECAST); }
OPPONENT(SPECIES_KYOGRE) { Ability(ABILITY_DRIZZLE); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_DRIZZLE);
ABILITY_POPUP(playerRight, ABILITY_FORECAST);
ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO);
ABILITY_POPUP(playerRight, ABILITY_FORECAST);
} THEN {
EXPECT_EQ(playerRight->species, SPECIES_CASTFORM_NORMAL);
}
}

View File

@ -201,3 +201,21 @@ SINGLE_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on
NOT ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
}
}
SINGLE_BATTLE_TEST("Protosynthesis activates after weather was reset")
{
GIVEN {
PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); }
TURN { MOVE(player, MOVE_RAIN_DANCE); }
TURN { MOVE(player, MOVE_SUNNY_DAY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
}
}

View File

@ -1,28 +1,30 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Minior Meteor transforms into Minior Core on switch-in if it has 1/2 or less health")
SINGLE_BATTLE_TEST("Minior Core doesn't transform into Minior Meteor on switch-in if it has 1/2 or less health")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); HP(1); }
OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(50); MaxHP(100); }
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
}
} THEN {
EXPECT_EQ(opponent->species, SPECIES_MINIOR_CORE);
}
}
SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it more then 1/2 health")
SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it has more than 1/2 health")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); }
OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(51); MaxHP(101); }
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); }
} SCENE {
@ -32,3 +34,44 @@ SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it
EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR);
}
}
SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on battle start if it has more than 1/2 health")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(51); MaxHP(101); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
} THEN {
EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR);
}
}
SINGLE_BATTLE_TEST("Shields Down protects Minior Meteor from status conditions")
{
u32 species, hp;
PARAMETRIZE { species = SPECIES_MINIOR_METEOR; hp = 300; }
PARAMETRIZE { species = SPECIES_MINIOR_CORE; hp = 100; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ABILITY_SHIELDS_DOWN); HP(hp); MaxHP(300); }
} WHEN {
TURN { MOVE(player, MOVE_WILL_O_WISP); }
} SCENE {
if (species == SPECIES_MINIOR_METEOR)
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player);
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player);
} THEN {
if (species == SPECIES_MINIOR_METEOR)
EXPECT_EQ(opponent->status1, STATUS1_NONE);
else
EXPECT(opponent->status1 & STATUS1_BURN);
}
}

View File

@ -120,3 +120,23 @@ SINGLE_BATTLE_TEST("Air Balloon is popped after Toxic Debris activates")
MESSAGE("Glimmora's Air Balloon popped!");
}
}
DOUBLE_BATTLE_TEST("Toxic Debris sets Toxic Spikes on the opposing side even when hit by an ally")
{
struct BattlePokemon *user = NULL;
PARAMETRIZE{ user = opponentLeft; }
PARAMETRIZE{ user = opponentRight; }
PARAMETRIZE{ user = playerRight; }
GIVEN {
PLAYER(SPECIES_GLIMMORA) { Ability(ABILITY_TOXIC_DEBRIS); }
PLAYER(SPECIES_WYNAUT) { }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT) { }
} WHEN {
TURN { MOVE(user, MOVE_SCRATCH, target: playerLeft); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_TOXIC_DEBRIS);
MESSAGE("Poison spikes were scattered on the ground all around the opposing team!");
}
}

166
test/battle/badge_boost.c Normal file
View File

@ -0,0 +1,166 @@
#include "global.h"
#include "event_data.h"
#include "test/battle.h"
WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_ATTACK boost Attack", s16 dmg)
{
u32 badge = 0;
u32 genConfig = 0;
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
PARAMETRIZE{badge = FALSE; genConfig = gen;}
PARAMETRIZE{badge = TRUE; genConfig = gen;}
}
GIVEN {
if (badge)
FlagSet(B_FLAG_BADGE_BOOST_ATTACK);
else
FlagClear(B_FLAG_BADGE_BOOST_ATTACK);
WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
PLAYER(SPECIES_WOBBUFFET) {}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].dmg);
} FINALLY {
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
if (gen <= GEN_3)
EXPECT_GT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
else
EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
}
}
}
WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPATK boost Special Attack", s16 dmg)
{
u32 badge = 0;
u32 genConfig = 0;
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
PARAMETRIZE{badge = FALSE; genConfig = gen;}
PARAMETRIZE{badge = TRUE; genConfig = gen;}
}
GIVEN {
if (badge)
FlagSet(B_FLAG_BADGE_BOOST_SPATK);
else
FlagClear(B_FLAG_BADGE_BOOST_SPATK);
WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
PLAYER(SPECIES_WOBBUFFET) {}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(player, MOVE_THUNDER_SHOCK); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].dmg);
} FINALLY {
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
if (gen <= GEN_3)
EXPECT_GT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
else
EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
}
}
}
WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_DEFENSE boost Defense", s16 dmg)
{
u32 badge = 0;
u32 genConfig = 0;
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
PARAMETRIZE{badge = FALSE; genConfig = gen;}
PARAMETRIZE{badge = TRUE; genConfig = gen;}
}
GIVEN {
if (badge)
FlagSet(B_FLAG_BADGE_BOOST_DEFENSE);
else
FlagClear(B_FLAG_BADGE_BOOST_DEFENSE);
WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
PLAYER(SPECIES_WOBBUFFET) {}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].dmg);
} FINALLY {
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
if (gen <= GEN_3)
EXPECT_LT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
else
EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
}
}
}
WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPDEF boost Special Defense", s16 dmg)
{
u32 badge = 0;
u32 genConfig = 0;
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
PARAMETRIZE{badge = FALSE; genConfig = gen;}
PARAMETRIZE{badge = TRUE; genConfig = gen;}
}
GIVEN {
if (badge)
FlagSet(B_FLAG_BADGE_BOOST_SPDEF);
else
FlagClear(B_FLAG_BADGE_BOOST_SPDEF);
WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
PLAYER(SPECIES_WOBBUFFET) {}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(opponent, MOVE_THUNDER_SHOCK); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].dmg);
} FINALLY {
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
if (gen <= GEN_3)
EXPECT_LT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
else
EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
}
}
}
WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPEED boost Speed", s16 dmg)
{
u32 badge = 0;
u32 genConfig = 0;
for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
{
PARAMETRIZE{badge = FALSE; genConfig = gen;}
PARAMETRIZE{badge = TRUE; genConfig = gen;}
}
GIVEN {
if (badge)
FlagSet(B_FLAG_BADGE_BOOST_SPEED);
else
FlagClear(B_FLAG_BADGE_BOOST_SPEED);
WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
PLAYER(SPECIES_WOBBUFFET) { Speed(100); HP(1); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(101); HP(1); }
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SCRATCH);}
} THEN {
if (badge && genConfig <= GEN_3)
{
EXPECT_EQ(opponent->hp, 0);
EXPECT_EQ(player->hp, 1);
}
else
{
EXPECT_EQ(opponent->hp, 1);
EXPECT_EQ(player->hp, 0);
}
}
}

View File

@ -75,10 +75,10 @@ SINGLE_BATTLE_TEST("Mega Evolution doesn't affect turn order (Gen6)")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_6);
PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); }
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("The opposing Wobbuffet used Celebrate!");
MESSAGE("Gardevoir used Celebrate!");
@ -91,10 +91,10 @@ SINGLE_BATTLE_TEST("Mega Evolution affects turn order (Gen7+)")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_7);
PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE);}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Gardevoir used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
@ -117,7 +117,7 @@ SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn orde
MESSAGE("Sableye used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
} THEN {
ASSUME(player->speed == 45);
ASSUME(player->speed == 105);
}
}

View File

@ -59,10 +59,10 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_7);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z);}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
} SCENE {
MESSAGE("Necrozma used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");

View File

@ -1,7 +1,169 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5");
TO_DO_BATTLE_TEST("Echoed Voice's power is reset when using a different move");
TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it misses");
TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect");
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_ECHOED_VOICE) == EFFECT_ECHOED_VOICE);
}
SINGLE_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5")
{
s16 damage[6];
GIVEN {
ASSUME(GetMoveEffect(MOVE_SOFT_BOILED) == EFFECT_SOFTBOILED);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BLISSEY);
} WHEN {
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[5]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(3.0), damage[2]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[3]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(5.0), damage[4]);
EXPECT_EQ(damage[4], damage[5]);
}
}
SINGLE_BATTLE_TEST("Echoed Voice's power increases even if used by another battler")
{
s16 damage[2];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent);
HP_BAR(player, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
}
}
SINGLE_BATTLE_TEST("Echoed Voice's power does not change until the end of the turn")
{
s16 damage[3];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent);
HP_BAR(player, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[2]);
} THEN {
EXPECT_EQ(damage[0], damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[2]);
}
}
SINGLE_BATTLE_TEST("Echoed Voice's power increase is reset when no battler uses it successfully during a turn")
{
s16 damage[3];
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_BITE, MOVE_EFFECT_FLINCH));
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
} WHEN {
TURN { MOVE(opponent, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(opponent, MOVE_BITE); MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent);
HP_BAR(player, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent);
MESSAGE("Wobbuffet flinched and couldn't move!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[2]);
} THEN {
EXPECT_EQ(damage[0], damage[2]);
EXPECT_NE(damage[1], damage[2]);
}
}
SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it misses")
{
s16 damage[3];
GIVEN {
ASSUME(GetMoveEffect(MOVE_SAND_ATTACK) == EFFECT_ACCURACY_DOWN);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SAND_ATTACK); }
TURN { MOVE(player, MOVE_ECHOED_VOICE, hit: FALSE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[1]);
MESSAGE("Wobbuffet's attack missed!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[2]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]);
}
}
SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect")
{
s16 damage[3];
GIVEN {
ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_PROTECT); }
TURN { MOVE(player, MOVE_ECHOED_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
HP_BAR(opponent, captureDamage: &damage[2]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]);
}
}

View File

@ -23,7 +23,34 @@ SINGLE_BATTLE_TEST("Focus Energy increases the user's critical hit ratio by 1 st
}
PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT);
GIVEN {
WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig);
WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, (genConfig == GEN_1)? GEN_2 : genConfig);
WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig);
ASSUME(GetSpeciesBaseSpeed(SPECIES_WOBBUFFET) == 33);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (useFocusEnergy)
TURN { MOVE(player, MOVE_FOCUS_ENERGY); }
TURN { MOVE(player, MOVE_SCRATCH); }
} SCENE {
if (useFocusEnergy)
ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
MESSAGE("A critical hit!");
}
}
SINGLE_BATTLE_TEST("Focus Energy multiplies crit chance by 4 with gen 1 crit chance")
{
bool32 useFocusEnergy = 0;
u32 genConfig = 0, chance = 0;
for (u32 j = GEN_1; j <= GEN_9; j++) {
PARAMETRIZE { genConfig = j; useFocusEnergy = FALSE; chance = 16;}
PARAMETRIZE { genConfig = j; useFocusEnergy = TRUE; chance = 4;}
}
PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT);
GIVEN {
WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, GEN_1);
WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig);
ASSUME(GetSpeciesBaseSpeed(SPECIES_WOBBUFFET) == 33);
PLAYER(SPECIES_WOBBUFFET);

View File

@ -114,6 +114,38 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves")
}
}
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves against semi-invulnerable targets")
{
u32 move = 0, shouldWork = 0;
PARAMETRIZE { move = MOVE_SOLAR_BEAM; shouldWork = FALSE;}
PARAMETRIZE { move = MOVE_FLY; shouldWork = TRUE;}
GIVEN {
PLAYER(SPECIES_SHROODLE) { Ability(ABILITY_PRANKSTER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); MOVE(opponent,move);}
TURN { MOVE(player, MOVE_TOXIC); SKIP_TURN(opponent);}
} SCENE {
if (shouldWork)
{
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
}
else
{
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
}
}
} THEN {
if (shouldWork)
EXPECT(opponent->status1 & STATUS1_TOXIC_POISON);
else
EXPECT(!(opponent->status1 & STATUS1_TOXIC_POISON));
}
}
SINGLE_BATTLE_TEST("Psychic Terrain lasts for 5 turns")
{
GIVEN {

View File

@ -1,4 +1,121 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
}
SINGLE_BATTLE_TEST("Rest causes the user to fall asleep and restores HP to full")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(player->status1 & STATUS1_SLEEP);
EXPECT_EQ(player->hp, player->maxHP);
}
}
SINGLE_BATTLE_TEST("Rest fails if the user is at full HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REST); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(!(player->status1 & STATUS1_SLEEP));
}
}
SINGLE_BATTLE_TEST("Rest fails if the user is protected by Leaf Guard")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_SUNNY_DAY) == EFFECT_SUNNY_DAY);
ASSUME(B_LEAF_GUARD_PREVENTS_REST >= GEN_5);
PLAYER(SPECIES_CHIKORITA) { Ability(ABILITY_LEAF_GUARD); HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(!(player->status1 & STATUS1_SLEEP));
}
}
SINGLE_BATTLE_TEST("Rest fails if the user is protected by Shields Down")
{
GIVEN {
PLAYER(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); HP(299); MaxHP(300); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REST); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(!(player->status1 & STATUS1_SLEEP));
}
}
SINGLE_BATTLE_TEST("Rest fails if the user is protected by Electric/Misty Terrain")
{
u32 move;
PARAMETRIZE { move = MOVE_ELECTRIC_TERRAIN; }
PARAMETRIZE { move = MOVE_MISTY_TERRAIN; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_ELECTRIC_TERRAIN) == EFFECT_ELECTRIC_TERRAIN);
ASSUME(GetMoveEffect(MOVE_MISTY_TERRAIN) == EFFECT_MISTY_TERRAIN);
ASSUME(GetSpeciesType(SPECIES_WYNAUT, 0) != TYPE_FLYING && GetSpeciesType(SPECIES_WYNAUT, 1) != TYPE_FLYING);
PLAYER(SPECIES_WYNAUT) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_REST); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(!(player->status1 & STATUS1_SLEEP));
}
}
SINGLE_BATTLE_TEST("Rest doesn't fail if the user is protected by Safeguard")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_SAFEGUARD) == EFFECT_SAFEGUARD);
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SAFEGUARD); }
TURN { MOVE(player, MOVE_REST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(player->status1 & STATUS1_SLEEP);
}
}
DOUBLE_BATTLE_TEST("Rest doesn't fail if the user is protected by Flower Veil")
{
GIVEN {
ASSUME(GetSpeciesType(SPECIES_CHIKORITA, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_CHIKORITA, 1) == TYPE_GRASS);
PLAYER(SPECIES_CHIKORITA) { HP(1); }
PLAYER(SPECIES_FLORGES) { Ability(ABILITY_FLOWER_VEIL); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_REST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, playerLeft);
} THEN {
EXPECT(playerLeft->status1 & STATUS1_SLEEP);
}
}
TO_DO_BATTLE_TEST("TODO: Write Rest (Move Effect) test titles")

View File

@ -90,6 +90,31 @@ SINGLE_BATTLE_TEST("Stomping Tantrum will not deal double damage if target prote
}
}
SINGLE_BATTLE_TEST("Stomping Tantrum will deal double damage if user failed a Protect")
{
s16 damage[2];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
TURN { MOVE(player, MOVE_PROTECT); }
TURN { MOVE(player, MOVE_PROTECT, WITH_RNG(RNG_PROTECT_FAIL, USHRT_MAX)); }
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
HP_BAR(opponent, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
}
}
SINGLE_BATTLE_TEST("Stomping Tantrum will not deal double if it missed")
{
s16 damage[2];

View File

@ -25,3 +25,58 @@ SINGLE_BATTLE_TEST("Forced abilities activate on switch-in")
MESSAGE("Kadabra's Sp. Atk was heightened!");
}
}
SINGLE_BATTLE_TEST("Setting level doesn't overwrite set stats")
{
u32 level = 0;
PARAMETRIZE{level = 1;}
PARAMETRIZE{level = 10;}
PARAMETRIZE{level = 50;}
PARAMETRIZE{level = 99;}
GIVEN {
PLAYER(SPECIES_WOBBUFFET) {HP(5); MaxHP(10); Attack(10); Defense(10); Speed(10); SpAttack(10); SpDefense(10); Level(level); };
OPPONENT(SPECIES_WOBBUFFET) {Speed(1);}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE);}
} THEN {
EXPECT_EQ(player->hp, 5);
EXPECT_EQ(player->maxHP, 10);
EXPECT_EQ(player->attack, 10);
EXPECT_EQ(player->defense, 10);
EXPECT_EQ(player->speed, 10);
EXPECT_EQ(player->spAttack, 10);
EXPECT_EQ(player->spDefense, 10);
}
}
SINGLE_BATTLE_TEST("Changing forms doesn't overwrite set stats (not HP)")
{
GIVEN {
PLAYER(SPECIES_DIANCIE) {Attack(10); Defense(10); Speed(10); SpAttack(10); SpDefense(10); Item(ITEM_DIANCITE);}
OPPONENT(SPECIES_WOBBUFFET) {Speed(1);}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
} THEN {
EXPECT_EQ(player->attack, 10);
EXPECT_EQ(player->defense, 10);
EXPECT_EQ(player->speed, 10);
EXPECT_EQ(player->spAttack, 10);
EXPECT_EQ(player->spDefense, 10);
}
}
SINGLE_BATTLE_TEST("Changing forms doesn't overwrite set stats (HP)")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS) {HP(5); MaxHP(10); TeraType(TYPE_STELLAR);}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CELEBRATE);}
} THEN {
EXPECT_EQ(player->hp, 5);
EXPECT_EQ(player->maxHP, 10);
}
}

View File

@ -477,6 +477,24 @@ TEST("Optimised SetMonData")
EXPECT_FASTER(optimised, vanilla);
}
//Sanity check for a CalculateMonStats refactor (could be deleted or improved)
TEST("CalculateMonStats")
{
ZeroPlayerPartyMons();
RUN_OVERWORLD_SCRIPT(
givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, shinyMode=SHINY_MODE_ALWAYS, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7;
);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MAX_HP), 497);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK), 71);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF), 143);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED), 82);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK), 83);
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF), 134);
}
TEST("BoxPokemon encryption works")
{
u32 raw[20] =

35
test/save.c Normal file
View File

@ -0,0 +1,35 @@
#include "global.h"
#include "pokemon_storage_system.h"
#include "test/test.h"
// If you would like to ensure save compatibility, update the values below with those for your hack. You can find these through the debug menu.
// Please note that this simple check is not 100% foolproof, but should be able to catch most unintended shifts.
#define T_SAVEBLOCK1_SIZE 15568
#define T_SAVEBLOCK2_SIZE 3884
#define T_SAVEBLOCK3_SIZE 4
#define T_POKEMONSTORAGE_SIZE 34144
TEST("SaveBlock1 is backwards compatible")
{
EXPECT_EQ(sizeof(struct SaveBlock1), T_SAVEBLOCK1_SIZE);
}
TEST("SaveBlock2 is backwards compatible")
{
EXPECT_EQ(sizeof(struct SaveBlock2), T_SAVEBLOCK2_SIZE);
}
TEST("SaveBlock3 is backwards compatible")
{
EXPECT_EQ(sizeof(struct SaveBlock3), T_SAVEBLOCK3_SIZE);
}
TEST("PokemonStorage is backwards compatible")
{
EXPECT_EQ(sizeof(struct PokemonStorage), T_POKEMONSTORAGE_SIZE);
}
#undef T_SAVEBLOCK1_SIZE
#undef T_SAVEBLOCK2_SIZE
#undef T_SAVEBLOCK3_SIZE
#undef T_POKEMONSTORAGE_SIZE

View File

@ -1969,7 +1969,9 @@ void Level_(u32 sourceLine, u32 level)
INVALID_IF(level == 0 || level > MAX_LEVEL, "Illegal level: %d", level);
SetMonData(DATA.currentMon, MON_DATA_LEVEL, &level);
SetMonData(DATA.currentMon, MON_DATA_EXP, &gExperienceTables[gSpeciesInfo[species].growthRate][level]);
gMain.inBattle = TRUE;
CalculateMonStats(DATA.currentMon);
gMain.inBattle = FALSE;
}
void MaxHP_(u32 sourceLine, u32 maxHP)
@ -1977,6 +1979,8 @@ void MaxHP_(u32 sourceLine, u32 maxHP)
INVALID_IF(!DATA.currentMon, "MaxHP outside of PLAYER/OPPONENT");
INVALID_IF(maxHP == 0, "Illegal max HP: %d", maxHP);
SetMonData(DATA.currentMon, MON_DATA_MAX_HP, &maxHP);
bool32 hyperTrainingFlag = TRUE;
SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_HP, &hyperTrainingFlag);
}
void HP_(u32 sourceLine, u32 hp)
@ -1992,6 +1996,8 @@ void Attack_(u32 sourceLine, u32 attack)
INVALID_IF(!DATA.currentMon, "Attack outside of PLAYER/OPPONENT");
INVALID_IF(attack == 0, "Illegal attack: %d", attack);
SetMonData(DATA.currentMon, MON_DATA_ATK, &attack);
bool32 hyperTrainingFlag = TRUE;
SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_ATK, &hyperTrainingFlag);
}
void Defense_(u32 sourceLine, u32 defense)
@ -1999,6 +2005,8 @@ void Defense_(u32 sourceLine, u32 defense)
INVALID_IF(!DATA.currentMon, "Defense outside of PLAYER/OPPONENT");
INVALID_IF(defense == 0, "Illegal defense: %d", defense);
SetMonData(DATA.currentMon, MON_DATA_DEF, &defense);
bool32 hyperTrainingFlag = TRUE;
SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_DEF, &hyperTrainingFlag);
}
void SpAttack_(u32 sourceLine, u32 spAttack)
@ -2006,6 +2014,8 @@ void SpAttack_(u32 sourceLine, u32 spAttack)
INVALID_IF(!DATA.currentMon, "SpAttack outside of PLAYER/OPPONENT");
INVALID_IF(spAttack == 0, "Illegal special attack: %d", spAttack);
SetMonData(DATA.currentMon, MON_DATA_SPATK, &spAttack);
bool32 hyperTrainingFlag = TRUE;
SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPATK, &hyperTrainingFlag);
}
void SpDefense_(u32 sourceLine, u32 spDefense)
@ -2013,6 +2023,8 @@ void SpDefense_(u32 sourceLine, u32 spDefense)
INVALID_IF(!DATA.currentMon, "SpDefense outside of PLAYER/OPPONENT");
INVALID_IF(spDefense == 0, "Illegal special defense: %d", spDefense);
SetMonData(DATA.currentMon, MON_DATA_SPDEF, &spDefense);
bool32 hyperTrainingFlag = TRUE;
SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPDEF, &hyperTrainingFlag);
}
void Speed_(u32 sourceLine, u32 speed)
@ -2020,6 +2032,8 @@ void Speed_(u32 sourceLine, u32 speed)
INVALID_IF(!DATA.currentMon, "Speed outside of PLAYER/OPPONENT");
INVALID_IF(speed == 0, "Illegal speed: %d", speed);
SetMonData(DATA.currentMon, MON_DATA_SPEED, &speed);
bool32 hyperTrainingFlag = TRUE;
SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPEED, &hyperTrainingFlag);
DATA.hasExplicitSpeeds = TRUE;
DATA.explicitSpeeds[DATA.currentPosition] |= 1 << DATA.currentPartyIndex;
}