25/10/25 Master to upcoming merge (#8041)
This commit is contained in:
commit
776a2e762b
@ -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,
|
||||
|
||||
13
CREDITS.md
13
CREDITS.md
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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::
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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::
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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'])
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
## Running documentation website locally
|
||||
- [Ubuntu WSL1/WSL2](/docs/local_mdbook/ubuntu_WSL.md)
|
||||
- [Ubuntu WSL1/WSL2](ubuntu_WSL.md)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -23,8 +23,8 @@ When writing map scripts, `fadescreenswapbuffers` should be preferred over `fade
|
||||
|
||||
### Q: How do I make lightbulbs glow?
|
||||
|
||||

|
||||

|
||||

|
||||

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

|
||||

|
||||
|
||||
The windows appear as normal during the day time (blue) and light up in the night. These use the default color.
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
*Written by Bivurnum*
|
||||
*gif by ghoulslash*
|
||||
|
||||

|
||||

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

|
||||

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

|
||||

|
||||
|
||||
Hmmm, something's not right...
|
||||
|
||||
@ -446,7 +446,7 @@ Now we can add the number and entry to our Mewthree:
|
||||
},
|
||||
};
|
||||
```
|
||||

|
||||

|
||||
|
||||
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
|
||||
...
|
||||
};
|
||||
```
|
||||

|
||||

|
||||
|
||||
|
||||
# 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.:
|
||||
|
||||
 
|
||||
 
|
||||
|
||||
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
|
||||

|
||||

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

|
||||

|
||||

|
||||

|
||||
|
||||
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 |
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -217,6 +217,7 @@ enum RandomTag
|
||||
RNG_WRAP,
|
||||
RNG_BALLTHROW_CRITICAL,
|
||||
RNG_BALLTHROW_SHAKE,
|
||||
RNG_PROTECT_FAIL,
|
||||
RNG_PRESENT,
|
||||
RNG_MAGNITUDE,
|
||||
RNG_FISHING_BITE,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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}");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
166
test/battle/badge_boost.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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!");
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
35
test/save.c
Normal 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user