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

This commit is contained in:
RoamerX 2025-11-10 12:09:54 +08:00
commit c262995dd7
194 changed files with 3668 additions and 1036 deletions

View File

@ -357,6 +357,108 @@
"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"
]
},
{
"login": "MandL27",
"name": "MandL27",
"avatar_url": "https://avatars.githubusercontent.com/u/10366615?v=4",
"profile": "https://github.com/MandL27",
"contributions": [
"code"
]
},
{
"login": "cawtds",
"name": "cawtds",
"avatar_url": "https://avatars.githubusercontent.com/u/38510667?v=4",
"profile": "https://github.com/cawtds",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@ -43,9 +43,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using?
options:
- 1.13.2 (Latest release)
- 1.13.3 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
- 1.13.2
- 1.13.1
- 1.13.0
- 1.12.3

View File

@ -43,9 +43,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using?
options:
- 1.13.2 (Latest release)
- 1.13.3 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
- 1.13.2
- 1.13.1
- 1.13.0
- 1.12.3

View File

@ -43,9 +43,10 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using?
options:
- 1.13.2 (Latest release)
- 1.13.3 (Latest release)
- master (default, unreleased bugfixes)
- upcoming (Edge)
- 1.13.2
- 1.13.1
- 1.13.0
- 1.12.3

View File

@ -17,7 +17,7 @@ jobs:
fetch-depth: 0
- name: Install latest mdbook
run: |
tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name')
tag="v0.5.0-beta.1"
url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz"
mkdir mdbook
curl -sSL $url | tar -xz --directory=./mdbook

View File

@ -3,10 +3,12 @@ name: Labels
on:
pull_request:
types: [opened, synchronize, labeled, unlabeled]
pull_request_review:
types: [submitted]
jobs:
label:
if: github.actor != 'allcontributors[bot]'
if: ${{ github.actor != 'allcontributors[bot]' && github.event.review.state == 'approved' }}
runs-on: ubuntu-latest
steps:
- name: check labels

View File

@ -62,6 +62,21 @@ 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>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MandL27"><img src="https://avatars.githubusercontent.com/u/10366615?v=4?s=100" width="100px;" alt="MandL27"/><br /><sub><b>MandL27</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=MandL27" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cawtds"><img src="https://avatars.githubusercontent.com/u/38510667?v=4?s=100" width="100px;" alt="cawtds"/><br /><sub><b>cawtds</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=cawtds" title="Code">💻</a></td>
</tr>
</tbody>
<tfoot>
<tr>

View File

@ -197,10 +197,18 @@ ALL_LEARNABLES_JSON := $(LEARNSET_HELPERS_BUILD_DIR)/all_learnables.json
WILD_ENCOUNTERS_TOOL_DIR := $(TOOLS_DIR)/wild_encounters
AUTO_GEN_TARGETS += $(DATA_SRC_SUBDIR)/wild_encounters.h
MISC_TOOL_DIR := $(TOOLS_DIR)/misc
AUTO_GEN_TARGETS += $(INCLUDE_DIRS)/constants/script_commands.h
$(DATA_SRC_SUBDIR)/wild_encounters.h: $(DATA_SRC_SUBDIR)/wild_encounters.json $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py $(INCLUDE_DIRS)/config/overworld.h $(INCLUDE_DIRS)/config/dexnav.h
python3 $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py > $@
$(INCLUDE_DIRS)/constants/script_commands.h: $(MISC_TOOL_DIR)/make_scr_cmd_constants.py $(DATA_ASM_SUBDIR)/script_cmd_table.inc
python3 $(MISC_TOOL_DIR)/make_scr_cmd_constants.py
$(C_BUILDDIR)/wild_encounter.o: c_dep += $(DATA_SRC_SUBDIR)/wild_encounters.h
$(C_BUILDDIR)/trainer_see.o: c_dep += $(INCLUDE_DIRS)/constants/script_commands.h
$(C_BUILDDIR)/vs_seeker.o: c_dep += $(INCLUDE_DIRS)/constants/script_commands.h
PERL := perl
SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c

View File

@ -17,7 +17,7 @@
If you use **`pokeemerald-expansion`**, please credit **RHH (Rom Hacking Hideout)**. Optionally, include the version number for clarity.
```
Based off RHH's pokeemerald-expansion 1.13.2 https://github.com/rh-hideout/pokeemerald-expansion/
Based off RHH's pokeemerald-expansion 1.13.3 https://github.com/rh-hideout/pokeemerald-expansion/
```
Please consider [crediting all contributors](CREDITS.md) involved in the project!

View File

@ -187,8 +187,13 @@
.4byte \jumpInstr
.endm
.macro unused_0x21
.macro jumpifstatignorecontrary battler:req, comparison:req, stat:req, value:req, jumpInstr:req
.byte 0x21
.byte \battler
.byte \comparison
.byte \stat
.byte \value
.4byte \jumpInstr
.endm
.macro jumpbasedontype battler:req, type:req, jumpIfType:req, jumpInstr:req
@ -1965,12 +1970,6 @@
.4byte \jumpInstr
.endm
.macro infatuatewithbattler battler:req, infatuateWith:req
callnative BS_InfatuateWithBattler
.byte \battler
.byte \infatuateWith
.endm
.macro setlastuseditem battler:req
callnative BS_SetLastUsedItem
.byte \battler
@ -2102,9 +2101,8 @@
.byte \id
.endm
.macro arenawaitmessage id:req
.macro arenawaitmessage
callnative BS_ArenaWaitMessage
.byte \id
.endm
.macro waitcry
@ -2406,8 +2404,8 @@
.4byte \jumpInstr
.endm
.macro jumpifleafguardprotected battler:req, jumpInstr:req
callnative BS_JumpIfLeafGuardProtected
.macro jumpifabilitypreventsrest battler:req, jumpInstr:req
callnative BS_JumpIfAbilityPreventsRest
.byte \battler
.4byte \jumpInstr
.endm

View File

@ -684,6 +684,14 @@
map \map
.endm
@ Set the player object's invisibility to FALSE.
.macro showplayer
.byte SCR_OP_SHOWOBJECTAT
.2byte LOCALID_PLAYER
.byte 0 @ map group
.byte 0 @ map num
.endm
@ Sets the specified object's invisibility to TRUE.
.macro hideobjectat localId:req, map:req
.byte SCR_OP_HIDEOBJECTAT
@ -691,6 +699,14 @@
map \map
.endm
@ Set the player object's invisibility to TRUE.
.macro hideplayer
.byte SCR_OP_HIDEOBJECTAT
.2byte LOCALID_PLAYER
.byte 0 @ map group
.byte 0 @ map num
.endm
@ Turns the currently selected object (if there is one) to face the player.
.macro faceplayer
.byte SCR_OP_FACEPLAYER
@ -2482,7 +2498,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
@ -2654,3 +2670,9 @@
callnative ScriptChangeFollowerNPCBattlePartner
.2byte \battlePartner
.endm
@ VS Seeker
.macro vsseeker_rematchid rematchId:req
callnative NativeVsSeekerRematchId, requests_effects=1
.2byte \rematchId
.endm

View File

@ -2,8 +2,12 @@
@ Takes a MAP constant and outputs the map group and map number as separate bytes
.macro map map_id:req
.ifdef \map_id
.byte \map_id >> 8 @ map group
.byte \map_id & 0xFF @ map num
.else
.error "undefined map (check for typos)"
.endif
.endm
@ Defines a map script. 'type' is any MAP_SCRIPT_* constant (see include/constants/map_scripts.h)

View File

@ -3356,6 +3356,7 @@ gBattleAnimMove_AquaJet::
call RisingWaterHitEffect
waitforvisualfinish
createvisualtask AnimTask_ExtremeSpeedMonReappear, 2
setarg 0x7, 0x1000
waitforvisualfinish
visible ANIM_ATTACKER
clearmonbg ANIM_DEF_PARTNER
@ -29001,7 +29002,7 @@ gBattleAnimMove_Transform::
monbg ANIM_ATTACKER
playsewithpan SE_M_TELEPORT, SOUND_PAN_ATTACKER
waitplaysewithpan SE_M_MINIMIZE, SOUND_PAN_ATTACKER, 48
createvisualtask AnimTask_TransformMon, 2, 0, 1
createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_TRANSFORM
waitforvisualfinish
clearmonbg ANIM_ATTACKER
end
@ -31512,14 +31513,14 @@ gBattleAnimGeneral_SimpleHeal::
gBattleAnimGeneral_IllusionOff::
monbg ANIM_TARGET
createvisualtask AnimTask_TransformMon, 2, 1, 0
createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_ILLUSION_OFF
waitforvisualfinish
clearmonbg ANIM_TARGET
end
gBattleAnimGeneral_FormChange::
monbg ANIM_ATTACKER
createvisualtask AnimTask_TransformMon, 2, 1, 0
createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_FORM_CHANGE
waitforvisualfinish
clearmonbg ANIM_ATTACKER
end
@ -31551,7 +31552,7 @@ gBattleAnimGeneral_MegaEvolution::
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
waitforvisualfinish
createvisualtask SoundTask_PlayNormalCry, 0
createvisualtask AnimTask_HideSwapSprite, 2, 1, 0
createvisualtask AnimTask_HideSwapSprite, 2
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
createsprite gMegaSymbolSpriteTemplate ANIM_ATTACKER, 3, 0, 0, ANIM_ATTACKER
@ -31636,7 +31637,7 @@ gBattleAnimGeneral_TeraActivate::
createvisualtask AnimTask_SetOpponentShadowCallbacks, 2 @ Restore shadows hidden in the charge script
loadspritegfx ANIM_TAG_TERA_SYMBOL
loadspritegfx ANIM_TAG_SPARKLE_6
createvisualtask AnimTask_HideSwapSprite, 2, 1, 0
createvisualtask AnimTask_HideSwapSprite, 2
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
createvisualtask SoundTask_PlayNormalCry, 0
@ -31776,7 +31777,7 @@ General_PrimalReversion_Alpha:
delay 20
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
waitforvisualfinish
createvisualtask AnimTask_HideSwapSprite, 2, 1, 0
createvisualtask AnimTask_HideSwapSprite, 2
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
createvisualtask SoundTask_PlayNormalCry, 0
@ -31809,7 +31810,7 @@ General_PrimalReversion_Omega:
delay 20
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
waitforvisualfinish
createvisualtask AnimTask_HideSwapSprite, 2, 1, 0
createvisualtask AnimTask_HideSwapSprite, 2
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
createvisualtask SoundTask_PlayNormalCry, 0
@ -31849,7 +31850,7 @@ gBattleAnimGeneral_PowerConstruct::
delay 20
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
waitforvisualfinish
createvisualtask AnimTask_HideSwapSprite, 2, 1, 0
createvisualtask AnimTask_HideSwapSprite, 2
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
createvisualtask SoundTask_PlayNormalCry, 0
@ -31919,7 +31920,7 @@ gBattleAnimGeneral_UltraBurst::
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
createsprite gUltraBurstSymbolSpriteTemplate, ANIM_ATTACKER, 0x0, 0x0, 0x0, 0x0, 0x0
waitforvisualfinish
createvisualtask AnimTask_HideSwapSprite, 2, 1, 0
createvisualtask AnimTask_HideSwapSprite, 2
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
createvisualtask SoundTask_PlayNormalCry, 0

View File

@ -408,6 +408,7 @@ BattleScript_SaltCureExtraDamage::
printstring STRINGID_TARGETISHURTBYSALTCURE
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_ATTACKER
tryrestorehpberry
end2
BattleScript_HurtTarget_NoString:
@ -2879,6 +2880,7 @@ BattleScript_CantMakeAsleep::
BattleScript_EffectAbsorbLiquidOoze::
call BattleScript_AbilityPopUpTarget
jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_EffectAbsorbRet
goto BattleScript_EffectAbsorb
BattleScript_EffectAbsorb::
@ -2888,6 +2890,7 @@ BattleScript_EffectAbsorb::
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_ATTACKER
bicword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_HP_UPDATE | HITMARKER_PASSIVE_HP_UPDATE
BattleScript_EffectAbsorbRet:
return
BattleScript_EffectExplosion::
@ -3186,9 +3189,7 @@ BattleScript_EffectRest::
jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_InsomniaProtects
jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_InsomniaProtects
jumpifability BS_ATTACKER, ABILITY_PURIFYING_SALT, BattleScript_InsomniaProtects
.if B_LEAF_GUARD_PREVENTS_REST >= GEN_5
jumpifleafguardprotected BS_TARGET, BattleScript_LeafGuardPreventsRest
.endif
jumpifabilitypreventsrest BS_TARGET, BattleScript_AbilityPreventsRest
trysetrest BattleScript_AlreadyAtFullHp
pause B_WAIT_TIME_SHORT
printfromtable gRestUsedStringIds
@ -3210,7 +3211,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
@ -4024,26 +4025,27 @@ BattleScript_FuryCutterHit:
BattleScript_TryDestinyKnotTarget:
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotTargetRet
infatuatewithbattler BS_TARGET, BS_ATTACKER
playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT
waitanimation
printstring STRINGID_DESTINYKNOTACTIVATES
tryinfatuating BattleScript_ButItFailed
volatileanimation BS_TARGET, VOLATILE_INFATUATION
waitanimation
printstring STRINGID_DESTINYKNOTACTIVATES
waitmessage B_WAIT_TIME_LONG
BattleScript_TryDestinyKnotTargetRet:
return
BattleScript_TryDestinyKnotAttacker:
jumpifnoholdeffect BS_TARGET, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotAttackerRet
infatuatewithbattler BS_ATTACKER, BS_TARGET
jumpifnoholdeffect BS_TARGET, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotTargetRet
playanimation BS_TARGET, B_ANIM_HELD_ITEM_EFFECT
waitanimation
swapattackerwithtarget
printstring STRINGID_DESTINYKNOTACTIVATES
tryinfatuating BattleScript_SwapTargetAttackerButItFailed
swapattackerwithtarget
volatileanimation BS_ATTACKER, VOLATILE_INFATUATION
waitanimation
printstring STRINGID_DESTINYKNOTACTIVATES
waitmessage B_WAIT_TIME_LONG
BattleScript_TryDestinyKnotAttackerRet:
return
BattleScript_EffectAttract::
@ -4220,7 +4222,7 @@ BattleScript_EffectBellyDrum::
attackcanceler
attackstring
ppreduce
jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed
jumpifstatignorecontrary BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed
halvehp BattleScript_ButItFailed
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_HP_UPDATE
attackanimation
@ -4377,6 +4379,9 @@ BattleScript_RestoreAttackerButItFailed:
BattleScript_RestoreTargetButItFailed:
restoretarget
goto BattleScript_ButItFailed
BattleScript_SwapTargetAttackerButItFailed:
swapattackerwithtarget
goto BattleScript_ButItFailed
BattleScript_NotAffected::
pause B_WAIT_TIME_SHORT
@ -5084,8 +5089,8 @@ BattleScript_FaintAttacker::
tryrevertweatherform
flushtextbox
waitanimation
tryactivatesoulheart
tryactivatereceiver BS_ATTACKER
tryactivatesoulheart
trytrainerslidemsgfirstoff BS_ATTACKER
return
@ -5102,8 +5107,8 @@ BattleScript_FaintTarget::
tryrevertweatherform
flushtextbox
waitanimation
tryactivatesoulheart
tryactivatereceiver BS_TARGET
tryactivatesoulheart
trytrainerslidemsgfirstoff BS_TARGET
return
@ -5626,10 +5631,12 @@ BattleScript_LeechSeedTurnDrainLiquidOoze::
copybyte gBattlerAbility, gBattlerAttacker
call BattleScript_AbilityPopUp
copybyte gBattlerAttacker, gBattlerTarget @ needed to get liquid ooze message correct
jumpifability BS_TARGET, ABILITY_MAGIC_GUARD, BattleScript_LeechSeedTurnDrainHealBlockEnd2
goto BattleScript_LeechSeedTurnDrainGainHp
BattleScript_LeechSeedTurnDrainHealBlock::
call BattleScript_LeechSeedTurnDrain
BattleScript_LeechSeedTurnDrainHealBlockEnd2:
end2
BattleScript_LeechSeedTurnDrainRecovery::
@ -6050,9 +6057,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::
@ -7105,7 +7111,7 @@ BattleScript_RecoilEnd::
return
BattleScript_ItemSteal::
playanimation BS_TARGET, B_ANIM_ITEM_STEAL
playanimation BS_EFFECT_BATTLER, B_ANIM_ITEM_STEAL
printstring STRINGID_PKMNSTOLEITEM
waitmessage B_WAIT_TIME_LONG
return
@ -7283,6 +7289,7 @@ BattleScript_ReceiverActivates::
printstring STRINGID_RECEIVERABILITYTAKEOVER
waitmessage B_WAIT_TIME_LONG
settracedability BS_ABILITY_BATTLER
switchinabilities BS_ABILITY_BATTLER
return
BattleScript_AbilityHpHeal:
@ -8212,11 +8219,13 @@ BattleScript_CuteCharmActivates::
return
BattleScript_GooeyActivates::
statbuffchange BS_ATTACKER, STAT_CHANGE_ONLY_CHECKING, BattleScript_GooeyActivatesRet
waitstate
call BattleScript_AbilityPopUp
swapattackerwithtarget @ for defiant, mirror armor
seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_SPD_MINUS_1
swapattackerwithtarget
BattleScript_GooeyActivatesRet:
return
BattleScript_AbilityStatusEffect::
@ -8711,7 +8720,7 @@ BattleScript_ArenaTurnBeginning::
playse SE_ARENA_TIMEUP1
drawarenareftextbox
arenajudgmentstring B_MSG_REF_COMMENCE_BATTLE
arenawaitmessage B_MSG_REF_COMMENCE_BATTLE
arenawaitmessage
pause B_WAIT_TIME_LONG
erasearenareftextbox
volumeup
@ -8729,26 +8738,26 @@ BattleScript_ArenaDoJudgment::
pause B_WAIT_TIME_LONG
drawarenareftextbox
arenajudgmentstring B_MSG_REF_THATS_IT
arenawaitmessage B_MSG_REF_THATS_IT
arenawaitmessage
pause B_WAIT_TIME_LONG
setbyte gBattleCommunication, 0 @ Reset state for arenajudgmentwindow
arenajudgmentwindow
pause B_WAIT_TIME_LONG
arenajudgmentwindow
arenajudgmentstring B_MSG_REF_JUDGE_MIND
arenawaitmessage B_MSG_REF_JUDGE_MIND
arenawaitmessage
arenajudgmentwindow
arenajudgmentstring B_MSG_REF_JUDGE_SKILL
arenawaitmessage B_MSG_REF_JUDGE_SKILL
arenawaitmessage
arenajudgmentwindow
arenajudgmentstring B_MSG_REF_JUDGE_BODY
arenawaitmessage B_MSG_REF_JUDGE_BODY
arenawaitmessage
arenajudgmentwindow
jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_PLAYER_LOST, BattleScript_ArenaJudgmentPlayerLoses
jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_TIE, BattleScript_ArenaJudgmentDraw
@ ARENA_RESULT_PLAYER_WON
arenajudgmentstring B_MSG_REF_PLAYER_WON
arenawaitmessage B_MSG_REF_PLAYER_WON
arenawaitmessage
arenajudgmentwindow
erasearenareftextbox
printstring STRINGID_DEFEATEDOPPONENTBYREFEREE
@ -8763,7 +8772,7 @@ BattleScript_ArenaDoJudgment::
BattleScript_ArenaJudgmentPlayerLoses:
arenajudgmentstring B_MSG_REF_OPPONENT_WON
arenawaitmessage B_MSG_REF_OPPONENT_WON
arenawaitmessage
arenajudgmentwindow
erasearenareftextbox
printstring STRINGID_LOSTTOOPPONENTBYREFEREE
@ -8778,11 +8787,12 @@ BattleScript_ArenaJudgmentPlayerLoses:
BattleScript_ArenaJudgmentDraw:
arenajudgmentstring B_MSG_REF_DRAW
arenawaitmessage B_MSG_REF_DRAW
arenawaitmessage
arenajudgmentwindow
erasearenareftextbox
printstring STRINGID_TIEDOPPONENTBYREFEREE
waitmessage B_WAIT_TIME_LONG
arenabothmonslost
playfaintcry BS_PLAYER1
waitcry
dofaintanimation BS_PLAYER1
@ -8793,7 +8803,6 @@ BattleScript_ArenaJudgmentDraw:
dofaintanimation BS_OPPONENT1
cleareffectsonfaint BS_OPPONENT1
waitanimation
arenabothmonslost
end2
BattleScript_AskIfWantsToForfeitMatch::
@ -8883,14 +8892,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::
@ -9099,6 +9122,7 @@ BattleScript_Pickpocket::
call BattleScript_AbilityPopUp
jumpifability BS_ATTACKER, ABILITY_STICKY_HOLD, BattleScript_PickpocketPrevented
swapattackerwithtarget
copybyte gEffectBattler, gBattlerTarget
call BattleScript_ItemSteal
swapattackerwithtarget
activateitemeffects

2
data/battle_scripts_2.s Normal file → Executable file
View File

@ -212,9 +212,9 @@ BattleScript_WallyBallThrow::
BattleScript_ShakeBallThrow::
animatewildpokemonafterfailedpokeball BS_TARGET
waitstate
waitmessage B_WAIT_TIME_LONG
printfromtable gBallEscapeStringIds
waitanimation
waitmessage B_WAIT_TIME_LONG
jumpifword CMP_NO_COMMON_BITS, gBattleTypeFlags, BATTLE_TYPE_SAFARI, BattleScript_ShakeBallThrowEnd
jumpifbyte CMP_NOT_EQUAL, gNumSafariBalls, 0, BattleScript_ShakeBallThrowEnd

View File

@ -835,7 +835,7 @@ EventScript_UnusedBoardFerry::
delay 30
applymovement LOCALID_PLAYER, Common_Movement_WalkInPlaceFasterUp
waitmovement 0
showobjectat LOCALID_PLAYER, 0
showplayer
delay 30
applymovement LOCALID_PLAYER, Movement_UnusedBoardFerry
waitmovement 0
@ -850,7 +850,7 @@ Common_EventScript_FerryDepartIsland::
call_if_eq VAR_FACING, DIR_SOUTH, Ferry_EventScript_DepartIslandSouth
call_if_eq VAR_FACING, DIR_WEST, Ferry_EventScript_DepartIslandWest
delay 30
hideobjectat LOCALID_PLAYER, 0
hideplayer
call Common_EventScript_FerryDepart
return

View File

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

View File

@ -333,7 +333,7 @@ LilycoveCity_Harbor_EventScript_BoardFerryWithSailor::
call_if_eq VAR_FACING, DIR_NORTH, LilycoveCity_Harbor_EventScript_PlayerBoardFerryNorth
call_if_eq VAR_FACING, DIR_EAST, LilycoveCity_Harbor_EventScript_PlayerBoardFerryEast
delay 30
hideobjectat LOCALID_PLAYER, 0
hideplayer
setvar VAR_0x8004, LOCALID_LILYCOVE_HARBOR_SS_TIDAL
call Common_EventScript_FerryDepart
return
@ -393,7 +393,7 @@ LilycoveCity_Harbor_EventScript_BoardFerry::
call_if_eq VAR_FACING, DIR_NORTH, LilycoveCity_Harbor_EventScript_PlayerBoardFerryNorth
call_if_eq VAR_FACING, DIR_EAST, LilycoveCity_Harbor_EventScript_PlayerBoardFerryEast
delay 30
hideobjectat LOCALID_PLAYER, 0
hideplayer
setvar VAR_0x8004, LOCALID_LILYCOVE_HARBOR_SS_TIDAL
call Common_EventScript_FerryDepart
return

View File

@ -156,7 +156,7 @@ LittlerootTown_EventScript_GoInsideWithMom::
waitmovement 0
setflag FLAG_HIDE_LITTLEROOT_TOWN_MOM_OUTSIDE
setvar VAR_LITTLEROOT_INTRO_STATE, 3
hideobjectat LOCALID_PLAYER, 0
hideplayer
closedoor VAR_0x8004, VAR_0x8005
waitdooranim
clearflag FLAG_HIDE_LITTLEROOT_TOWN_FAT_MAN

View File

@ -228,7 +228,7 @@ SlateportCity_Harbor_EventScript_BoardFerry::
call_if_eq VAR_FACING, DIR_NORTH, SlateportCity_Harbor_EventScript_BoardFerryNorth
call_if_eq VAR_FACING, DIR_EAST, SlateportCity_Harbor_EventScript_BoardFerryEast
delay 30
hideobjectat LOCALID_PLAYER, 0
hideplayer
setvar VAR_0x8004, LOCALID_SLATEPORT_HARBOR_SS_TIDAL
call Common_EventScript_FerryDepart
return

View File

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

View File

@ -356,7 +356,7 @@ CableClub_EventScript_EnterColosseum::
waitdooranim
applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom
waitmovement 0
hideobjectat LOCALID_PLAYER, 0
hideplayer
closedoor 9, 1
waitdooranim
release
@ -450,7 +450,7 @@ CableClub_EventScript_EnterTradeCenter::
waitdooranim
applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom
waitmovement 0
hideobjectat LOCALID_PLAYER, 0
hideplayer
closedoor 9, 1
waitdooranim
release
@ -515,7 +515,7 @@ CableClub_EventScript_EnterRecordCorner::
waitdooranim
applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom
waitmovement 0
hideobjectat LOCALID_PLAYER, 0
hideplayer
closedoor 9, 1
waitdooranim
release
@ -902,7 +902,7 @@ CableClub_EventScript_EnterUnionRoom::
waitdooranim
applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom
waitmovement 0
hideobjectat LOCALID_PLAYER, 0
hideplayer
closedoor 5, 1
waitdooranim
special Script_ResetUnionRoomTrade
@ -1202,7 +1202,7 @@ CableClub_EventScript_EnterWirelessLinkRoom::
waitdooranim
applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom
waitmovement 0
hideobjectat LOCALID_PLAYER, 0
hideplayer
closedoor 9, 1
waitdooranim
release
@ -1384,7 +1384,7 @@ MossdeepCity_GameCorner_1F_EventScript_EnterMinigameRoom::
closemessage
applymovement LOCALID_PLAYER, Movement_PlayerEnterMinigameRoom
waitmovement 0
hideobjectat LOCALID_PLAYER, 0
hideplayer
release
waitstate
end

View File

@ -3,23 +3,44 @@
- [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)
- [Vs. Seeker](tutorials/vs_seeker.md)
- [Changelog](./CHANGELOG.md)
- [1.13.x]()
- [Version 1.13.3](changelogs/1.13.x/1.13.3.md)
- [Version 1.13.2](changelogs/1.13.x/1.13.2.md)
- [Version 1.13.1](changelogs/1.13.x/1.13.1.md)
- [Version 1.13.0](changelogs/1.13.x/1.13.0.md)
@ -39,6 +60,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 +109,5 @@
- [Team Procedures]()
- [How to make an Expansion version](team_procedures/expansion_versions.md)
- [Release Schedule and Process](team_procedures/schedule.md)
- [Merge Checklist](team_procedures/merge_checklist.md)
- [Scope Guidelines](team_procedures/scope.md)

View File

@ -1,6 +1,5 @@
[book]
language = "en"
multilingual = false
src = "."
title = "pokeemerald-expansion"

View File

@ -0,0 +1,193 @@
```md
## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.13.3
`.
```
## 🧬 General 🧬
### Added
* Add test to detect save file shifting by @Bassoonian in [#8030](https://github.com/rh-hideout/pokeemerald-expansion/pull/8030)
### Changed
* 1.13.2 release by @hedara90 in [#7831](https://github.com/rh-hideout/pokeemerald-expansion/pull/7831)
* Remove unnecessary EWRAM and IWRAM variables from the Window code by @estellarc in [#7897](https://github.com/rh-hideout/pokeemerald-expansion/pull/7897)
* Replace magic numbers with define'd values in field_player_avatar.c by @FosterProgramming in [#7910](https://github.com/rh-hideout/pokeemerald-expansion/pull/7910)
* Pret merge (1st of November, 2025) by @hedara90 in [#8103](https://github.com/rh-hideout/pokeemerald-expansion/pull/8103)
### Fixed
* Fixes `EVO_BATTLE_END` evolutions not removing item with additional conditions by @PhallenTree in [#7841](https://github.com/rh-hideout/pokeemerald-expansion/pull/7841)
* Fix EV display in debug menu by @cawtds in [#7848](https://github.com/rh-hideout/pokeemerald-expansion/pull/7848)
* Fix right player position battle partner target display by @ravepossum in [#7878](https://github.com/rh-hideout/pokeemerald-expansion/pull/7878)
* Ensure last used ball and move description window sprites don't free palette too early by @ravepossum in [#7875](https://github.com/rh-hideout/pokeemerald-expansion/pull/7875)
* Fix fusion pokemon aquiring illegal movesets by @FosterProgramming in [#7896](https://github.com/rh-hideout/pokeemerald-expansion/pull/7896)
- Calyrex will now delete moves if they are not part of its learnset when unfusing
- Kyurem will now properly swap the moves Glaciate and Scary Face to its signature moves when fusing/unfusing
* Fix bug causing hgss dex to freeze by @FosterProgramming in [#7936](https://github.com/rh-hideout/pokeemerald-expansion/pull/7936)
- Fix a bug when checking evolutions info screen while search mode is active in the hgss dex
* Show convergent evolution to Gholdengo in HGSS dex by @FosterProgramming in [#7934](https://github.com/rh-hideout/pokeemerald-expansion/pull/7934)
* Fix HGSS dex sprites for gen9+ by @FosterProgramming in [#7922](https://github.com/rh-hideout/pokeemerald-expansion/pull/7922)
* Fix nicknames containing many / overflowing the party screen textbox by @hedara90 in [#7970](https://github.com/rh-hideout/pokeemerald-expansion/pull/7970)
* Fix ruination and nature's madness damage percentage by @FosterProgramming in [#7983](https://github.com/rh-hideout/pokeemerald-expansion/pull/7983)
* Fix ribbon colours by @hedara90 in [#7971](https://github.com/rh-hideout/pokeemerald-expansion/pull/7971)
* Fix long pokemon name in partner party not appearing properly by @FosterProgramming in [#8009](https://github.com/rh-hideout/pokeemerald-expansion/pull/8009)
* Fix battle dome bug (again) by @FosterProgramming in [#8007](https://github.com/rh-hideout/pokeemerald-expansion/pull/8007)
* Fix battle arena counting all judges loss for the opponent by @FosterProgramming in [#8046](https://github.com/rh-hideout/pokeemerald-expansion/pull/8046)
- Fix battle arena referees giving undeserved wins to the player
* Fix wrong gimmick spite showing when inputting too fast by @FosterProgramming in [#8066](https://github.com/rh-hideout/pokeemerald-expansion/pull/8066)
## 🗺️ Overworld 🗺️
### Fixed
* Fix LTO breaking with FREE_MYSTERY_GIFT set to TRUE by @DizzyEggg in [#7844](https://github.com/rh-hideout/pokeemerald-expansion/pull/7844)
* Fix dns palette weight by @FosterProgramming in [#7855](https://github.com/rh-hideout/pokeemerald-expansion/pull/7855)
* Bug Fix: NPC follower not inheriting facing direction upon creation by @Bivurnum in [#7895](https://github.com/rh-hideout/pokeemerald-expansion/pull/7895)
* Fix follower pokemon not playing animation when colliding by @FosterProgramming in [#7908](https://github.com/rh-hideout/pokeemerald-expansion/pull/7908)
* Fix incorrect font width in Dexnav search window hiding some elements by @FosterProgramming in [#7949](https://github.com/rh-hideout/pokeemerald-expansion/pull/7949)
* Fix dns color transition not applying weather blending by @FosterProgramming in [#7883](https://github.com/rh-hideout/pokeemerald-expansion/pull/7883)
* Fix follower NPC sidewaystair movement by @FosterProgramming in [#7909](https://github.com/rh-hideout/pokeemerald-expansion/pull/7909)
* Fix battle dome pre round 1 waiting room by @FosterProgramming in [#7976](https://github.com/rh-hideout/pokeemerald-expansion/pull/7976)
* Fix no_effect script command overwriting trainer data in trainer script by @FosterProgramming in [#7978](https://github.com/rh-hideout/pokeemerald-expansion/pull/7978)
* Setting wallclock time now properly sets fakeRTC by @FosterProgramming in [#7860](https://github.com/rh-hideout/pokeemerald-expansion/pull/7860)
- Fix time bug when setting wallclock in fakeRTC mode
- When setting the wall clock, it will start on current time instead of 10AM
- If FakeRTC is active, new game will start at 10AM
* Bugfix hidefollower not waiting properly by @FosterProgramming in [#7768](https://github.com/rh-hideout/pokeemerald-expansion/pull/7768)
* Bugfix Emotes not loading their palette by @estellarc in [#7843](https://github.com/rh-hideout/pokeemerald-expansion/pull/7843)
* Fix OW Pokémon VObjects by @HashtagMarky in [#7991](https://github.com/rh-hideout/pokeemerald-expansion/pull/7991)
* fix: hypertraining a stat now optionally reflects in the summary screen by @khbsd in [#8035](https://github.com/rh-hideout/pokeemerald-expansion/pull/8035)
* Fix pc turning on/off animation not working in battle frontier by @FosterProgramming in [#8048](https://github.com/rh-hideout/pokeemerald-expansion/pull/8048)
* Fix non-battle trainer script not running properly by @FosterProgramming in [#8056](https://github.com/rh-hideout/pokeemerald-expansion/pull/8056)
## 🐉 Pokémon 🐉
### Fixed
* Fixes shininess for givemon by @cawtds in [#7847](https://github.com/rh-hideout/pokeemerald-expansion/pull/7847)
* Fix Minior start of battle form by @hedara90 in [#7972](https://github.com/rh-hideout/pokeemerald-expansion/pull/7972)
* Add error messages for trying to send an illegal mon to the PC and fixes index in double wild battles by @hedara90 in [#7982](https://github.com/rh-hideout/pokeemerald-expansion/pull/7982)
* fix: hypertraining a stat now optionally reflects in the summary screen by @khbsd in [#8035](https://github.com/rh-hideout/pokeemerald-expansion/pull/8035)
* Add camera-facing right-walking Krabby and Kingler follower sprites by @rayrobdod in [#7881](https://github.com/rh-hideout/pokeemerald-expansion/pull/7881)
## ⚔️ Battle General ⚔️
### Changed
* Tests for Battery ability by @grintoul1 in [#7846](https://github.com/rh-hideout/pokeemerald-expansion/pull/7846)
* Aura Break tests by @grintoul1 in [#8099](https://github.com/rh-hideout/pokeemerald-expansion/pull/8099)
### Fixed
* Fixes Endure lasting forever by @AlexOn1ine in [#7838](https://github.com/rh-hideout/pokeemerald-expansion/pull/7838)
* Fix for uncaught mon with terrain active by @DizzyEggg in [#7868](https://github.com/rh-hideout/pokeemerald-expansion/pull/7868)
* Fixes Steadfast not activating + tests by @PhallenTree in [#7886](https://github.com/rh-hideout/pokeemerald-expansion/pull/7886)
* Fix hgss pokedex when catching mon with terrain by @DizzyEggg in [#7884](https://github.com/rh-hideout/pokeemerald-expansion/pull/7884)
* Fix SmartStrike crashing the game in double battles by @DizzyEggg in [#7902](https://github.com/rh-hideout/pokeemerald-expansion/pull/7902)
* Fix palaceUnableToUseMove falling through to change battle script by @ghoulslash in [#7912](https://github.com/rh-hideout/pokeemerald-expansion/pull/7912)
* Add new Move target types to GetBattlePalaceMoveGroup by @ghoulslash in [#7913](https://github.com/rh-hideout/pokeemerald-expansion/pull/7913)
* Fixes 2 instances of global usage in the `Cmd_adjustdamage` loop by @AlexOn1ine in [#7918](https://github.com/rh-hideout/pokeemerald-expansion/pull/7918)
* Fix Battle Anim monbg calls Part 1 by @ghoulslash in [#7906](https://github.com/rh-hideout/pokeemerald-expansion/pull/7906)
* Adds missing breakable flag for Bulletproof by @AlexOn1ine in [#7928](https://github.com/rh-hideout/pokeemerald-expansion/pull/7928)
* Fix multiple battle arena bugs by @FosterProgramming in [#7941](https://github.com/rh-hideout/pokeemerald-expansion/pull/7941)
* Fixes Cursed Body failing to disable moves on the last PP by @PhallenTree in [#7940](https://github.com/rh-hideout/pokeemerald-expansion/pull/7940)
* Fixed an issue related to Pokemon animation bleeding into attack anim… by @LinathanZel in [#7924](https://github.com/rh-hideout/pokeemerald-expansion/pull/7924)
* Fixes terrain not failing on duplicate by @AlexOn1ine in [#7939](https://github.com/rh-hideout/pokeemerald-expansion/pull/7939)
* Fix volt tackle not inflicting recoil by @FosterProgramming in [#7944](https://github.com/rh-hideout/pokeemerald-expansion/pull/7944)
* Fix Knock Off not being restored and Wild Battles by @ghoulslash in [#7952](https://github.com/rh-hideout/pokeemerald-expansion/pull/7952)
* Fix Anticipation type effectiveness check by @spindrift64 in [#7840](https://github.com/rh-hideout/pokeemerald-expansion/pull/7840)
* Fix Cherim and Castfrom not reverting to baseform when Teraform Zero is triggered by @FosterProgramming in [#7961](https://github.com/rh-hideout/pokeemerald-expansion/pull/7961)
* Fix Focus Energy boosting crit by the wrong amount with gen1 crit chance by @FosterProgramming in [#7956](https://github.com/rh-hideout/pokeemerald-expansion/pull/7956)
* Fix bug where transformed pokemon lose copied stats on levelup by @FosterProgramming in [#7969](https://github.com/rh-hideout/pokeemerald-expansion/pull/7969)
* Fixes Shields Down incorrectly preventing status on Minior Core form by @PhallenTree in [#7968](https://github.com/rh-hideout/pokeemerald-expansion/pull/7968)
* SetShellSideArmCategory avoid div by zero by @DizzyEggg in [#7980](https://github.com/rh-hideout/pokeemerald-expansion/pull/7980)
* CalcBarFilledPixels Safe Div by @DizzyEggg in [#7979](https://github.com/rh-hideout/pokeemerald-expansion/pull/7979)
* Fix psychic terrain affecting semi-invulnerable mons by @FosterProgramming in [#7986](https://github.com/rh-hideout/pokeemerald-expansion/pull/7986)
* Fixes Terrain Extender timer by @AlexOn1ine in [#7995](https://github.com/rh-hideout/pokeemerald-expansion/pull/7995)
* Fixed Max Move in-battle descriptions by @AsparagusEduardo in [#8004](https://github.com/rh-hideout/pokeemerald-expansion/pull/8004)
* Fixes Echoed Voice base power increase depending on attacker's use of the move by @PhallenTree in [#7997](https://github.com/rh-hideout/pokeemerald-expansion/pull/7997)
* Fixed Stomping Tantrum not doubling in damage if the user failed Protect by @AsparagusEduardo in [#8008](https://github.com/rh-hideout/pokeemerald-expansion/pull/8008)
* Fix badge boost not applying in gen1 and 2 by @FosterProgramming in [#8013](https://github.com/rh-hideout/pokeemerald-expansion/pull/8013)
* Fix toxic debris setting hazards on the wrong side when hit by an ally by @FosterProgramming in [#8026](https://github.com/rh-hideout/pokeemerald-expansion/pull/8026)
* Adds missing alive check for Rapid Spin by @AlexOn1ine in [#8024](https://github.com/rh-hideout/pokeemerald-expansion/pull/8024)
* Fixes visual glitch after Misty Explosion by @AlexOn1ine in [#8022](https://github.com/rh-hideout/pokeemerald-expansion/pull/8022)
* Fixes Protosynthesis not activating after weather was reset by @AlexOn1ine in [#8021](https://github.com/rh-hideout/pokeemerald-expansion/pull/8021)
* Fix Salt Cure script by @AlexOn1ine in [#8005](https://github.com/rh-hideout/pokeemerald-expansion/pull/8005)
* Fix emergency exit not triggering properly during wild battles by @FosterProgramming in [#8037](https://github.com/rh-hideout/pokeemerald-expansion/pull/8037)
* Fix target cancelling not working properly with z-move by @FosterProgramming in [#8067](https://github.com/rh-hideout/pokeemerald-expansion/pull/8067)
* Corrects battler partner identification in battle_ai_switch_items.c by @grintoul1 in [#8071](https://github.com/rh-hideout/pokeemerald-expansion/pull/8071)
* Fix Ally Switch being useable in Frontier Link Multi battles by @grintoul1 in [#8059](https://github.com/rh-hideout/pokeemerald-expansion/pull/8059)
* Fixes hazards and switch-in items not being reset when switching in by @PhallenTree in [#8074](https://github.com/rh-hideout/pokeemerald-expansion/pull/8074)
* Fixes Liquid Ooze dmg not blocked by Magic Guard by @AlexOn1ine in [#8036](https://github.com/rh-hideout/pokeemerald-expansion/pull/8036)
* Fix move description prompt window not appear when choosing a move after canceling target selection by @FosterProgramming in [#8055](https://github.com/rh-hideout/pokeemerald-expansion/pull/8055)
* Initialize DamageContext on declaration to zero by @AlexOn1ine in [#8076](https://github.com/rh-hideout/pokeemerald-expansion/pull/8076)
* Fixed Hunger Switch changing forms on switch out while Tera'd by @AsparagusEduardo in [#8080](https://github.com/rh-hideout/pokeemerald-expansion/pull/8080)
* Fixes Gooey/Tangling Hair ability pop up triggering on Clear Body by @AlexOn1ine in [#8083](https://github.com/rh-hideout/pokeemerald-expansion/pull/8083)
* Fixes intimidate activating on empty field by @AlexOn1ine in [#8058](https://github.com/rh-hideout/pokeemerald-expansion/pull/8058)
* Fix bug where mon selection doesn't properly account for party order by @FosterProgramming in [#8088](https://github.com/rh-hideout/pokeemerald-expansion/pull/8088)
* Fix bug when a captured pokemon replaces a party member who changed forms by @FosterProgramming in [#8091](https://github.com/rh-hideout/pokeemerald-expansion/pull/8091)
* Fixed Zygarde Complete disappearing upon catch by @AsparagusEduardo in [#8089](https://github.com/rh-hideout/pokeemerald-expansion/pull/8089)
* Initialize DamageContext struct with zero values by @AlexOn1ine in [#8107](https://github.com/rh-hideout/pokeemerald-expansion/pull/8107)
## 🤹 Moves 🤹
### Fixed
* Updated Mountain Gale's PP for Gen 9 by @fdeblasio in [#7856](https://github.com/rh-hideout/pokeemerald-expansion/pull/7856)
* Fix Brine move anim and document Water Spout anim by @ravepossum in [#7865](https://github.com/rh-hideout/pokeemerald-expansion/pull/7865)
* Add Struggle tests, weakness berry tests and prevent Struggle from activating Silk Scarf and Chilan Berry by @rayrobdod in [#7880](https://github.com/rh-hideout/pokeemerald-expansion/pull/7880)
* Fix Battle Anim monbg calls Part 1 by @ghoulslash in [#7906](https://github.com/rh-hideout/pokeemerald-expansion/pull/7906)
* Add missing end signal for AnimTask_SetAttackerInvisibleWaitForSignal by @hedara90 in [#7950](https://github.com/rh-hideout/pokeemerald-expansion/pull/7950)
* Fix Ally Switch being useable in Frontier Link Multi battles by @grintoul1 in [#8059](https://github.com/rh-hideout/pokeemerald-expansion/pull/8059)
* Fixed Belly Drum/Contrary interaction at max Attack by @AsparagusEduardo in [#8078](https://github.com/rh-hideout/pokeemerald-expansion/pull/8078)
## 🎭 Abilities 🎭
### Changed
* Tests for Battery ability by @grintoul1 in [#7846](https://github.com/rh-hideout/pokeemerald-expansion/pull/7846)
* Aura Break tests by @grintoul1 in [#8099](https://github.com/rh-hideout/pokeemerald-expansion/pull/8099)
## 🧶 Items 🧶
### Fixed
* Add gBallItemIds Array by @HashtagMarky in [#7905](https://github.com/rh-hideout/pokeemerald-expansion/pull/7905)
* Fix Persim Berry battle usage by @hedara90 in [#7963](https://github.com/rh-hideout/pokeemerald-expansion/pull/7963)
## 🤖 Battle AI 🤖
### Fixed
* Add failsafe to AI_DecideHoldEffectForTurn by @AlexOn1ine in [#7849](https://github.com/rh-hideout/pokeemerald-expansion/pull/7849)
* Fix some ai action check happening before the logic was computed by @FosterProgramming in [#7867](https://github.com/rh-hideout/pokeemerald-expansion/pull/7867)
- Roamers will now flee in the first turn of battle
* Fix ShouldPivot overwriting random memory by @DizzyEggg in [#7882](https://github.com/rh-hideout/pokeemerald-expansion/pull/7882)
* Fix AI seeing priority wrong for players choice lock by @MaximeGr00 in [#7899](https://github.com/rh-hideout/pokeemerald-expansion/pull/7899)
* fix (post-KO switch): force AI data recalc to see abilities on field correctly when pivot moves used by player by @ghostyboyy97 in [#7900](https://github.com/rh-hideout/pokeemerald-expansion/pull/7900)
* Add missing break to Power Split AI case by @ghoulslash in [#7959](https://github.com/rh-hideout/pokeemerald-expansion/pull/7959)
## 🧹 Other Cleanup 🧹
* Fix some failed and assume fail tests with `GEN_LATEST` = `GEN_5` by @AsparagusEduardo in [#7735](https://github.com/rh-hideout/pokeemerald-expansion/pull/7735)
* Update INSTALL.md by @RubyRaven6 in [#7852](https://github.com/rh-hideout/pokeemerald-expansion/pull/7852)
* Remove unnecessary EWRAM and IWRAM variables from the Window code by @estellarc in [#7897](https://github.com/rh-hideout/pokeemerald-expansion/pull/7897)
* Replace magic numbers with define'd values in field_player_avatar.c by @FosterProgramming in [#7910](https://github.com/rh-hideout/pokeemerald-expansion/pull/7910)
* Reverts wrongly applies fix to book.toml by @AlexOn1ine in [#8105](https://github.com/rh-hideout/pokeemerald-expansion/pull/8105)
## 🧪 Test Runner 🧪
### Changed
* Fix some failed and assume fail tests with `GEN_LATEST` = `GEN_5` by @AsparagusEduardo in [#7735](https://github.com/rh-hideout/pokeemerald-expansion/pull/7735)
* Tests for Battery ability by @grintoul1 in [#7846](https://github.com/rh-hideout/pokeemerald-expansion/pull/7846)
* Fixed fainting form change tests by @AsparagusEduardo in [#8079](https://github.com/rh-hideout/pokeemerald-expansion/pull/8079)
* Aura Break tests by @grintoul1 in [#8099](https://github.com/rh-hideout/pokeemerald-expansion/pull/8099)
### Fixed
* Fix Knock Off not being restored and Wild Battles by @ghoulslash in [#7952](https://github.com/rh-hideout/pokeemerald-expansion/pull/7952)
* Fixes Shields Down incorrectly preventing status on Minior Core form by @PhallenTree in [#7968](https://github.com/rh-hideout/pokeemerald-expansion/pull/7968)
* Fixed Stomping Tantrum not doubling in damage if the user failed Protect by @AsparagusEduardo in [#8008](https://github.com/rh-hideout/pokeemerald-expansion/pull/8008)
* Fix stats defined in tests being overwritteng by stat change by @FosterProgramming in [#8018](https://github.com/rh-hideout/pokeemerald-expansion/pull/8018)
## 📚 Documentation 📚
* Update INSTALL.md by @RubyRaven6 in [#7852](https://github.com/rh-hideout/pokeemerald-expansion/pull/7852)
* Updated PR template to make existing credit policy clearer by @pkmnsnfrn in [#7864](https://github.com/rh-hideout/pokeemerald-expansion/pull/7864)
* Fix image links in doc site by @rayrobdod in [#7948](https://github.com/rh-hideout/pokeemerald-expansion/pull/7948)
* Add all pages in `docs` to doc website by @rayrobdod in [#7907](https://github.com/rh-hideout/pokeemerald-expansion/pull/7907)
* Relativize doc links, to fix links in docs site by @rayrobdod in [#7964](https://github.com/rh-hideout/pokeemerald-expansion/pull/7964)
* Fix docs compile issue by @AlexOn1ine in [#8101](https://github.com/rh-hideout/pokeemerald-expansion/pull/8101)
* Reverts wrongly applies fix to book.toml by @AlexOn1ine in [#8105](https://github.com/rh-hideout/pokeemerald-expansion/pull/8105)
## New Contributors
* @HashtagMarky made their first contribution in [#7905](https://github.com/rh-hideout/pokeemerald-expansion/pull/7905)
* @MaximeGr00 made their first contribution in [#7899](https://github.com/rh-hideout/pokeemerald-expansion/pull/7899)
**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.13.2...expansion/1.13.3
<!--Last PR: 8107-->
<!--Used to keep track of the last PR merged in case new ones come in before the changelog is done.-->

View File

@ -46,6 +46,6 @@ if __name__ == '__main__':
sys.exit(0)
context, book = json.load(sys.stdin)
proc_items(book['sections'])
proc_items(book['items'])
print(json.dumps(book))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

115
docs/tutorials/vs_seeker.md Normal file
View File

@ -0,0 +1,115 @@
# `pokemerald-expansion` Vs. Seeker
## What is the Vs. Seeker?
The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously.
When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle.
## How is the Vs. Seeker enabled?
### Users
Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`.
### Players
`ITEM_VS_SEEKER` can only be used outside of battle. It can be used from the bag or registered to be used from the field.
Usage of the Vs. Seeker will ALWAYS fail unless all of the conditions are met:
* Player has at least five badges
* There is an NPC on screen that has previously been defeated
* Player is not inside of a building
* The Vs. Seeker is fully recharged
#### Charge
If the player has `ITEM_VS_SEEKER` and at least five badges, the Vs. Seeker will be charged by walking steps. The Vs. Seeker is fully charged once the player has walked `VSSEEKER_RECHARGE_STEPS`, which is `100` by default. The Vs. Seeker's charge is depleted if the player uses the item.
### How does Match Call interact with the Vs. Seeker?
When `I_VS_SEEKER_CHARGING` is enabled, the Match Call does not function at all. Trainers will never be rematch eligible outside of the use of the Vs. Seeker.
## How does the Vs. Seeker choose a Trainer?
When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch.
Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are considered Land/Water objects.
| Status | Is Land/Water Object | Emote | New Movement Type |
| --- | --- | --- | --- |
| Wants Rematch | Yes | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_COUNTER_CLOCKWISE` |
| Wants Rematch | No | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_FACE_DOWN` |
| Does Not Want Rematch | - | `MOVEMENT_ACTION_EMOTE_X` | none |
| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none |
### Rematch Table
| Sequence | Trainer ID |
| ---------- | ---------------- |
| 1st Battle | `TRAINER_ROSE_1` |
| 2nd Battle | `TRAINER_ROSE_2` |
| 3rd Battle | `TRAINER_ROSE_3` |
| 4th Battle | `TRAINER_ROSE_4` |
| 5th Battle | `TRAINER_ROSE_5` |
The game determines which version of the Trainer you'll fight next by following these rules:
1. Start with the next Trainer in the sequence after the one that has been defeated. If there are no more, the battle is against the last listed Trainer.
2. If that next Trainer hasn't been unlocked yet, the battle is against the latest available unlocked version.
3. If the next Trainer is unlocked but not yet defeated, the battle is against that version.
4. If the next Trainer has already been defeated, check the next one in the sequence.
## How do users implement rematches with the Vs. Seeker?
### Existing `pokemerald` Trainers
No extra work is required. With the exception of Wally, Gym Leaders and Elite Four, all of the rematchable Trainers in Emerald will work with the Vs. Seeker without any changes.
### New Trainers
#### Party / `gRematchTable`
Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`.
Once all of those constants and parties are defined, a new row must be added to `gRematchTable` (located in in `src/battle_setup.c`). The row header should be a rematch ID, which can be added in `include/constants/rematches.h`. The row contents must be the five constants created for the new parties, with the lat argument being the constant of the map (`include/constants/map_groups.h`) where the Trainer is placed.
If a Trainer is intended to have less than five unique rematch parties, the extra slots can be filled with the last available Trainer ID.
```c
// This Trainer only has two teams.
[REMATCH_ROSE] = REMATCH(TRAINER_ROSE_1, TRAINER_ROSE_2, TRAINER_ROSE_2, TRAINER_ROSE_2, TRAINER_ROSE_2, MAP_ROUTE118),
```
WARNING: Rematch IDs should be placed BEFORE `REMATCH_WALLY_VR`. Trainers below that are treated as "special Trainers" that are not triggered by the Vs. Seeker.
#### Scripts
The trainer's object needs to have a script that begins with a method to signify what this object's trainer ID is.
#### `trainerbattle`
```
Route103_EventScript_Daisy::
trainerbattle_single TRAINER_DAISY, Route103_Text_DaisyIntro, Route103_Text_DaisyDefeated
msgbox Route103_Text_DaisyPostBattle, MSGBOX_AUTOCLOSE
end
```
Daisy is using one of the `trainerbattle` macros, which has the trainer battle macro in the first command of the script. Most trainers in `pokeemerald` use this pattern.
##### `vsseeker_rematchid`
```
Route102_EventScript_Calvin::
vsseeker_rematchid TRAINER_CALVIN_1
applymovement LOCALID_CALVIN, CalvinMovementTest
waitmovement 0
trainerbattle_single TRAINER_CALVIN_1, Route102_Text_CalvinIntro, Route102_Text_CalvinDefeated, Route102_EventScript_CalvinRegisterMatchCallAfterBattle
specialvar VAR_RESULT, ShouldTryRematchBattle
goto_if_eq VAR_RESULT, TRUE, Route102_EventScript_CalvinRematch
setvar VAR_0x8004, TRAINER_CALVIN_1
specialvar VAR_RESULT, IsTrainerRegistered
goto_if_eq VAR_RESULT, FALSE, Route102_EventScript_CalvinTryRegister
msgbox Route102_Text_CalvinPostBattle, MSGBOX_DEFAULT
release
end
```
If the trainer has other script commands before the eventual `trainerbattle` macro, the first command in the script needs to be `vsseeker_rematchid`. This macro does nothing but takes a single argument, which should be the same as the first Trainer ID for this trainer.
#### `MOVEMENT_TYPE_COUNTER_CLOCKWISE`
If you want Trainers to spin once they are eligible for a rematch, their overworld graphics object ID (`include/constants/event_objects.h`) must be listed in either `regularTrainersOnLand` or `regularTrainersInWater`.Otherwise they will adopt the movement type `MOVEMENT_TYPE_FACE_DOWN`.
## What can be customized about the Vs. Seeker?
* **Unlock Conditions**: The next "level" of rematches is unlocked when a specific flag is set. The flags that are currently used in `GetGameProgressFlags` can be changed to flags that better suit your game.
* **Recharge Steps**: `VSSEEKER_RECHARGE_STEPS` is initally set to 100, but this value can be changed to any number under 256.
* **Badge Requirement**: `HasAtLeastFiveBadges` is used to check if the Vs. Seeker will successfully work. You can customize the number of badges by changing `REMATCH_BADGE_COUNT` or otherwise alterting the function.
## What are the limitations of the Vs. Seeker?
The Vs. Seeker does not currently work with Gym Leaders. There is a bug filed to hopefully fix this in the future.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

After

Width:  |  Height:  |  Size: 567 B

View File

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

View File

@ -13,6 +13,7 @@
#include "battle_util2.h"
#include "battle_bg.h"
#include "pokeball.h"
#include "main.h"
#include "battle_debug.h"
#include "battle_dynamax.h"
#include "battle_terastal.h"
@ -722,7 +723,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
@ -782,7 +783,8 @@ struct BattleStruct
u8 hazardsQueue[NUM_BATTLE_SIDES][HAZARDS_MAX_COUNT];
u8 numHazards[NUM_BATTLE_SIDES];
u8 hazardsCounter:4; // Counter for applying hazard on switch in
u8 padding2:4;
u8 incrementEchoedVoice:1;
u8 echoedVoiceCounter:3;
};
struct AiBattleData
@ -1117,7 +1119,7 @@ extern u16 gBattleTurnCounter;
extern u8 gBattlerAbility;
extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT];
extern void (*gPreBattleCallback1)(void);
extern MainCallback gPreBattleCallback1;
extern void (*gBattleMainFunc)(void);
extern struct BattleResults gBattleResults;
extern u8 gLeveledUpInBattle;

View File

@ -155,6 +155,7 @@ bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffe
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category);
enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef);
bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, u32 abilityDef);
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather);
bool32 AI_IsDamagedByRecoil(u32 battler);
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);

View File

@ -22,7 +22,7 @@ bool8 BattleInitAllSprites(u8 *state1, u8 *battler);
void ClearSpritesHealthboxAnimData(void);
void CopyAllBattleSpritesInvisibilities(void);
void CopyBattleSpriteInvisibility(u8 battler);
void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool8 trackEnemyPersonality);
void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, u8 changeType);
void BattleLoadSubstituteOrMonSpriteGfx(u8 battler, bool8 loadMonSprite);
void LoadBattleMonGfxAndAnimate(u8 battler, bool8 loadMonSprite, u8 spriteId);
void TrySetBehindSubstituteSpriteBit(u8 battler, u16 move);

View File

@ -35,6 +35,7 @@ void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick);
void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId);
void CreateGimmickTriggerSprite(u32 battler);
bool32 IsGimmickTriggerSpriteActive(void);
bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler);
void HideGimmickTriggerSprite(void);
void DestroyGimmickTriggerSprite(void);

View File

@ -2,6 +2,8 @@
#define GUARD_BATTLE_PYRAMID_BAG_H
#include "list_menu.h"
#include "main.h"
#include "task.h"
enum {
PYRAMIDBAG_LOC_FIELD,
@ -29,7 +31,7 @@ enum {
struct PyramidBagMenu
{
void (*newScreenCallback)(void);
MainCallback newScreenCallback;
u8 tilemapBuffer[BG_SCREEN_SIZE];
u8 spriteIds[PBAG_SPRITE_COUNT];
u8 windowIds[5];
@ -49,7 +51,7 @@ struct PyramidBagMenu
struct PyramidBagMenuState
{
void (*exitCallback)(void);
MainCallback exitCallback;
u8 location;
u16 cursorPosition;
u16 scrollPosition;
@ -63,11 +65,11 @@ void CB2_PyramidBagMenuFromStartMenu(void);
void CB2_ReturnToPyramidBagMenu(void);
void UpdatePyramidBagList(void);
void UpdatePyramidBagCursorPos(void);
void GoToBattlePyramidBagMenu(u8 location, void (*exitCallback)(void));
void GoToBattlePyramidBagMenu(u8 location, MainCallback exitCallback);
void Task_CloseBattlePyramidBagMessage(u8 taskId);
void TryStoreHeldItemsInPyramidBag(void);
void ChooseItemsToTossFromPyramidBag(void);
void CloseBattlePyramidBag(u8 taskId);
void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, void (*callback)(u8 taskId));
void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, TaskFunc callback);
#endif // GUARD_BATTLE_PYRAMID_BAG_H

View File

@ -73,6 +73,7 @@ void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerSc
void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript);
void SetUpTwoTrainersBattle(void);
bool32 GetTrainerFlagFromScriptPointer(const u8 *data);
bool32 GetRematchFromScriptPointer(const u8 *data);
void SetTrainerFacingDirection(void);
u8 GetTrainerBattleMode(void);
bool8 GetTrainerFlag(void);
@ -90,12 +91,14 @@ const u8 *GetTrainerALoseText(void);
const u8 *GetTrainerBLoseText(void);
const u8 *GetTrainerWonSpeech(void);
void UpdateRematchIfDefeated(s32 rematchTableId);
void ClearCurrentTrainerWantRematchVsSeeker(void);
void IncrementRematchStepCounter(void);
void TryUpdateRandomTrainerRematches(u16 mapGroup, u16 mapNum);
bool32 DoesSomeoneWantRematchIn(u16 mapGroup, u16 mapNum);
bool32 IsRematchTrainerIn(u16 mapGroup, u16 mapNum);
u16 GetLastBeatenRematchTrainerId(u16 trainerId);
bool8 ShouldTryRematchBattle(void);
bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId);
bool8 IsTrainerReadyForRematch(void);
void ShouldTryGetTrainerScript(void);
u16 CountBattledRematchTeams(u16 trainerId);

View File

@ -165,7 +165,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;
@ -241,7 +242,8 @@ void TryClearRageAndFuryCutter(void);
enum MoveCanceller AtkCanceller_MoveSuccessOrder(void);
void SetAtkCancellerForCalledMove(void);
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, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option);
bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType, enum FunctionCallOption option);
u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg);
@ -298,6 +300,7 @@ bool32 IsBattlerMegaEvolved(u32 battler);
bool32 IsBattlerPrimalReverted(u32 battler);
bool32 IsBattlerUltraBursted(u32 battler);
u16 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method);
bool32 TryRevertPartyMonFormChange(u32 partyIndex);
bool32 TryBattleFormChange(u32 battler, enum FormChanges method);
bool32 DoBattlersShareType(u32 battler1, u32 battler2);
bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId);
@ -310,6 +313,7 @@ u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pok
bool32 SetIllusionMon(struct Pokemon *mon, u32 battler);
u32 TryImmunityAbilityHealStatus(u32 battler, u32 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);
@ -329,7 +333,7 @@ bool32 IsPartnerMonFromSameTrainer(u32 battler);
enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, enum ItemCaseId caseID);
bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes);
void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast);
bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind);
bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind, u32 ability);
bool32 TryRoomService(u32 battler);
void BufferStatChange(u32 battler, u8 statId, enum StringID stringId);
bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget);
@ -411,5 +415,6 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander);
bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move);
bool32 HasPartnerTrainer(u32 battler);
#endif // GUARD_BATTLE_UTIL_H

View File

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

View File

@ -36,7 +36,7 @@
#define I_REPEL_LURE_MENU TRUE // If TRUE, the player is able to choose which Repel/Lure to use once the previous one runs out. Cursor position is saved by VAR_LAST_REPEL_LURE_USED if not 0.
// Vs. Seeker
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working.
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working. Documentation for the Vs. Seeker can be found in docs/tutorials/vs_seeker.md.
// Fishing
#define I_FISHING_BITE_ODDS GEN_LATEST // In Gen 1 and Gen 2, the Old Rod has a 100% chance for a bite, Good Rod has a 66% chance for a bite, and Super Rod has a 50% chance for a bite. In Gen 3, all rods have a base 50% chance for a bite. In Gen 4 onwards, the Old Rod has a base 25% chance for a bite, Good Rod has a 50% chance for a bite, and Super Rod has a 75% chance for a bite.

View File

@ -133,6 +133,7 @@
#define OW_POPUP_BW_TIME_MODE OW_POPUP_BW_TIME_NONE // Determines what type of time is shown.
#define OW_POPUP_BW_ALPHA_BLEND FALSE // Enables alpha blending/transparency for the pop-ups. Mainly intended to be used with the black color option.
// Setting this to TRUE will cause graphical errors with the Day Night System enabled.
// It will also cause minor visual glitches of shadow and reflection sprites adjusting their transparency when the pop-up disappear
// Pokémon Center
#define OW_IGNORE_EGGS_ON_HEAL GEN_LATEST // In Gen 4+, the nurse in the Pokémon Center does not heal Eggs on healing machine.
@ -142,4 +143,7 @@
// Berry Blender
#define BERRY_BLENDER_THROW_ALL_BERRIES_AT_ONCE TRUE // This is a small little addition, that basically speeds up the animation where all players' berries are thrown into the blender. Self-explanatory I hope!
// Trainer Rematches
#define OW_REMATCH_BADGE_COUNT 5 // Number of badges necessary before the match call or vs seeker features allow rematches
#endif // GUARD_CONFIG_OVERWORLD_H

View File

@ -8,6 +8,7 @@
#define P_SUMMARY_SCREEN_RENAME TRUE // If TRUE, an option to change Pokémon nicknames replaces the cancel prompt on the summary screen info page.
#define P_SUMMARY_SCREEN_IV_EV_INFO FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page.
#define P_SUMMARY_SCREEN_IV_EV_BOX_ONLY FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page, but only in the PC storage box.
#define P_SUMMARY_SCREEN_IV_HYPERTRAIN TRUE // If TRUE, stats that have been hyper trained will show as 31/S when viewing them in the summary screen
#define P_SUMMARY_SCREEN_IV_EV_TILESET FALSE // If TRUE, loads an alternate tileset to allow changing the "STATS" label in the summary screen skills page. Note: if it's still loading the alternate tileset after changing this and recompiling, you may need a `make clean` before compilation.
#define P_SUMMARY_SCREEN_IV_EV_VALUES FALSE // If TRUE, will show the actual IV value instead of the letter grade.
/*

View File

@ -99,6 +99,7 @@ enum BattlerId
#define RECORDED_WILD_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_RECORDED) && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FRONTIER)))
#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && TRAINER_BATTLE_PARAM.opponentB == 0xFFFF))
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER)
#define BATTLE_TYPE_PLAYER_HAS_PARTNER (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TOWER_LINK_MULTI)
// Battle Outcome defines
#define B_OUTCOME_WON 1
@ -159,7 +160,6 @@ enum VolatileFlags
F(VOLATILE_INFATUATION, infatuation, (enum BattlerId, MAX_BITS(4))) \
F(VOLATILE_DEFENSE_CURL, defenseCurl, (u32, 1)) \
F(VOLATILE_TRANSFORMED, transformed, (u32, 1)) \
F(VOLATILE_RECHARGE, recharge, (u32, 1)) \
F(VOLATILE_RAGE, rage, (u32, 1)) \
F(VOLATILE_SUBSTITUTE, substitute, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 1)) \

View File

@ -667,6 +667,14 @@
#define ANIM_ORDER_UP_DROOPY 2
#define ANIM_ORDER_UP_STRETCHY 3
// AnimTask_TransformMon variations
enum SpeciesGfxChange
{
SPECIES_GFX_CHANGE_TRANSFORM,
SPECIES_GFX_CHANGE_FORM_CHANGE,
SPECIES_GFX_CHANGE_ILLUSION_OFF,
};
// Flags given to various functions to indicate which palettes to consider.
// Handled by UnpackSelectedBattlePalettes
#define F_PAL_BG (1 << 0)

View File

@ -1,10 +1,10 @@
#ifndef GUARD_CONSTANTS_EXPANSION_H
#define GUARD_CONSTANTS_EXPANSION_H
// Last version: 1.13.2
// Last version: 1.13.3
#define EXPANSION_VERSION_MAJOR 1
#define EXPANSION_VERSION_MINOR 13
#define EXPANSION_VERSION_PATCH 3
#define EXPANSION_VERSION_PATCH 4
// FALSE if this this version of Expansion is not a tagged commit, i.e.
// it contains unreleased changes.

View File

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

View File

@ -3,11 +3,14 @@
#include "map_groups.h"
// Warps using this map will instead use the warp data stored in gSaveBlock1Ptr->dynamicWarp.
// Used for warps that need to change destinations, e.g. when stepping off an elevator.
#define MAP_DYNAMIC (0x7F | (0x7F << 8))
enum
{
// Warps using this map will instead use the warp data stored in gSaveBlock1Ptr->dynamicWarp.
// Used for warps that need to change destinations, e.g. when stepping off an elevator.
MAP_DYNAMIC = (0x7F | (0x7F << 8)),
#define MAP_UNDEFINED (0xFF | (0xFF << 8))
MAP_UNDEFINED = (0xFF | (0xFF << 8)),
};
#define MAP_GROUP(map) (map >> 8)
#define MAP_NUM(map) (map & 0xFF)

View File

@ -1,7 +1,9 @@
#ifndef GUARD_DODRIO_BERRY_PICKING_H
#define GUARD_DODRIO_BERRY_PICKING_H
void StartDodrioBerryPicking(u16 partyId, void (*exitCallback)(void));
#include "main.h"
void StartDodrioBerryPicking(u16 partyId, MainCallback exitCallback);
void IsDodrioInParty(void);
void ShowDodrioBerryPickingRecords(void);

67
include/gametypes.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef GUARD_GAMETYPES_H
#define GUARD_GAMETYPES_H
#include "gba/types.h"
//
// This header includes typedefs for fields that commonly appear throughout
// the codebase, and which ROM hacks might benefit from being able to widen.
//
// These typedefs include the underlying type in their name for two reasons:
//
// - Game Freak wasn't fully consistent about field widths throughout
// their codebase. For example, when Region Map Sections are persistently
// stored in savedata, they're stored as 8-bit values; but much of the
// codebase handles them as 16-bit values.
//
// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does*
// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the
// flash memory available for storing players' save files, leaving 2172
// bytes to spare within each of the game's two save files (primary and
// backup). These spare bytes are not contiguous: SaveBlock1 can only grow
// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest
// of the free space located after the player's PC-boxed Pokemon.
//
// With so little flash memory to spare, keeping track of how much space
// you're using is vital -- and so is arranging struct members to minimize
// compiler-inserted padding. It's easier to deal with this when you can
// see these types' widths at a glance.
//
// Accordingly, this file generally doesn't contain just single types, but
// rather families of types. For example, Region Map Sections are saved as
// u8s within the player's save file, but are sometimes handled as u16s or
// even s16s and ints; and so there are multiple typedefs for Map Sections
// corresponding to each of these underlying types, and each typedef has a
// name which indicates the underlying type.
//
// For a given family of typedefs, the smallest one should be considered
// the "real" or "canonical" type. Continuing with Map Sections as our
// example, the smallest type is an 8-bit integer, and so any values that
// can't fit in an 8-bit integer will be truncated and lost at some point
// within the codebase. Therefore mapsec_u8_t is the "canonical" type for
// Map Sections, and the larger typedefs just exist to describe situations
// where the game handles Map Sections inconsistently with that "canon."
//
// Map Sections are named areas that can appear in the region map. Each
// individual map can be assigned to a Map Section as appropriate. The
// possible values are in constants/region_map_sections.h.
//
// If you choose to widen Map Sections, be aware that Met Locations (below)
// are based on Map Sections and will also be widened.
typedef u8 mapsec_u8_t;
typedef u16 mapsec_u16_t;
typedef s16 mapsec_s16_t;
typedef s32 mapsec_s32_t;
// Met Locations for caught Pokemon use the same values as Map Sections,
// except that 0xFD, 0xFE, and 0xFF have special meanings.
//
// Because this value appears inside every Pokemon's data, widening it will
// consume a lot more space within flash memory. The space usage will be
// greater than you expect due to how Pokemon substructs are laid out; you
// would have to rearrange the substructs' contents in order to minimize
// how much more space a wider Met Location would consume.
typedef mapsec_u8_t metloc_u8_t;
#endif //GUARD_GAMETYPES_H

View File

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

View File

@ -169,7 +169,7 @@ struct MapHeader
/* 0x0C */ const struct MapConnections *connections;
/* 0x10 */ u16 music;
/* 0x12 */ u16 mapLayoutId;
/* 0x14 */ u8 regionMapSectionId;
/* 0x14 */ mapsec_u8_t regionMapSectionId;
/* 0x15 */ u8 cave;
/* 0x16 */ u8 weather;
/* 0x17 */ u8 mapType;

View File

@ -5,6 +5,7 @@
#include <limits.h>
#include "config/general.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines.
#include "gba/gba.h"
#include "gametypes.h"
#include "siirtc.h"
#include "fpmath.h"
#include "metaprogram.h"

View File

@ -226,7 +226,7 @@ typedef union // size = 0x24
/*0x04*/ u8 filler_04[2];
/*0x06*/ u16 itemIds[SMARTSHOPPER_NUM_ITEMS];
/*0x0C*/ u16 itemAmounts[SMARTSHOPPER_NUM_ITEMS];
/*0x12*/ u8 shopLocation;
/*0x12*/ mapsec_u8_t shopLocation;
/*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
/*0x1B*/ //u8 padding;
} smartshopperShow;
@ -241,7 +241,7 @@ typedef union // size = 0x24
/*0x0E*/ u16 species2;
/*0x10*/ u8 nBallsUsed;
/*0x11*/ u8 outcome;
/*0x12*/ u8 location;
/*0x12*/ mapsec_u8_t location;
/*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
/*0x1B*/ //u8 padding;
} pokemonTodayFailed;
@ -267,7 +267,7 @@ typedef union // size = 0x24
/*0x04*/ u16 caughtPoke;
/*0x06*/ u16 steps;
/*0x08*/ u16 species;
/*0x0A*/ u8 location;
/*0x0A*/ mapsec_u8_t location;
/*0x0B*/ u8 language;
/*0x0C*/ u8 filler_0C[7];
/*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
@ -282,7 +282,7 @@ typedef union // size = 0x24
/*0x04*/ u8 badgeCount;
/*0x05*/ u8 nSilverSymbols;
/*0x06*/ u8 nGoldSymbols;
/*0x07*/ u8 location;
/*0x07*/ mapsec_u8_t location;
/*0x08*/ u16 battlePoints;
/*0x0A*/ u16 mapLayoutId;
/*0x0C*/ u8 language;
@ -309,7 +309,7 @@ typedef union // size = 0x24
/*0x00*/ u8 kind;
/*0x01*/ bool8 active;
/*0x02*/ u16 item;
/*0x04*/ u8 location;
/*0x04*/ mapsec_u8_t location;
/*0x05*/ u8 language;
/*0x06*/ u16 mapLayoutId;
/*0x08*/ u8 filler_08[11];
@ -336,7 +336,7 @@ typedef union // size = 0x24
/*0x00*/ u8 kind;
/*0x01*/ bool8 active;
/*0x02*/ u16 lastOpponentSpecies;
/*0x04*/ u8 location;
/*0x04*/ mapsec_u8_t location;
/*0x05*/ u8 outcome;
/*0x06*/ u16 caughtMonBall;
/*0x08*/ u16 balls;
@ -505,7 +505,7 @@ struct GabbyAndTyData
/*2BA6*/ u16 mon2;
/*2BA8*/ u16 lastMove;
/*2BAA*/ u16 quote[1];
/*2BAC*/ u8 mapnum;
/*2BAC*/ mapsec_u8_t mapnum;
/*2BAD*/ u8 battleNum;
/*2BAE*/ u8 battleTookMoreThanOneTurn:1;
u8 playerLostAMon:1;

View File

@ -2,6 +2,7 @@
#define GUARD_ITEM_MENU_H
#include "item.h"
#include "main.h"
#include "menu_helpers.h"
enum {
@ -57,7 +58,7 @@ enum {
struct BagPosition
{
void (*exitCallback)(void);
MainCallback exitCallback;
u8 location;
u8 pocket;
u16 pocketSwitchArrowPos;
@ -69,7 +70,7 @@ extern struct BagPosition gBagPosition;
struct BagMenu
{
void (*newScreenCallback)(void);
MainCallback newScreenCallback;
u8 tilemapBuffer[BG_SCREEN_SIZE];
u8 spriteIds[ITEMMENUSPRITE_COUNT];
u8 windowIds[ITEMWIN_COUNT];
@ -106,16 +107,16 @@ void CB2_BagMenuFromStartMenu(void);
u8 GetItemListPosition(u8 pocketId);
bool8 UseRegisteredKeyItemOnField(void);
void CB2_GoToSellMenu(void);
void GoToBagMenu(u8 location, u8 pocket, void ( *exitCallback)());
void GoToBagMenu(u8 location, u8 pocket, MainCallback exitCallback);
void DoWallyTutorialBagMenu(void);
void ResetBagScrollPositions(void);
void ChooseBerryForMachine(void (*exitCallback)(void));
void ChooseBerryForMachine(MainCallback exitCallback);
void CB2_ChooseBerry(void);
void CB2_ChooseMulch(void);
void Task_FadeAndCloseBagMenu(u8 taskId);
void BagMenu_YesNo(u8 taskId, u8 windowType, const struct YesNoFuncTable *funcTable);
void UpdatePocketItemList(enum Pocket pocketId);
void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, void (*callback)(u8 taskId));
void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, TaskFunc callback);
void DisplayItemMessageOnField(u8 taskId, const u8 *string, TaskFunc callback);
void CloseItemMessage(u8 taskId);
void ItemMenu_RotomCatalog(u8 taskId);

View File

@ -1,6 +1,6 @@
#ifndef GUARD_LANDMARK_H
#define GUARD_LANDMARK_H
const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count);
const u8 *GetLandmarkName(mapsec_u8_t mapSection, u8 id, u8 count);
#endif // GUARD_LANDMARK_H

View File

@ -1,6 +1,8 @@
#ifndef GUARD_MAIL_H
#define GUARD_MAIL_H
#include "main.h"
#define IS_ITEM_MAIL(itemId) ((itemId == ITEM_ORANGE_MAIL \
|| itemId == ITEM_HARBOR_MAIL \
|| itemId == ITEM_GLITTER_MAIL \
@ -15,7 +17,7 @@
|| itemId == ITEM_RETRO_MAIL))
// mail.h
void ReadMail(struct Mail *mail, void (*exitCallback)(void), bool8 hasText);
void ReadMail(struct Mail *mail, MainCallback exitCallback, bool8 hasText);
// mail_data.h
void ClearAllMail(void);

View File

@ -135,8 +135,8 @@ enum MapType GetLastUsedWarpMapType(void);
bool8 IsMapTypeOutdoors(enum MapType mapType);
bool8 Overworld_MapTypeAllowsTeleportAndFly(enum MapType mapType);
bool8 IsMapTypeIndoors(enum MapType mapType);
u8 GetSavedWarpRegionMapSectionId(void);
u8 GetCurrentRegionMapSectionId(void);
mapsec_u8_t GetSavedWarpRegionMapSectionId(void);
mapsec_u8_t GetCurrentRegionMapSectionId(void);
enum MapBattleScene GetCurrentMapBattleScene(void);
void CleanupOverworldWindowsAndTilemaps(void);
bool32 IsOverworldLinkActive(void);

View File

@ -654,10 +654,10 @@ extern const struct Fusion *const gFusionTablePointers[NUM_SPECIES];
#if P_FUSION_FORMS
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
extern const u16 gKyurenWhiteSwapMoveTable[][2];
extern const u16 gKyuremWhiteSwapMoveTable[][2];
#endif //P_FAMILY_RESHIRAM
#if P_FAMILY_ZEKROM
extern const u16 gKyurenBlackSwapMoveTable[][2];
extern const u16 gKyuremBlackSwapMoveTable[][2];
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
#endif //P_FUSION_FORMS
@ -789,6 +789,7 @@ u32 GetSpeciesBaseDefense(u16 species);
u32 GetSpeciesBaseSpAttack(u16 species);
u32 GetSpeciesBaseSpDefense(u16 species);
u32 GetSpeciesBaseSpeed(u16 species);
u32 GetSpeciesBaseStat(u16 species, u32 statIndex);
const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species);
const u16 *GetSpeciesTeachableLearnset(u16 species);
const u16 *GetSpeciesEggMoves(u16 species);

View File

@ -17,7 +17,7 @@ struct PokenavMonListItem
struct PokenavMatchCallEntry
{
bool8 isSpecialTrainer;
u8 mapSec;
mapsec_u8_t mapSec;
u16 headerId;
};
@ -413,7 +413,7 @@ void FreeMatchCallSubstruct1(void);
int IsMatchCallListInitFinished(void);
int GetNumberRegistered(void);
struct PokenavMatchCallEntry *GetMatchCallList(void);
u16 GetMatchCallMapSec(int index);
mapsec_u16_t GetMatchCallMapSec(int index);
bool32 ShouldDrawRematchPokeballIcon(int index);
void ClearRematchPokeballIcon(u16 windowId, u32 tileOffset);
int GetMatchCallTrainerPic(int index);
@ -422,7 +422,7 @@ const u8 *GetMatchCallMessageText(int index, bool8 *newRematchRequest);
u16 GetMatchCallOptionCursorPos(void);
u16 GetMatchCallOptionId(int optionId);
void BufferMatchCallNameAndDesc(struct PokenavMatchCallEntry *matchCallEntry, u8 *str);
u8 GetMatchTableMapSectionId(int rematchIndex);
mapsec_u8_t GetMatchTableMapSectionId(int rematchIndex);
int GetIndexDeltaOfNextCheckPageDown(int index);
int GetIndexDeltaOfNextCheckPageUp(int index);
bool32 IsRematchEntryRegistered(int rematchIndex);

View File

@ -217,6 +217,7 @@ enum RandomTag
RNG_WRAP,
RNG_BALLTHROW_CRITICAL,
RNG_BALLTHROW_SHAKE,
RNG_PROTECT_FAIL,
};
#define RandomWeighted(tag, ...) \

View File

@ -1,6 +1,8 @@
#ifndef GUARD_RAYQUAZA_SCENE_H
#define GUARD_RAYQUAZA_SCENE_H
void DoRayquazaScene(u8 animId, bool8 endEarly, void (*exitCallback)(void));
#include "main.h"
void DoRayquazaScene(u8 animId, bool8 endEarly, MainCallback exitCallback);
#endif // GUARD_RAYQUAZA_SCENE_H

View File

@ -27,7 +27,7 @@ enum {
};
struct RegionMap {
/*0x000*/ u16 mapSecId;
/*0x000*/ mapsec_u16_t mapSecId;
/*0x002*/ u8 mapSecType;
/*0x003*/ u8 posWithinMapSec;
/*0x004*/ u8 mapSecName[20];
@ -100,14 +100,14 @@ void InitRegionMap(struct RegionMap *regionMap, bool8 zoomed);
u8 DoRegionMapInputCallback(void);
bool8 UpdateRegionMapZoom(void);
void FreeRegionMapIconResources(void);
u16 GetRegionMapSecIdAt(u16 x, u16 y);
mapsec_u16_t GetRegionMapSecIdAt(u16 x, u16 y);
void CreateRegionMapPlayerIcon(u16 tileTag, u16 paletteTag);
void CreateRegionMapCursor(u16 tileTag, u16 paletteTag);
bool32 IsEventIslandMapSecId(u8 mapSecId);
u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength);
u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId);
u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId);
u16 CorrectSpecialMapSecId(u16 mapSecId);
bool32 IsEventIslandMapSecId(mapsec_u8_t mapSecId);
u8 *GetMapName(u8 *dest, mapsec_u16_t regionMapId, u16 padLength);
u8 *GetMapNameGeneric(u8 *dest, mapsec_u16_t mapSecId);
u8 *GetMapNameHandleAquaHideout(u8 *dest, mapsec_u16_t mapSecId);
mapsec_u16_t CorrectSpecialMapSecId(mapsec_u16_t mapSecId);
void ShowRegionMapForPokedexAreaScreen(struct RegionMap *regionMap);
void PokedexAreaScreen_UpdateRegionMapVariablesAndVideoRegs(s16 x, s16 y);
void CB2_OpenFlyMap(void);

View File

@ -1,6 +1,8 @@
#ifndef GUARD_SAVE_H
#define GUARD_SAVE_H
#include "main.h"
// Each 4 KiB flash sector contains 3968 bytes of actual data followed by 116 bytes of SaveBlock3 and then 12 bytes of footer.
#define SECTOR_DATA_SIZE 3968
#define SAVE_BLOCK_3_CHUNK_SIZE 116
@ -87,7 +89,7 @@ extern u32 gSaveCounter;
extern struct SaveSector *gFastSaveSector;
extern u16 gIncrementalSectorId;
extern u16 gSaveFileStatus;
extern void (*gGameContinueCallback)(void);
extern MainCallback gGameContinueCallback;
extern struct SaveSectorLocation gRamSaveSectorLocations[];
extern struct SaveSector gSaveDataBuffer;

View File

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

View File

@ -143,7 +143,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a != _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
} while (0)
#define EXPECT_NE(a, b) \
@ -151,7 +151,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a == _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
} while (0)
#define EXPECT_LT(a, b) \
@ -159,7 +159,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a >= _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
} while (0)
#define EXPECT_LE(a, b) \
@ -167,7 +167,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a > _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
} while (0)
#define EXPECT_GT(a, b) \
@ -175,7 +175,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a <= _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
} while (0)
#define EXPECT_GE(a, b) \
@ -183,7 +183,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a < _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
} while (0)
struct Benchmark { s32 ticks; };

View File

@ -9,6 +9,7 @@ void MapResetTrainerRematches(u16 mapGroup, u16 mapNum);
void ClearRematchMovementByTrainerId(void);
u16 GetRematchTrainerIdVSSeeker(u16 trainerId);
bool32 IsVsSeekerEnabled(void);
void NativeVsSeekerRematchId(struct ScriptContext *ctx);
#define VSSEEKER_RECHARGE_STEPS 100

View File

@ -1,6 +1,7 @@
POKEMONGFXDIR := graphics/pokemon
OBJEVENTGFXDIR := graphics/object_events/pics
FLDEFFGFXDIR := graphics/field_effects/pics
BATINTGFXDIR := graphics/battle_interface
MISCGFXDIR := graphics/misc
$(OBJEVENTGFXDIR)/people/brendan/walking.4bpp: %.4bpp: %.png
@ -4934,3 +4935,18 @@ $(OBJEVENTGFXDIR)/misc/ball_%.4bpp: $(OBJEVENTGFXDIR)/misc/ball_%.png ; $(GFX) $
graphics/door_anims/battle_tower_multi_corridor.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 2 -mheight 4
$(BATINTGFXDIR)/healthbox_doubles_opponent.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 8 -mheight 4
$(BATINTGFXDIR)/healthbox_doubles_player.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 8 -mheight 4
$(BATINTGFXDIR)/healthbox_safari.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 8 -mheight 8
$(BATINTGFXDIR)/healthbox_singles_opponent.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 8 -mheight 4
$(BATINTGFXDIR)/healthbox_singles_player.4bpp: %.4bpp: %.png
$(GFX) $< $@ -mwidth 8 -mheight 8

View File

@ -5069,7 +5069,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (gBattleMons[battlerDef].speed > gBattleMons[battlerAtk].speed)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_GUARD_SPLIT:
case EFFECT_GUARD_SPLIT:
{
u32 atkDefense = gBattleMons[battlerAtk].defense;
u32 defDefense = gBattleMons[battlerDef].defense;
@ -5116,6 +5116,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))
@ -5589,6 +5590,9 @@ case EFFECT_GUARD_SPLIT:
}
else // consider move effects that hinder the target
{
if (IsAdditionalEffectBlocked(battlerAtk, aiData->abilities[battlerAtk], battlerDef, aiData->abilities[battlerDef]))
continue;
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_FLINCH:

View File

@ -1124,7 +1124,7 @@ bool32 ShouldSwitch(u32 battler)
if (IsDoubleBattle())
{
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
u32 partner = BATTLE_PARTNER(battler);
battlerIn1 = battler;
if (gAbsentBattlerFlags & (1u << partner))
battlerIn2 = battler;
@ -1275,7 +1275,7 @@ void ModifySwitchAfterMoveScoring(u32 battler)
if (IsDoubleBattle())
{
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
u32 partner = BATTLE_PARTNER(battler);
battlerIn1 = battler;
if (gAbsentBattlerFlags & (1u << partner))
battlerIn2 = battler;
@ -1323,7 +1323,7 @@ bool32 IsSwitchinValid(u32 battler)
// Edge case: See if partner already chose to switch into the same mon
if (IsDoubleBattle())
{
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
u32 partner = BATTLE_PARTNER(battler);
if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch
{
if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gAiLogicData->mostSuitableMonId[battler])

View File

@ -701,6 +701,17 @@ bool32 IsDamageMoveUnusable(struct DamageContext *ctx)
return FALSE;
}
bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, u32 abilityDef)
{
if (gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK)
return TRUE;
if (abilityDef == ABILITY_SHIELD_DUST && !IsMoldBreakerTypeAbility(battlerAtk, abilityAtk))
return TRUE;
return FALSE;
}
static inline s32 GetDamageByRollType(s32 dmg, enum DamageRollType rollType)
{
if (rollType == DMG_ROLL_LOWEST)
@ -897,7 +908,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
gBattleStruct->magnitudeBasePower = 70;
gBattleStruct->presentBasePower = 80;
struct DamageContext ctx;
struct DamageContext ctx = {0};
ctx.battlerAtk = battlerAtk;
ctx.battlerDef = battlerDef;
ctx.move = move;
@ -1030,6 +1041,49 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
{
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_ATK_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK))
return TRUE;
break;
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK))
return TRUE;
break;
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_DEF))
return TRUE;
break;
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_SPATK))
return TRUE;
break;
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_SPDEF))
return TRUE;
break;
case MOVE_EFFECT_EVS_MINUS_1:
case MOVE_EFFECT_EVS_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_EVASION))
return TRUE;
break;
case MOVE_EFFECT_ACC_MINUS_1:
case MOVE_EFFECT_ACC_MINUS_2:
if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ACC))
return TRUE;
break;
case MOVE_EFFECT_ATK_DEF_DOWN:
if (abilityAtk == ABILITY_CONTRARY && (BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK) || BattlerStatCanRise(battlerAtk, abilityAtk, STAT_DEF)))
return TRUE;
break;
case MOVE_EFFECT_DEF_SPDEF_DOWN:
if (abilityAtk == ABILITY_CONTRARY && (BattlerStatCanRise(battlerAtk, abilityAtk, STAT_DEF) || BattlerStatCanRise(battlerAtk, abilityAtk, STAT_SPDEF)))
return TRUE;
break;
case MOVE_EFFECT_ATK_PLUS_1:
case MOVE_EFFECT_ATK_PLUS_2:
if (BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK))
@ -1073,6 +1127,9 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
}
else // consider move effects that hinder the target
{
if (IsAdditionalEffectBlocked(battlerAtk, abilityAtk, battlerDef, abilityDef))
continue;
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_POISON:
@ -1107,7 +1164,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_ACC_MINUS_1:
case MOVE_EFFECT_EVS_MINUS_1:
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo != 1)
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo > 1)
return TRUE;
break;
case MOVE_EFFECT_ATK_MINUS_2:
@ -1117,7 +1174,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
case MOVE_EFFECT_SP_DEF_MINUS_2:
case MOVE_EFFECT_ACC_MINUS_2:
case MOVE_EFFECT_EVS_MINUS_2:
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo != 1)
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo > 1)
return TRUE;
break;
default:
@ -1174,7 +1231,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
case MOVE_EFFECT_ATK_DEF_DOWN:
case MOVE_EFFECT_DEF_SPDEF_DOWN:
if ((additionalEffect->self && abilityAtk != ABILITY_CONTRARY)
|| (noOfHitsToKo != 1 && abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move)))
|| (noOfHitsToKo > 1 && !additionalEffect->self && abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move)))
return TRUE;
break;
case MOVE_EFFECT_RECHARGE:
@ -1195,7 +1252,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
case MOVE_EFFECT_ACC_PLUS_2:
case MOVE_EFFECT_ALL_STATS_UP:
if ((additionalEffect->self && abilityAtk == ABILITY_CONTRARY)
|| (noOfHitsToKo != 1 && !(abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move))))
|| (noOfHitsToKo > 1 && !additionalEffect->self && !(abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move))))
return TRUE;
break;
default:
@ -3378,7 +3435,7 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability)
if (gBattleMons[battler].status1 & STATUS1_SLEEP && !HasMoveWithEffect(battler, EFFECT_SLEEP_TALK))
return TRUE;
if (gBattleMons[battler].volatiles.recharge || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0))
if (gDisableStructs[battler].rechargeTimer > 0 || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0))
return TRUE;
return FALSE;

View File

@ -2484,11 +2484,10 @@ void AnimTask_HideSwapSprite(u8 taskId)
case 0:
gTasks[taskId].data[11] = gSprites[spriteId].x; // Save battler position
gSprites[spriteId].x = -64; // hide it from screen to avoid the blip/glitch effect when swapping the sprite.
gTasks[taskId].data[10] = gBattleAnimArgs[0];
gTasks[taskId].data[0]++;
break;
case 1:
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gBattleAnimArgs[1]);
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, SPECIES_GFX_CHANGE_FORM_CHANGE);
GetBgDataForTransform(&animBg, gBattleAnimAttacker);
if (IsContest())
@ -2536,14 +2535,6 @@ void AnimTask_HideSwapSprite(u8 taskId)
break;
case 2:
gSprites[spriteId].x = gTasks[taskId].data[11]; // restores battler position
if (!IsContest())
{
if (!IsOnPlayerSide(gBattleAnimAttacker))
{
if (gTasks[taskId].data[10] == 0)
SetBattlerShadowSpriteCallback(gBattleAnimAttacker, gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies);
}
}
DestroyAnimVisualTask(taskId);
break;
@ -2596,7 +2587,6 @@ void AnimTask_TransformMon(u8 taskId)
SetAnimBgAttribute(2, BG_ANIM_MOSAIC, 1);
gTasks[taskId].data[10] = gBattleAnimArgs[0];
gTasks[taskId].data[11] = gBattleAnimArgs[1];
gTasks[taskId].data[0]++;
break;
case 1:
@ -2611,7 +2601,7 @@ void AnimTask_TransformMon(u8 taskId)
}
break;
case 2:
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gTasks[taskId].data[11]);
HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10]);
GetBgDataForTransform(&animBg, gBattleAnimAttacker);
if (IsContest())
@ -2680,7 +2670,7 @@ void AnimTask_TransformMon(u8 taskId)
{
if (!IsOnPlayerSide(gBattleAnimAttacker))
{
if (gTasks[taskId].data[10] == 0)
if (gTasks[taskId].data[10] == SPECIES_GFX_CHANGE_TRANSFORM)
SetBattlerShadowSpriteCallback(gBattleAnimAttacker, gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies);
}
}

View File

@ -454,6 +454,13 @@ void HandleInputChooseTarget(u32 battler)
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
gBattlerControllerFuncs[battler] = HandleInputChooseMove;
if (gBattleStruct->gimmick.playerSelect == 1 && gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE)
{
gBattleStruct->gimmick.playerSelect = 0;
gBattleStruct->zmove.viewing = TRUE;
ReloadMoveNames(battler);
}
TryToAddMoveInfoWindow();
DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1);
DoBounceEffect(battler, BOUNCE_MON, 7, 1);
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
@ -1725,6 +1732,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[] = _("分类:");
@ -1760,7 +1775,7 @@ static void MoveSelectionDisplayMoveDescription(u32 battler)
if (gCategoryIconSpriteId == 0xFF)
gCategoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 43, 64, 1);
StartSpriteAnim(&gSprites[gCategoryIconSpriteId], GetBattleMoveCategory(move));
StartSpriteAnim(&gSprites[gCategoryIconSpriteId], cat);
CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_FULL);
}
@ -2084,6 +2099,8 @@ void PlayerHandleChooseMove(u32 battler)
if (!IsGimmickTriggerSpriteActive())
gBattleStruct->gimmick.triggerSpriteId = 0xFF;
else if (!IsGimmickTriggerSpriteMatchingBattler(battler))
DestroyGimmickTriggerSprite();
if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable))
CreateGimmickTriggerSprite(battler);

View File

@ -361,7 +361,6 @@ static const struct ListMenuItem sVolatileStatusListItems[] =
{COMPOUND_STRING("无理取闹"), VOLATILE_TORMENT},
{COMPOUND_STRING("粉尘"), VOLATILE_POWDER},
{COMPOUND_STRING("变圆"), VOLATILE_DEFENSE_CURL},
{COMPOUND_STRING("无法动弹"), VOLATILE_RECHARGE},
{COMPOUND_STRING("愤怒"), VOLATILE_RAGE},
{COMPOUND_STRING("同命"), VOLATILE_DESTINY_BOND},
{COMPOUND_STRING("无法逃脱"), VOLATILE_ESCAPE_PREVENTION},

View File

@ -168,6 +168,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;
}
@ -300,13 +311,12 @@ static bool32 HandleEndTurnEmergencyExit(u32 battler)
&& IsBattlerAlive(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& CountUsablePartyMons(battler) > 0
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) // Not currently held by Sky Drop
{
gBattlerAbility = battler;
gLastUsedAbility = ability;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler))
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
BattleScriptExecute(BattleScript_EmergencyExitEnd2);
else
BattleScriptExecute(BattleScript_EmergencyExitWildEnd2);
@ -797,9 +807,9 @@ static bool32 HandleEndTurnSaltCure(u32 battler)
&& !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD))
{
if (IS_BATTLER_ANY_TYPE(battler, TYPE_STEEL, TYPE_WATER))
gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 4;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4;
else
gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 8;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE);

View File

@ -125,7 +125,7 @@ struct FactorySelectScreen
struct SwapScreenAction
{
u8 id;
void (*func)(u8 taskId);
TaskFunc func;
};
struct FactorySwapScreen
@ -253,7 +253,7 @@ static EWRAM_DATA u8 *sSwapMenuTilemapBuffer = NULL;
static EWRAM_DATA u8 *sSwapMonPicBgTilemapBuffer = NULL;
static struct FactorySelectScreen *sFactorySelectScreen;
static void (*sSwap_CurrentOptionFunc)(u8 taskId);
static TaskFunc sSwap_CurrentOptionFunc;
static struct FactorySwapScreen *sFactorySwapScreen;
COMMON_DATA u8 (*gFactorySelect_CurrentOptionFunc)(void) = NULL;
@ -886,7 +886,7 @@ static const struct SpriteTemplate sSpriteTemplate_Swap_MonPicBgAnim =
.callback = SpriteCallbackDummy
};
void static (*const sSwap_MenuOptionFuncs[])(u8 taskId) =
static const TaskFunc sSwap_MenuOptionFuncs[] =
{
Swap_OptionSummary,
Swap_OptionSwap,

View File

@ -633,15 +633,8 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
gBattleSpritesDataPtr->battlerData[battler].transformSpecies = species = GetGMaxTargetSpecies(species);
if (B_TRANSFORM_SHINY >= GEN_4)
{
personalityValue = gTransformedPersonalities[battler];
isShiny = gTransformedShininess[battler];
}
else
{
personalityValue = GetMonData(mon, MON_DATA_PERSONALITY);
}
personalityValue = gTransformedPersonalities[battler];
isShiny = gTransformedShininess[battler];
}
position = GetBattlerPosition(battler);
@ -913,7 +906,7 @@ void CopyBattleSpriteInvisibility(u8 battler)
gBattleSpritesDataPtr->battlerData[battler].invisible = gSprites[gBattlerSpriteIds[battler]].invisible;
}
void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool8 trackEnemyPersonality)
void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, u8 changeType)
{
u32 personalityValue, position, paletteOffset, targetSpecies;
bool32 isShiny;
@ -947,24 +940,23 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo
targetSpecies = GetIllusionMonSpecies(battlerDef);
else
targetSpecies = GetMonData(monDef, MON_DATA_SPECIES);
personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY);
isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY);
gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies;
}
else
{
targetSpecies = gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies;
if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality && !megaEvo)
{
personalityValue = GetMonData(monDef, MON_DATA_PERSONALITY);
isShiny = GetMonData(monDef, MON_DATA_IS_SHINY);
}
else
{
personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY);
isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY);
}
}
if (changeType == SPECIES_GFX_CHANGE_TRANSFORM)
{
personalityValue = gDisableStructs[battlerAtk].transformedMonPersonality;
isShiny = gDisableStructs[battlerAtk].transformedMonShininess;
}
else
{
personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY);
isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY);
}
HandleLoadSpecialPokePic(!IsOnPlayerSide(battlerAtk),
gMonSpritesGfxPtr->spritesGfx[position],
targetSpecies,
@ -977,14 +969,10 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo
paletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, isShiny, personalityValue);
LoadPalette(paletteData, paletteOffset, PLTT_SIZE_4BPP);
if (!megaEvo)
if (changeType == SPECIES_GFX_CHANGE_TRANSFORM)
{
BlendPalette(paletteOffset, 16, 6, RGB_WHITE);
CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16));
if (!IsContest())
{
gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies;
}
}
// dynamax tint
@ -998,6 +986,13 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo
CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16));
}
// Terastallization's tint
if (changeType != SPECIES_GFX_CHANGE_ILLUSION_OFF && GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
{
BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battlerAtk)));
CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16));
}
gSprites[gBattlerSpriteIds[battlerAtk]].y = GetBattlerSpriteDefault_Y(battlerAtk);
StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], 0);
}

View File

@ -178,6 +178,13 @@ bool32 IsGimmickTriggerSpriteActive(void)
return FALSE;
}
bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler)
{
if (battler == gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler)
return TRUE;
return FALSE;
}
void HideGimmickTriggerSprite(void)
{
if (gBattleStruct->gimmick.triggerSpriteId != 0xFF)

View File

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

View File

@ -244,7 +244,7 @@ EWRAM_DATA u8 gPartyCriticalHits[PARTY_SIZE] = {0};
EWRAM_DATA static u8 sTriedEvolving = 0;
EWRAM_DATA u8 gCategoryIconSpriteId = 0;
COMMON_DATA void (*gPreBattleCallback1)(void) = NULL;
COMMON_DATA MainCallback gPreBattleCallback1 = NULL;
COMMON_DATA void (*gBattleMainFunc)(void) = NULL;
COMMON_DATA struct BattleResults gBattleResults = {0};
COMMON_DATA u8 gLeveledUpInBattle = 0;
@ -3002,7 +3002,10 @@ static void ClearSetBScriptingStruct(void)
memset(&gBattleScripting, 0, sizeof(gBattleScripting));
gBattleScripting.windowsType = temp;
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
if (TESTING)
gBattleScripting.battleStyle = OPTIONS_BATTLE_STYLE_SET;
else
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6);
gBattleScripting.specialTrainerBattleType = specialBattleType;
}
@ -3227,7 +3230,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;
@ -3235,6 +3238,9 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy)
gBattleStruct->battlerState[battler].stompingTantrumTimer = 0;
gBattleStruct->palaceFlags &= ~(1u << battler);
gBattleStruct->battlerState[battler].canPickupItem = FALSE;
gBattleStruct->hazardsCounter = 0;
gDisableStructs[battler].hazardsDone = FALSE;
gSpecialStatuses[battler].switchInItemDone = FALSE;
ClearPursuitValuesIfSet(battler);
@ -3348,7 +3354,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;
@ -4207,7 +4213,7 @@ static void HandleTurnActionSelectionState(void)
else
{
if (gBattleMons[battler].volatiles.multipleTurns
|| gBattleMons[battler].volatiles.recharge)
|| gDisableStructs[battler].rechargeTimer > 0)
{
gChosenActionByBattler[battler] = B_ACTION_USE_MOVE;
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
@ -4362,7 +4368,7 @@ static void HandleTurnActionSelectionState(void)
gBattleCommunication[GetPartnerBattler(battler)] = STATE_BEFORE_ACTION_CHOSEN;
RecordedBattle_ClearBattlerAction(battler, 1);
if (gBattleMons[GetPartnerBattler(battler)].volatiles.multipleTurns
|| gBattleMons[GetPartnerBattler(battler)].volatiles.recharge)
|| gDisableStructs[GetPartnerBattler(battler)].rechargeTimer > 0)
{
BtlController_EmitEndBounceEffect(battler, B_COMM_TO_CONTROLLER);
MarkBattlerForControllerExec(battler);
@ -4770,7 +4776,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
&& 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
@ -5126,11 +5132,8 @@ static void TurnValuesCleanUp(bool8 var0)
gDisableStructs[i].isFirstTurn--;
if (gDisableStructs[i].rechargeTimer)
{
gDisableStructs[i].rechargeTimer--;
if (gDisableStructs[i].rechargeTimer == 0)
gBattleMons[i].volatiles.recharge = FALSE;
}
gBattleStruct->battlerState[i].canPickupItem = FALSE;
}
@ -5624,17 +5627,7 @@ static void HandleEndTurn_FinishBattle(void)
for (i = 0; i < PARTY_SIZE; i++)
{
bool8 changedForm = FALSE;
// Appeared in battle and didn't faint
if ((gBattleStruct->appearedInBattle & (1u << i)) && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0)
changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT);
if (!changedForm)
changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE);
// Clear original species field
gBattleStruct->partyState[B_SIDE_PLAYER][i].changedSpecies = SPECIES_NONE;
bool8 changedForm = TryRevertPartyMonFormChange(i);
gBattleStruct->partyState[B_SIDE_OPPONENT][i].changedSpecies = SPECIES_NONE;
// Recalculate the stats of every party member before the end
@ -5672,6 +5665,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
else
gSaveBlock3Ptr->dexNavChain = 0;
ClearCurrentTrainerWantRematchVsSeeker();
gDexNavSpecies = SPECIES_NONE;
ResetSpriteData();
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK

View File

@ -303,7 +303,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_PKMNTRYINGTOTAKEFOE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}\n想和对手同归于尽!"),
[STRINGID_PKMNTOOKFOE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}\n和对手同归于尽了!"),
[STRINGID_PKMNREDUCEDPP] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}的PP减少了"),
[STRINGID_PKMNSTOLEITEM] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}\n从{B_DEF_NAME_WITH_PREFIX2}那里\l夺取了{B_LAST_ITEM}"),
[STRINGID_PKMNSTOLEITEM] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}\n从{B_EFF_NAME_WITH_PREFIX2}那里\l夺取了{B_LAST_ITEM}"),
[STRINGID_TARGETCANTESCAPENOW] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}\n无法逃走了!"),
[STRINGID_PKMNFELLINTONIGHTMARE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}\n开始做恶梦了!"),
[STRINGID_PKMNLOCKEDINNIGHTMARE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}\n正被恶梦缠身!"),
@ -705,7 +705,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_RECEIVERABILITYTAKEOVER] = COMPOUND_STRING("继承了{B_SCR_NAME_WITH_PREFIX}的\n{B_SCR_ABILITY}"),
[STRINGID_PKNMABSORBINGPOWER] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}\n正在积蓄力量!"),
[STRINGID_NOONEWILLBEABLETORUNAWAY] = COMPOUND_STRING("下回合无法逃走!"),
[STRINGID_DESTINYKNOTACTIVATES] = COMPOUND_STRING("{B_LAST_ITEM}让{B_SCR_NAME_WITH_PREFIX}\n着迷了!"),
[STRINGID_DESTINYKNOTACTIVATES] = COMPOUND_STRING("{B_LAST_ITEM}让{B_DEF_NAME_WITH_PREFIX}\n着迷了!"),
[STRINGID_CLOAKEDINAFREEZINGLIGHT] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}\n被冷光包围了!"),
[STRINGID_CLEARAMULETWONTLOWERSTATS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX2}的\n能力因{B_LAST_ITEM}不会降低!"),
[STRINGID_FERVENTWISHREACHED] = COMPOUND_STRING("{B_ATK_TRAINER_NAME}衷心的祈愿\n传递到了{B_ATK_NAME_WITH_PREFIX2}那里!"),

View File

@ -16,7 +16,6 @@
#include "item_use.h"
#include "list_menu.h"
#include "mail.h"
#include "main.h"
#include "malloc.h"
#include "menu.h"
#include "menu_helpers.h"
@ -414,7 +413,7 @@ void CB2_ReturnToPyramidBagMenu(void)
GoToBattlePyramidBagMenu(PYRAMIDBAG_LOC_PREV, gPyramidBagMenuState.exitCallback);
}
void GoToBattlePyramidBagMenu(u8 location, void (*exitCallback)(void))
void GoToBattlePyramidBagMenu(u8 location, MainCallback exitCallback)
{
gPyramidBagMenu = AllocZeroed(sizeof(*gPyramidBagMenu));
@ -1526,7 +1525,7 @@ static void CreatePyramidBagYesNo(u8 taskId, const struct YesNoFuncTable *yesNoT
CreateYesNoMenuWithCallbacks(taskId, &sWindowTemplates_MenuActions[MENU_WIN_YESNO], 1, 0, 2, 1, 0xE, yesNoTable);
}
void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, void (*callback)(u8 taskId))
void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, TaskFunc callback)
{
FillWindowPixelBuffer(WIN_MSG, PIXEL_FILL(1));
DisplayMessageAndContinueTask(taskId, WIN_MSG, 0xA, 0xD, FONT_NORMAL, GetPlayerTextSpeedDelay(), str, callback);

View File

@ -370,7 +370,7 @@ static void Cmd_jumpifvolatile(void);
static void Cmd_jumpifability(void);
static void Cmd_jumpifsideaffecting(void);
static void Cmd_jumpifstat(void);
static void Cmd_unused_0x21(void);
static void Cmd_jumpifstatignorecontrary(void);
static void Cmd_jumpbasedontype(void);
static void Cmd_getexp(void);
static void Cmd_checkteamslost(void);
@ -629,7 +629,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_jumpifability, //0x1E
Cmd_jumpifsideaffecting, //0x1F
Cmd_jumpifstat, //0x20
Cmd_unused_0x21, //0x21
Cmd_jumpifstatignorecontrary, //0x21
Cmd_jumpbasedontype, //0x22
Cmd_getexp, //0x23
Cmd_checkteamslost, //0x24
@ -1079,7 +1079,6 @@ bool32 EmergencyExitCanBeTriggered(u32 battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& CountUsablePartyMons(battler) > 0
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP)
return TRUE;
@ -1541,7 +1540,7 @@ static void Cmd_ppreduce(void)
// For item Metronome, echoed voice
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker))
gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0;
gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0;
if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct)
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct;
@ -1807,7 +1806,7 @@ static void Cmd_damagecalc(void)
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
struct DamageContext ctx;
struct DamageContext ctx = {0};
ctx.battlerAtk = gBattlerAttacker;
ctx.move = gCurrentMove;
ctx.moveType = GetBattleMoveType(gCurrentMove);
@ -2832,11 +2831,11 @@ static void CheckSetUnburden(u8 battler)
gDisableStructs[battler].unburdenActive = TRUE;
}
// battlerStealer steals the item of battlerItem
void StealTargetItem(u8 battlerStealer, u8 battlerItem)
// battlerStealer steals the item of itemBattler
void StealTargetItem(u8 battlerStealer, u8 itemBattler)
{
gLastUsedItem = gBattleMons[battlerItem].item;
gBattleMons[battlerItem].item = ITEM_NONE;
gLastUsedItem = gBattleMons[itemBattler].item;
gBattleMons[itemBattler].item = ITEM_NONE;
if (GetGenConfig(GEN_STEAL_WILD_ITEMS) >= GEN_9
&& !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE))
@ -2855,16 +2854,16 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem)
MarkBattlerForControllerExec(battlerStealer);
}
RecordItemEffectBattle(battlerItem, ITEM_NONE);
CheckSetUnburden(battlerItem);
RecordItemEffectBattle(itemBattler, ITEM_NONE);
CheckSetUnburden(itemBattler);
BtlController_EmitSetMonData(battlerItem, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item
MarkBattlerForControllerExec(battlerItem);
BtlController_EmitSetMonData(itemBattler, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[itemBattler].item), &gBattleMons[itemBattler].item); // remove target item
MarkBattlerForControllerExec(itemBattler);
if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
if (GetBattlerAbility(itemBattler) != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[itemBattler] = MOVE_NONE;
TrySaveExchangedItem(battlerItem, gLastUsedItem);
TrySaveExchangedItem(itemBattler, gLastUsedItem);
}
static inline bool32 TrySetReflect(u32 battler)
@ -3315,7 +3314,6 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
if (B_SKIP_RECHARGE == GEN_1 && !IsBattlerAlive(gBattlerTarget)) // Skip recharge if gen 1 and foe is KO'd
break;
gBattleMons[gEffectBattler].volatiles.recharge = TRUE;
gDisableStructs[gEffectBattler].rechargeTimer = 2;
gLockedMoves[gEffectBattler] = gCurrentMove;
gBattlescriptCurrInstr++;
@ -3831,7 +3829,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);
@ -4433,7 +4431,7 @@ static void Cmd_jumpifstat(void)
u8 value = cmd->value;
u8 comparison = cmd->comparison;
ret = CompareStat(battler, stat, value, comparison);
ret = CompareStat(battler, stat, value, comparison, GetBattlerAbility(battler));
if (ret)
gBattlescriptCurrInstr = cmd->jumpInstr;
@ -4441,8 +4439,22 @@ static void Cmd_jumpifstat(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_unused_0x21(void)
static void Cmd_jumpifstatignorecontrary(void)
{
CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr);
bool32 ret = 0;
u8 battler = GetBattlerForBattleScript(cmd->battler);
u8 stat = cmd->stat;
u8 value = cmd->value;
u8 comparison = cmd->comparison;
ret = CompareStat(battler, stat, value, comparison, ABILITY_NONE);
if (ret)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_jumpbasedontype(void)
@ -4773,7 +4785,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);
}
@ -5577,22 +5597,54 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
switch (abilityAtk)
{
case ABILITY_MAGICIAN:
if (move != MOVE_FLING && move != MOVE_NATURAL_GIFT
if (GetMoveEffect(move) != EFFECT_FLING
&& GetMoveEffect(move) != EFFECT_NATURAL_GIFT
&& gBattleMons[battlerAtk].item == ITEM_NONE
&& gBattleMons[battlerDef].item != ITEM_NONE
&& IsBattlerAlive(battlerAtk)
&& IsBattlerTurnDamaged(battlerDef)
&& CanStealItem(battlerAtk, battlerDef, gBattleMons[battlerDef].item)
&& !gSpecialStatuses[battlerAtk].gemBoost // In base game, gems are consumed after magician would activate.
&& !(gWishFutureKnock.knockedOffMons[GetBattlerSide(battlerDef)] & (1u << gBattlerPartyIndexes[battlerDef]))
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
&& (GetBattlerAbility(battlerDef) != ABILITY_STICKY_HOLD || !IsBattlerAlive(battlerDef)))
&& !gSpecialStatuses[battlerAtk].gemBoost) // In base game, gems are consumed after magician would activate.
{
StealTargetItem(battlerAtk, battlerDef);
gBattleScripting.battler = gBattlerAbility = battlerAtk;
gEffectBattler = battlerDef;
BattleScriptCall(BattleScript_MagicianActivates);
effect = TRUE;
u32 numMagicianTargets = 0;
u32 magicianTargets = 0;
for (u32 i = 0; i < gBattlersCount; i++)
{
if (gBattleMons[i].item != ITEM_NONE
&& i != battlerAtk
&& IsBattlerTurnDamaged(i)
&& CanStealItem(battlerAtk, i, gBattleMons[i].item)
&& !(gWishFutureKnock.knockedOffMons[GetBattlerSide(i)] & (1u << gBattlerPartyIndexes[i]))
&& !DoesSubstituteBlockMove(battlerAtk, i, move)
&& (GetBattlerAbility(i) != ABILITY_STICKY_HOLD || !IsBattlerAlive(i)))
{
magicianTargets |= 1u << i;
numMagicianTargets++;
}
}
if (numMagicianTargets == 0)
{
effect = FALSE;
break;
}
u8 battlers[4] = {0, 1, 2, 3};
if (numMagicianTargets > 1)
SortBattlersBySpeed(battlers, FALSE);
for (u32 i = 0; i < gBattlersCount; i++)
{
u32 battler = battlers[i];
if (!(magicianTargets & 1u << battler))
continue;
StealTargetItem(battlerAtk, battler);
gBattlerAbility = battlerAtk;
gEffectBattler = battler;
BattleScriptCall(BattleScript_MagicianActivates);
effect = TRUE;
break; // found target to steal from
}
}
break;
case ABILITY_MOXIE:
@ -5613,7 +5665,7 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
else if (abilityAtk == ABILITY_GRIM_NEIGH || abilityAtk == ABILITY_AS_ONE_SHADOW_RIDER)
stat = STAT_SPATK;
if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gLastUsedAbility = abilityAtk;
if (abilityAtk == ABILITY_AS_ONE_ICE_RIDER)
@ -5653,17 +5705,17 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
else
{
u32 numStatBuffs = 0;
if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_ATK) + STAT_ANIM_PLUS1;
numStatBuffs++;
}
if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPATK) + STAT_ANIM_PLUS1;
numStatBuffs++;
}
if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk))
{
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPEED) + STAT_ANIM_PLUS1;
numStatBuffs++;
@ -5694,26 +5746,29 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
return FALSE;
u32 effect = FALSE;
u32 side = GetBattlerSide(gBattlerTarget);
switch (moveEffect)
{
case EFFECT_KNOCK_OFF:
if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff
&& gBattleMons[gBattlerTarget].item != ITEM_NONE
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker))
&& IsBattlerAlive(gBattlerAttacker)
&& !(B_KNOCK_OFF_REMOVAL >= GEN_5 && side == B_SIDE_PLAYER && !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)))
{
u32 side = GetBattlerSide(gBattlerTarget);
gLastUsedItem = gBattleMons[gBattlerTarget].item;
gBattleMons[gBattlerTarget].item = 0;
if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
CheckSetUnburden(gBattlerTarget);
// In Gen 5+, Knock Off removes the target's item rather than rendering it unusable.
// In Gen 5+, Knock Off removes the target's item rather than rendering it unusable
if (B_KNOCK_OFF_REMOVAL >= GEN_5)
{
BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item);
MarkBattlerForControllerExec(gBattlerTarget);
// Mark item as stolen so it will be restored after battle
gBattleStruct->itemLost[side][gBattlerPartyIndexes[gBattlerTarget]].stolen = TRUE;
}
else
{
@ -5752,6 +5807,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS)
gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later
}
gEffectBattler = gBattlerTarget;
BattleScriptCall(BattleScript_ItemSteal);
effect = TRUE;
}
@ -5828,6 +5884,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
}
break;
case EFFECT_EXPLOSION:
case EFFECT_MISTY_EXPLOSION:
if (!IsAbilityOnField(ABILITY_DAMP))
{
gBattleStruct->moveDamage[gBattlerAttacker] = 0;
@ -5854,7 +5911,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
BattleScriptCall(BattleScript_RapidSpinAway);
effect = TRUE;
@ -5865,7 +5922,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker)))
{
SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
@ -5875,7 +5932,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
}
break;
case EFFECT_STONE_AXE:
if (!IsHazardOnSide(GetBattlerSide(gBattlerTarget), HAZARDS_STEALTH_ROCK)
if (!IsHazardOnSide(side, HAZARDS_STEALTH_ROCK)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker))
{
@ -5886,7 +5943,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
}
break;
case EFFECT_CEASELESS_EDGE:
if (gSideTimers[GetBattlerSide(gBattlerTarget)].spikesAmount < 3
if (gSideTimers[side].spikesAmount < 3
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker))
{
@ -6095,7 +6152,7 @@ static void Cmd_moveend(void)
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !IsBattleMoveStatus(gCurrentMove)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
{
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptCall(BattleScript_RageIsBuilding);
@ -6511,7 +6568,7 @@ static void Cmd_moveend(void)
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_THROAT_SPRAY
&& IsBattlerAlive(gBattlerAttacker)
&& IsAnyTargetAffected(gBattlerAttacker)
&& CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker))
&& !NoAliveMonsForEitherParty()) // Don't activate if battle will end
{
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
@ -6684,7 +6741,7 @@ static void Cmd_moveend(void)
effect = TRUE;
gBattleScripting.battler = battler;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler))
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
BattleScriptCall(BattleScript_EmergencyExit);
else
BattleScriptCall(BattleScript_EmergencyExitWild);
@ -6882,9 +6939,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.
@ -6938,6 +6995,8 @@ static void Cmd_moveend(void)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
if (B_CHARGE >= GEN_9 && moveType == TYPE_ELECTRIC && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
gBattleMons[gBattlerAttacker].volatiles.charge = FALSE;
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
@ -7878,7 +7937,6 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp;
}
gDisableStructs[battler].hazardsDone = FALSE;
gBattleStruct->battlerState[battler].forcedSwitch = FALSE;
return FALSE;
}
@ -9562,7 +9620,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))
{
@ -9591,6 +9649,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;
@ -9729,7 +9788,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;
@ -11158,7 +11217,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;
@ -11190,7 +11250,10 @@ static void Cmd_transformdataexecution(void)
gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE;
gDisableStructs[gBattlerAttacker].disableTimer = 0;
gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality;
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny;
if (B_TRANSFORM_SHINY >= GEN_4)
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny;
else
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerAttacker].isShiny;
gDisableStructs[gBattlerAttacker].mimickedMoves = 0;
gDisableStructs[gBattlerAttacker].usedMoves = 0;
@ -12075,7 +12138,7 @@ static void Cmd_jumpifconfusedandstatmaxed(void)
CMD_ARGS(u8 stat, const u8 *jumpInstr);
if (gBattleMons[gBattlerTarget].volatiles.confusionTurns > 0
&& !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN))
&& !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
gBattlescriptCurrInstr = cmd->jumpInstr; // Fails if we're confused AND stat cannot be raised
else
gBattlescriptCurrInstr = cmd->nextInstr;
@ -14127,6 +14190,8 @@ static void Cmd_givecaughtmon(void)
}
else
{
//Before sending to PC, we revert battle form
TryRevertPartyMonFormChange(gSelectedMonPartyId);
// Mon chosen, try to put it in the PC
if (CopyMonToPC(&gPlayerParty[gSelectedMonPartyId]) == MON_GIVEN_TO_PC)
{
@ -15285,34 +15350,56 @@ void BS_SetTerrain(void)
switch (GetMoveEffect(gCurrentMove))
{
case EFFECT_MISTY_TERRAIN:
statusFlag = STATUS_FIELD_MISTY_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY;
if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))
{
statusFlag = STATUS_FIELD_MISTY_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY;
}
break;
case EFFECT_GRASSY_TERRAIN:
statusFlag = STATUS_FIELD_GRASSY_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY;
if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN))
{
statusFlag = STATUS_FIELD_GRASSY_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY;
}
break;
case EFFECT_ELECTRIC_TERRAIN:
statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC;
if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN))
{
statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC;
}
break;
case EFFECT_PSYCHIC_TERRAIN:
statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC;
if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN))
{
statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC;
}
break;
case EFFECT_HIT_SET_TERRAIN:
statusFlag = GetMoveTerrainFlag(gCurrentMove);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC;
if (!(gFieldStatuses & GetMoveTerrainFlag(gCurrentMove)))
{
statusFlag = GetMoveTerrainFlag(gCurrentMove);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC;
}
break;
default:
break;
}
enum ItemHoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses |= statusFlag;
gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
gBattlescriptCurrInstr = cmd->nextInstr;
if (gBattleStruct->isSkyBattle)
{
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else if (statusFlag)
{
TryChangeBattleTerrain(gBattlerAttacker, statusFlag);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->jumpInstr;
}
}
void BS_JumpIfTerrainAffected(void)
@ -15674,9 +15761,7 @@ void BS_TryAllySwitch(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) || HasPartnerTrainer(gBattlerAttacker))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -15980,7 +16065,7 @@ void BS_CanTarShotWork(void)
NATIVE_ARGS(const u8 *failInstr);
// Tar Shot will fail if it's already been used on the target or if its speed can't be lowered further
if (!gDisableStructs[gBattlerTarget].tarShot
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = cmd->failInstr;
@ -16795,15 +16880,6 @@ void BS_JumpIfNoAlly(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_InfatuateWithBattler(void)
{
NATIVE_ARGS(u8 battler, u8 infatuateWith);
u32 battler = GetBattlerForBattleScript(cmd->battler);
gBattleScripting.battler = battler;
gBattleMons[battler].volatiles.infatuation = INFATUATED_WITH(GetBattlerForBattleScript(cmd->infatuateWith));
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_SetLastUsedItem(void)
{
NATIVE_ARGS(u8 battler);
@ -16934,7 +17010,7 @@ void BS_TryAcupressure(void)
u32 bits = 0;
for (u32 stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++)
{
if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget)))
bits |= 1u << stat;
}
if (bits)
@ -17064,11 +17140,14 @@ void BS_ArenaJudgmentWindow(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void SetArenMonLostValues(u32 battler)
static void SetArenMonLostValues(u32 battler, u32 side)
{
gBattleMons[battler].hp = 0;
gHitMarker |= HITMARKER_FAINTED(battler);
gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler];
if (side == B_SIDE_PLAYER)
gBattleStruct->arenaLostPlayerMons |= 1u << gBattlerPartyIndexes[battler];
else
gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler];
gDisableStructs[battler].truantSwitchInHack = TRUE;
}
@ -17077,22 +17156,22 @@ static void SetArenMonLostValues(u32 battler)
void BS_ArenaOpponentMonLost(void)
{
NATIVE_ARGS();
SetArenMonLostValues(opponentMon);
SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_ArenaPlayerMonLost(void)
{
NATIVE_ARGS();
SetArenMonLostValues(playerMon);
SetArenMonLostValues(playerMon, B_SIDE_PLAYER);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_ArenaBothMonsLost(void)
{
NATIVE_ARGS();
SetArenMonLostValues(playerMon);
SetArenMonLostValues(opponentMon);
SetArenMonLostValues(playerMon, B_SIDE_PLAYER);
SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT);
gBattlescriptCurrInstr = cmd->nextInstr;
}
#undef playerMon
@ -17122,13 +17201,12 @@ void BS_EraseArenaRefTextBox(void)
void BS_ArenaJudgmentString(void)
{
CMD_ARGS(u8 id);
NATIVE_ARGS(u8 id);
BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[cmd->id]);
BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGMENT_TEXT);
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Argument passed but no use
void BS_ArenaWaitMessage(void)
{
NATIVE_ARGS();
@ -17331,10 +17409,11 @@ void BS_TryActivateSoulheart(void)
while (gBattleStruct->soulheartBattlerId < gBattlersCount)
{
gBattleScripting.battler = gBattleStruct->soulheartBattlerId++;
if (GetBattlerAbility(gBattleScripting.battler) == ABILITY_SOUL_HEART
u32 ability = GetBattlerAbility(gBattleScripting.battler);
if (ability == ABILITY_SOUL_HEART
&& IsBattlerAlive(gBattleScripting.battler)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, ability))
{
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK);
@ -18211,19 +18290,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)
@ -18244,10 +18321,11 @@ void BS_CutOneThirdHpAndRaiseStats(void)
bool8 atLeastOneStatBoosted = FALSE;
u16 hpFraction = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3);
u32 ability = GetBattlerAbility(gBattlerAttacker);
for (u32 stat = 1; stat < NUM_STATS; stat++)
{
if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN, ability))
{
atLeastOneStatBoosted = TRUE;
break;

View File

@ -1125,6 +1125,17 @@ bool32 GetTrainerFlagFromScriptPointer(const u8 *data)
TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET);
return FlagGet(TRAINER_FLAGS_START + temp->params.opponentA);
}
bool32 GetRematchFromScriptPointer(const u8 *data)
{
#if FREE_MATCH_CALL
return FALSE;
#else
TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET);
return ShouldTryRematchBattleForTrainerId(temp->params.opponentA);
#endif
}
#undef OPCODE_OFFSET
// Set trainer's movement type so they stop and remain facing that direction
@ -1741,6 +1752,20 @@ static void ClearTrainerWantRematchState(const struct RematchTrainer *table, u16
#endif //FREE_MATCH_CALL
}
void ClearCurrentTrainerWantRematchVsSeeker(void)
{
#if FREE_MATCH_CALL == FALSE
if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && FlagGet(I_VS_SEEKER_CHARGING) && (I_VS_SEEKER_CHARGING != 0))
{
for (u32 i = 0; i < REMATCH_TABLE_ENTRIES; i++)
{
if (gSaveBlock1Ptr->trainerRematches[i] == TRAINER_BATTLE_PARAM.opponentA)
gSaveBlock1Ptr->trainerRematches[i] = 0;
}
}
#endif //FREE_MATCH_CALL
}
static u32 GetTrainerMatchCallFlag(u32 trainerId)
{
s32 i;
@ -1772,12 +1797,18 @@ static bool8 WasSecondRematchWon(const struct RematchTrainer *table, u16 firstBa
return FALSE;
if (!HasTrainerBeenFought(table[tableId].trainerIds[1]))
return FALSE;
#if FREE_MATCH_CALL == FALSE
if (I_VS_SEEKER_CHARGING)
{
if (gSaveBlock1Ptr->trainerRematches[tableId] == 0)
return FALSE;
}
#endif
return TRUE;
}
#if FREE_MATCH_CALL == FALSE
static bool32 HasAtLeastFiveBadges(void)
static bool32 HasEnoughBadgesForRematch(void)
{
s32 i, count;
@ -1785,7 +1816,7 @@ static bool32 HasAtLeastFiveBadges(void)
{
if (FlagGet(gBadgeFlags[i]) == TRUE)
{
if (++count >= 5)
if (++count >= OW_REMATCH_BADGE_COUNT)
return TRUE;
}
}
@ -1799,7 +1830,7 @@ static bool32 HasAtLeastFiveBadges(void)
void IncrementRematchStepCounter(void)
{
#if FREE_MATCH_CALL == FALSE
if (!HasAtLeastFiveBadges())
if (!HasEnoughBadgesForRematch())
return;
if (IsVsSeekerEnabled())
@ -1815,7 +1846,7 @@ void IncrementRematchStepCounter(void)
#if FREE_MATCH_CALL == FALSE
static bool32 IsRematchStepCounterMaxed(void)
{
if (HasAtLeastFiveBadges() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
if (HasEnoughBadgesForRematch() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
return TRUE;
else
return FALSE;
@ -1855,10 +1886,15 @@ u16 GetLastBeatenRematchTrainerId(u16 trainerId)
bool8 ShouldTryRematchBattle(void)
{
if (IsFirstTrainerIdReadyForRematch(gRematchTable, TRAINER_BATTLE_PARAM.opponentA))
return ShouldTryRematchBattleForTrainerId(TRAINER_BATTLE_PARAM.opponentA);
}
bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId)
{
if (IsFirstTrainerIdReadyForRematch(gRematchTable, trainerId))
return TRUE;
return WasSecondRematchWon(gRematchTable, TRAINER_BATTLE_PARAM.opponentA);
return WasSecondRematchWon(gRematchTable, trainerId);
}
bool8 IsTrainerReadyForRematch(void)

View File

@ -1987,7 +1987,7 @@ void DoSpecialTrainerBattle(void)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS;
break;
case FRONTIER_MODE_LINK_MULTIS:
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI;
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI | BATTLE_TYPE_TWO_OPPONENTS;
FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE);
break;
}

View File

@ -67,6 +67,7 @@ static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item);
static bool32 CanBeInfinitelyConfused(u32 battler);
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum FunctionCallOption option);
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option);
static bool32 IsOpposingSideEmpty(u32 battler);
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent);
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent);
@ -405,7 +406,7 @@ void HandleAction_UseMove(void)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(MOVE_STRUGGLE, NO_TARGET_OVERRIDE);
}
else if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns || gBattleMons[gBattlerAttacker].volatiles.recharge)
else if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns || gDisableStructs[gBattlerAttacker].rechargeTimer > 0)
{
gCurrentMove = gChosenMove = gLockedMoves[gBattlerAttacker];
}
@ -1196,7 +1197,7 @@ void PrepareStringBattle(enum StringID stringId, u32 battler)
else if (GetGenConfig(GEN_CONFIG_UPDATED_INTIMIDATE) >= GEN_8
&& stringId == STRINGID_PKMNCUTSATTACKWITH
&& targetAbility == ABILITY_RATTLED
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, targetAbility))
{
gBattlerAbility = gBattlerTarget;
BattleScriptCall(BattleScript_AbilityRaisesDefenderStat);
@ -1702,13 +1703,13 @@ u32 GetBattlerAffectionHearts(u32 battler)
// gBattlerAttacker is the battler that's trying to raise their stats and due to limitations of RandomUniformExcept, cannot be an argument
bool32 MoodyCantRaiseStat(u32 stat)
{
return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL);
return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker));
}
// gBattlerAttacker is the battler that's trying to lower their stats and due to limitations of RandomUniformExcept, cannot be an argument
bool32 MoodyCantLowerStat(u32 stat)
{
return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL);
return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker));
}
void TryToRevertMimicryAndFlags(void)
@ -1927,10 +1928,8 @@ static enum MoveCanceller CancellerSkyDrop(void)
static enum MoveCanceller CancellerRecharge(void)
{
if (gBattleMons[gBattlerAttacker].volatiles.recharge)
if (gDisableStructs[gBattlerAttacker].rechargeTimer > 0)
{
gBattleMons[gBattlerAttacker].volatiles.recharge = FALSE;
gDisableStructs[gBattlerAttacker].rechargeTimer = 0;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
@ -2403,8 +2402,7 @@ static enum MoveCanceller CancellerProtean(void)
static enum MoveCanceller CancellerPsychicTerrain(void)
{
if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN
&& IsBattlerGrounded(gBattlerTarget)
if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_PSYCHIC_TERRAIN)
&& GetChosenMovePriority(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) > 0
&& GetMoveTarget(gCurrentMove) != MOVE_TARGET_ALL_BATTLERS
&& GetMoveTarget(gCurrentMove) != MOVE_TARGET_OPPONENTS_FIELD
@ -2792,24 +2790,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)
{
u16 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
@ -2822,25 +2820,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, TRUE) == HOLD_EFFECT_TERRAIN_EXTENDER)
*timer = gBattleTurnCounter + 8;
gFieldTimers.terrainTimer = gBattleTurnCounter + 8;
else
*timer = gBattleTurnCounter + 5;
gFieldTimers.terrainTimer = gBattleTurnCounter + 5;
gBattleScripting.battler = battler;
return TRUE;
}
@ -3210,7 +3212,7 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32
break;
case MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY:
gBattleStruct->pledgeMove = FALSE;
if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, abilityDef))
{
if ((gProtectStructs[battlerAtk].notFirstStrike))
battleScript = BattleScript_MonMadeMoveUseless;
@ -3690,8 +3692,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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))
@ -3702,9 +3704,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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)
{
@ -3768,7 +3775,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(statId, 1, FALSE);
SaveBattlerAttacker(gBattlerAttacker);
@ -3834,7 +3841,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_DRIZZLE:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates);
effect++;
@ -3847,7 +3854,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_SAND_STREAM:
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, TRUE))
if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility))
{
BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates);
effect++;
@ -3859,8 +3866,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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++;
@ -3873,12 +3881,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
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++;
@ -3892,35 +3900,35 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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++;
}
break;
case ABILITY_INTIMIDATE:
if (!gSpecialStatuses[battler].switchInAbilityDone)
if (!gSpecialStatuses[battler].switchInAbilityDone && !IsOpposingSideEmpty(battler))
{
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
@ -3932,7 +3940,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break;
case ABILITY_SUPERSWEET_SYRUP:
if (!gSpecialStatuses[battler].switchInAbilityDone
&& !GetBattlerPartyState(battler)->supersweetSyrup)
&& !GetBattlerPartyState(battler)->supersweetSyrup
&& !IsOpposingSideEmpty(battler))
{
SaveBattlerAttacker(gBattlerAttacker);
gBattlerAttacker = battler;
@ -3979,7 +3988,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (GetGenConfig(GEN_INTREPID_SWORD) == GEN_9)
GetBattlerPartyState(battler)->intrepidSwordBoost = TRUE;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
@ -3994,7 +4003,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (GetGenConfig(GEN_DAUNTLESS_SHIELD) == GEN_9)
GetBattlerPartyState(battler)->dauntlessShieldBoost = TRUE;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(STAT_DEF, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
@ -4004,7 +4013,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break;
case ABILITY_WIND_RIDER:
if (!gSpecialStatuses[battler].switchInAbilityDone
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -4014,21 +4023,21 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
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++;
@ -4070,13 +4079,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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)
{
@ -4147,7 +4149,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
else //ABILITY_EMBODY_ASPECT_TEAL_MASK
stat = STAT_SPEED;
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL))
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL, gLastUsedAbility))
break;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
@ -4300,7 +4302,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_SPEED_BOOST:
if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && gDisableStructs[battler].isFirstTurn != 2)
if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gDisableStructs[battler].isFirstTurn != 2)
{
SET_STATCHANGER(STAT_SPEED, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_SpeedBoostActivates);
@ -4316,9 +4318,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
for (i = STAT_ATK; i < statsNum; i++)
{
if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN))
if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility))
validToLower |= 1u << i;
if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
validToRaise |= 1u << i;
}
@ -4455,7 +4457,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& moveType == TYPE_DARK
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = battler;
SET_STATCHANGER(STAT_ATK, 1, FALSE);
@ -4467,7 +4469,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = battler;
SET_STATCHANGER(STAT_SPEED, 1, FALSE);
@ -4479,7 +4481,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& moveType == TYPE_WATER
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = battler;
SET_STATCHANGER(STAT_DEF, 2, FALSE);
@ -4491,7 +4493,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (gBattlerAttacker != gBattlerTarget
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = battler;
SET_STATCHANGER(STAT_DEF, 1, FALSE);
@ -4505,7 +4507,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& HadMoreThanHalfHpNowDoesnt(battler)
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
gEffectBattler = battler;
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
@ -4517,8 +4519,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& IsBattleMovePhysical(gCurrentMove)
&& (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) // Don't activate if both Speed and Defense cannot be raised.
|| CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN)))
&& (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) // Don't activate if both Speed and Defense cannot be raised.
|| CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility)))
{
if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE && CanBattlerSwitch(gBattlerAttacker))
gProtectStructs[battler].disableEjectPack = TRUE; // Set flag for target
@ -4532,8 +4534,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& gDisableStructs[gBattlerAttacker].disabledMove == MOVE_NONE
&& IsBattlerAlive(gBattlerAttacker)
&& !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL)
&& gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0
&& !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) // TODO: Max Moves don't make contact, useless?
&& gChosenMove != MOVE_STRUGGLE
&& RandomPercentage(RNG_CURSED_BODY, 30))
{
gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove;
@ -4596,7 +4597,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (gSpecialStatuses[battler].criticalHit
&& IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility))
{
SET_STATCHANGER(STAT_ATK, MAX_STAT_STAGE - gBattleMons[battler].statStages[STAT_ATK], FALSE);
BattleScriptCall(BattleScript_TargetsStatWasMaxedOut);
@ -4621,7 +4622,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_GOOEY:
case ABILITY_TANGLING_HAIR:
if (IsBattlerAlive(gBattlerAttacker)
&& (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR)
&& (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move))
@ -4829,7 +4830,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_STEAM_ENGINE:
if (IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& (moveType == TYPE_FIRE || moveType == TYPE_WATER))
{
gEffectBattler = battler;
@ -4848,7 +4849,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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);
@ -4906,7 +4907,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
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++;
@ -4915,7 +4916,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_THERMAL_EXCHANGE:
if (IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)
&& moveType == TYPE_FIRE)
{
gEffectBattler = gBattlerTarget;
@ -4956,7 +4957,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& 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++;
}
@ -5522,7 +5526,6 @@ bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 ability
return FALSE;
}
// TODO: check order of battlerAtk and battlerDef
bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
if (CanSetNonVolatileStatus(
@ -5709,7 +5712,6 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
// Checks that apply to all non volatile statuses
if (abilityDef == ABILITY_COMATOSE
|| abilityDef == ABILITY_SHIELDS_DOWN
|| abilityDef == ABILITY_PURIFYING_SALT)
{
abilityAffected = TRUE;
@ -5724,6 +5726,11 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
else if (IsShieldsDownProtected(battlerDef, abilityDef))
{
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
else if ((sideBattler = IsFlowerVeilProtected(battlerDef)))
{
abilityAffected = TRUE;
@ -5862,11 +5869,12 @@ static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, e
static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, u32 statId, enum ItemCaseId caseID)
{
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId))
u32 ability = GetBattlerAbility(battler);
if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, ability) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId))
{
BufferStatChange(battler, statId, STRINGID_STATROSE);
gEffectBattler = gBattleScripting.battler = battler;
if (GetBattlerAbility(battler) == ABILITY_RIPEN)
if (ability == ABILITY_RIPEN)
SET_STATCHANGER(statId, 2, FALSE);
else
SET_STATCHANGER(statId, 1, FALSE);
@ -5887,15 +5895,15 @@ static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, enum ItemCa
{
s32 stat;
enum StringID stringId;
u32 battlerAbility = GetBattlerAbility(battler);
for (stat = STAT_ATK; stat < NUM_STATS; stat++)
{
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN, battlerAbility))
break;
}
if (stat != NUM_STATS && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId))
{
u16 battlerAbility = GetBattlerAbility(battler);
u32 savedAttacker = gBattlerAttacker;
// MoodyCantRaiseStat requires that the battler is set to gBattlerAttacker
gBattlerAttacker = gBattleScripting.battler = battler;
@ -5968,8 +5976,9 @@ static enum ItemEffect TrySetEnigmaBerry(u32 battler)
static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum DamageCategory category)
{
u32 ability = GetBattlerAbility(battler);
if (IsBattlerAlive(battler)
&& CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)
&& CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, ability)
&& (gBattleScripting.overrideBerryRequirements
|| (!DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove)
&& GetBattleMoveCategory(gCurrentMove) == category
@ -5980,7 +5989,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum
BufferStatChange(battler, statId, STRINGID_STATROSE);
gEffectBattler = battler;
if (GetBattlerAbility(battler) == ABILITY_RIPEN)
if (ability == ABILITY_RIPEN)
SET_STATCHANGER(statId, 2, FALSE);
else
SET_STATCHANGER(statId, 1, FALSE);
@ -5996,7 +6005,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum
enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, enum ItemCaseId caseID)
{
if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battler)))
{
BufferStatChange(battler, statId, STRINGID_STATROSE);
gLastUsedItem = itemId; // For surge abilities
@ -6953,7 +6962,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
case HOLD_EFFECT_BLUNDER_POLICY:
if (gBattleStruct->blunderPolicy
&& IsBattlerAlive(gBattlerAttacker)
&& CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
&& CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker)))
{
gBattleStruct->blunderPolicy = FALSE;
gLastUsedItem = atkItem;
@ -7640,13 +7649,13 @@ enum IronBallCheck
};
// Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics
static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall)
static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall, bool32 isAnticipation)
{
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, TRUE);
if (!(checkIronBall == IGNORE_IRON_BALL) && 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;
@ -7667,7 +7676,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum Inver
bool32 IsBattlerGrounded(u32 battler)
{
return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL);
return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL, FALSE);
}
u32 GetMoveSlot(u16 *moves, u32 move)
@ -8067,10 +8076,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;
}
@ -8759,9 +8768,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);
}
@ -8935,11 +8944,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);
}
@ -9200,7 +9209,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);
@ -9523,7 +9532,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) && mod == UQ_4_12(0.0))
mod = UQ_4_12(1.0);
@ -9531,7 +9540,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);
@ -9622,7 +9631,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, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, INVERSE_BATTLE, CHECK_IRON_BALL, ctx->isAnticipation) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move)))
{
modifier = UQ_4_12(0.0);
if (ctx->updateFlags && ctx->abilityDef == ABILITY_LEVITATE)
@ -9652,7 +9661,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageCont
&& ctx->moveType == TYPE_GROUND
&& IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_FLYING)
&& GetBattlerHoldEffect(ctx->battlerDef, TRUE) == HOLD_EFFECT_IRON_BALL
&& !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL)
&& !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL, FALSE)
&& !FlagGet(B_FLAG_INVERSE_BATTLE))
{
modifier = UQ_4_12(1.0);
@ -9686,7 +9695,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);
@ -10096,17 +10105,46 @@ bool32 CanBattlerFormChange(u32 battler, enum FormChanges method)
if (gBattleMons[battler].volatiles.transformed
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
return FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
return TRUE;
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
return TRUE;
// Gigantamaxed Pokemon should revert upon fainting, switching, or ending the battle.
else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE))
return TRUE;
switch (method)
{
case FORM_CHANGE_END_BATTLE:
if (IsBattlerPrimalReverted(battler))
return TRUE;
// Fallthrough
case FORM_CHANGE_FAINT:
if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler))
return TRUE;
break;
case FORM_CHANGE_BATTLE_SWITCH:
if (IsGigantamaxed(battler))
return TRUE;
else if (GetActiveGimmick(battler) == GIMMICK_TERA && GetBattlerAbility(battler) == ABILITY_HUNGER_SWITCH)
return FALSE;
break;
default:
break;
}
return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method);
}
bool32 TryRevertPartyMonFormChange(u32 partyIndex)
{
bool32 changedForm = FALSE;
// Appeared in battle and didn't faint
if ((gBattleStruct->appearedInBattle & (1u << partyIndex)) && GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP, NULL) != 0)
changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT);
if (!changedForm)
changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE);
// Clear original species field
gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].changedSpecies = SPECIES_NONE;
return changedForm;
}
bool32 TryBattleFormChange(u32 battler, enum FormChanges method)
{
u32 monId = gBattlerPartyIndexes[battler];
@ -10120,7 +10158,7 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method)
targetSpecies = GetBattleFormChangeTargetSpecies(battler, method);
if (targetSpecies == currentSpecies)
targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0);
if (targetSpecies != currentSpecies)
if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE)
{
// Saves the original species on the first form change.
@ -10137,17 +10175,22 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method)
{
bool32 restoreSpecies = FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Unlike Megas, Primal Reversion isn't canceled on fainting.
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Gigantamax Pokemon have their forms reverted after fainting, switching, or ending the battle.
else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE))
switch (method)
{
case FORM_CHANGE_END_BATTLE:
restoreSpecies = TRUE;
break;
case FORM_CHANGE_FAINT:
if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler))
restoreSpecies = TRUE;
break;
case FORM_CHANGE_BATTLE_SWITCH:
if (IsGigantamaxed(battler))
restoreSpecies = TRUE;
break;
default:
break;
}
if (restoreSpecies)
{
@ -10425,9 +10468,17 @@ u32 TryImmunityAbilityHealStatus(u32 battler, u32 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;
@ -10729,15 +10780,15 @@ bool32 TestIfSheerForceAffected(u32 battler, u16 move)
return GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && MoveIsAffectedBySheerForce(move);
}
// This function is the body of "jumpifstat", but can be used dynamically in a function
bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind)
// This function is the body of "jumpifstat", but can be used dynamically in a function. It considers Contrary.
bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind, u32 ability)
{
bool32 ret = FALSE;
u8 statValue = gBattleMons[battler].statStages[statId];
// Because this command is used as a way of checking if a stat can be lowered/raised,
// we need to do some modification at run-time.
if (GetBattlerAbility(battler) == ABILITY_CONTRARY)
if (ability == ABILITY_CONTRARY)
{
if (cmpKind == CMP_GREATER_THAN)
cmpKind = CMP_LESS_THAN;
@ -10808,7 +10859,7 @@ void BufferStatChange(u32 battler, u8 statId, enum StringID stringId)
bool32 TryRoomService(u32 battler)
{
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN))
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, GetBattlerAbility(battler)))
{
BufferStatChange(battler, STAT_SPEED, STRINGID_STATFELL);
gEffectBattler = gBattleScripting.battler = battler;
@ -10866,7 +10917,6 @@ bool32 PickupHasValidTarget(u32 battler)
return FALSE;
}
// TODO: Pass down weather as an arg
bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags)
{
if (gBattleWeather & weatherFlags && HasWeatherEffect())
@ -11240,6 +11290,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);
@ -11247,6 +11299,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);
@ -11887,3 +11941,27 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move)
return FALSE;
}
bool32 HasPartnerTrainer(u32 battler)
{
if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER)
|| (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
return TRUE;
else
return FALSE;
}
static bool32 IsOpposingSideEmpty(u32 battler)
{
u32 oppositeBattler = BATTLE_OPPOSITE(battler);
if (IsBattlerAlive(oppositeBattler))
return FALSE;
if (!IsDoubleBattle())
return TRUE;
if (IsBattlerAlive(BATTLE_PARTNER(oppositeBattler)))
return FALSE;
return TRUE;
}

View File

@ -2432,6 +2432,7 @@ static void CalculatePokeblock(struct BlenderBerry *berries, struct Pokeblock *p
}
// Factor in max RPM and round
multiuseVar = maxRPM / 333 + 100;
for (i = 0; i < FLAVOR_COUNT; i++)
{
s32 remainder;

View File

@ -9110,7 +9110,7 @@ const struct Item gItemsInfo[] =
"携带后,可以治愈\n"
"混乱。"),
.pocket = POCKET_BERRIES,
.type = ITEM_USE_BAG_MENU,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.battleUsage = EFFECT_ITEM_CURE_STATUS,
.effect = gItemEffect_PersimBerry,

View File

@ -8964,7 +8964,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"让电流覆盖全身猛撞。自己也\n"
"会受伤。有时会让对手麻痹。"),
.effect = EFFECT_HIT,
.effect = EFFECT_RECOIL,
.power = 120,
.type = TYPE_ELECTRIC,
.accuracy = 100,
@ -16964,6 +16964,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,
@ -20024,6 +20025,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_SPECIAL,
.argument = { .damagePercentage = 50 },
.metronomeBanned = TRUE,
.battleAnimScript = gBattleAnimMove_Ruination,
},

View File

@ -38,14 +38,14 @@ const struct Fusion *const gFusionTablePointers[NUM_SPECIES] =
#if P_FUSION_FORMS
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
const u16 gKyurenWhiteSwapMoveTable[][2] =
const u16 gKyuremWhiteSwapMoveTable[][2] =
{
{MOVE_SCARY_FACE, MOVE_FUSION_FLARE},
{MOVE_GLACIATE, MOVE_ICE_BURN},
};
#endif //P_FAMILY_RESHIRAM
#if P_FAMILY_ZEKROM
const u16 gKyurenBlackSwapMoveTable[][2] =
const u16 gKyuremBlackSwapMoveTable[][2] =
{
{MOVE_SCARY_FACE, MOVE_FUSION_BOLT},
{MOVE_GLACIATE, MOVE_FREEZE_SHOCK},

Some files were not shown because too many files have changed in this diff Show More