diff --git a/.all-contributorsrc b/.all-contributorsrc
index 7d2e4d4bbf..0ae8f41116 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -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,
diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
index 56368ef27c..4564bad007 100644
--- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
+++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
@@ -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
diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
index 06c55598d6..8ee1d1cc17 100644
--- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
+++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
@@ -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
diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml
index 56cfecf33a..5791967807 100644
--- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml
+++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml
@@ -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
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 874ebdd590..e02d931e28 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -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
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index 325e72950d..3cc4ee57a7 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -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
diff --git a/CREDITS.md b/CREDITS.md
index 4c0b6a3d3e..64a31a4077 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -62,6 +62,21 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 tustin2121 📖 💻 |
 Phantonomy 🎨 |
+
+  Enrico Drago 📖 📓 |
+  Pyredrid 📓 🚧 |
+  mv 💻 🎨 |
+  Avara 🔣 |
+  Doesnty 🎨 |
+  FosterProgramming 💻 |
+  Squeetz 🚧 |
+
+
+  ghostyboyy97 💻 |
+  Marky 💻 |
+  MandL27 💻 |
+  cawtds 💻 |
+
diff --git a/Makefile b/Makefile
index 56a2ad82d3..9423d16e1a 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 8f9088a6e8..2389dfe26a 100644
--- a/README.md
+++ b/README.md
@@ -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!
diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc
index a301f0feb2..e5da506a4d 100644
--- a/asm/macros/battle_script.inc
+++ b/asm/macros/battle_script.inc
@@ -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
diff --git a/asm/macros/event.inc b/asm/macros/event.inc
index 43b9417860..bc55661bd6 100644
--- a/asm/macros/event.inc
+++ b/asm/macros/event.inc
@@ -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
diff --git a/asm/macros/map.inc b/asm/macros/map.inc
index 74d2b5ff42..56d7387238 100644
--- a/asm/macros/map.inc
+++ b/asm/macros/map.inc
@@ -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)
diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s
index 4d952965e6..4df85854dc 100644
--- a/data/battle_anim_scripts.s
+++ b/data/battle_anim_scripts.s
@@ -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
diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s
index f1eeeccdc9..a353b44ba8 100644
--- a/data/battle_scripts_1.s
+++ b/data/battle_scripts_1.s
@@ -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
diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s
old mode 100644
new mode 100755
index b46b30a443..586a79c5ef
--- a/data/battle_scripts_2.s
+++ b/data/battle_scripts_2.s
@@ -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
diff --git a/data/event_scripts.s b/data/event_scripts.s
index 61a1ba497c..1ddb33c9d2 100644
--- a/data/event_scripts.s
+++ b/data/event_scripts.s
@@ -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
diff --git a/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc
index 1a41bf7aac..482782f542 100644
--- a/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc
+++ b/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc
@@ -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
diff --git a/data/maps/LilycoveCity_Harbor/scripts.inc b/data/maps/LilycoveCity_Harbor/scripts.inc
index 93a93685ae..89ed7a595a 100644
--- a/data/maps/LilycoveCity_Harbor/scripts.inc
+++ b/data/maps/LilycoveCity_Harbor/scripts.inc
@@ -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
diff --git a/data/maps/LittlerootTown/scripts.inc b/data/maps/LittlerootTown/scripts.inc
index 569644cdac..a3a24fbca3 100644
--- a/data/maps/LittlerootTown/scripts.inc
+++ b/data/maps/LittlerootTown/scripts.inc
@@ -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
diff --git a/data/maps/SlateportCity_Harbor/scripts.inc b/data/maps/SlateportCity_Harbor/scripts.inc
index eb2e9388a0..dc0fda5223 100644
--- a/data/maps/SlateportCity_Harbor/scripts.inc
+++ b/data/maps/SlateportCity_Harbor/scripts.inc
@@ -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
diff --git a/data/script_cmd_table.inc b/data/script_cmd_table.inc
index e05260d3d4..a3defef83e 100644
--- a/data/script_cmd_table.inc
+++ b/data/script_cmd_table.inc
@@ -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::
diff --git a/data/scripts/cable_club.inc b/data/scripts/cable_club.inc
index 227981578f..1c547f8980 100644
--- a/data/scripts/cable_club.inc
+++ b/data/scripts/cable_club.inc
@@ -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
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index c1e6a78752..a38a539bef 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -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)
diff --git a/docs/book.toml b/docs/book.toml
index 9bc05435f7..a1e994eff3 100644
--- a/docs/book.toml
+++ b/docs/book.toml
@@ -1,14 +1,13 @@
-[book]
-language = "en"
-multilingual = false
-src = "."
-title = "pokeemerald-expansion"
-
-[output.html]
-git-repository-url = "https://github.com/rh-hideout/pokeemerald-expansion"
-edit-url-template = "https://github.com/rh-hideout/pokeemerald-expansion/edit/master/docs/{path}"
-site-url = "/pokeemerald-expansion/"
-
-[preprocessor.fix_links]
-command = "python3 fix_links.py"
-after = [ "links" ]
+[book]
+language = "en"
+src = "."
+title = "pokeemerald-expansion"
+
+[output.html]
+git-repository-url = "https://github.com/rh-hideout/pokeemerald-expansion"
+edit-url-template = "https://github.com/rh-hideout/pokeemerald-expansion/edit/master/docs/{path}"
+site-url = "/pokeemerald-expansion/"
+
+[preprocessor.fix_links]
+command = "python3 fix_links.py"
+after = [ "links" ]
diff --git a/docs/changelogs/1.13.x/1.13.3.md b/docs/changelogs/1.13.x/1.13.3.md
new file mode 100644
index 0000000000..70e1830ff4
--- /dev/null
+++ b/docs/changelogs/1.13.x/1.13.3.md
@@ -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
+
+
+
+
diff --git a/docs/fix_links.py b/docs/fix_links.py
index e5b7c09c62..3a4184b72c 100644
--- a/docs/fix_links.py
+++ b/docs/fix_links.py
@@ -1,51 +1,51 @@
-# workarounds to avoid changing current directory structure
-# autolink logic based on https://github.com/zopieux/py-gfm/blob/fd7b33ed138d240d24dfb659acff7d4ce3f43745/gfm/autolink.py
-
-import json
-import sys
-import re
-
-URL_RE = re.compile(
- r"(```(?s:.)+?```|`.+?`|<.+?>)|"
- r"\b((?:(?i:ftp|https?)://|(?i:www)\d{0,3}[.])(?:[^\s()<>]+|"
- r"\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()"
- r"<>]+\)))*\)|[^\s`!()\[\]{};:" + r"'" + r'".,<>?«»“”‘’*]))'
-)
-PROTOCOL_RE = re.compile(r"^(?i:ftp|https?)://")
-
-ANCHOR_RE = re.compile(r"(\]\((?:[^)#]+\.md)?#)([^)]+\))")
-
-def handle_url(m):
- code = m.group(1)
- if code:
- return code
- href = m.group(2)
- if not PROTOCOL_RE.match(href):
- href = "http://%s" % href
- return f'<{href}>'
-
-def handle_anchor(m):
- page = m.group(1)
- anchor = m.group(2)
- return page + anchor.lower()
-
-def proc_items(items):
- for item in items:
- if 'Chapter' in item:
- s = item['Chapter']['content']
- s = s.replace('](README.md)', '](./)')
- s = s.replace('](/INSTALL.md', '](INSTALL.md')
- s = s.replace('](docs/', '](')
- s = URL_RE.sub(handle_url, s)
- item['Chapter']['content'] = ANCHOR_RE.sub(handle_anchor, s)
- proc_items(item['Chapter']['sub_items'])
-
-if __name__ == '__main__':
- if len(sys.argv) > 1:
- if sys.argv[1] == "supports":
- sys.exit(0)
-
- context, book = json.load(sys.stdin)
- proc_items(book['sections'])
-
- print(json.dumps(book))
+# workarounds to avoid changing current directory structure
+# autolink logic based on https://github.com/zopieux/py-gfm/blob/fd7b33ed138d240d24dfb659acff7d4ce3f43745/gfm/autolink.py
+
+import json
+import sys
+import re
+
+URL_RE = re.compile(
+ r"(```(?s:.)+?```|`.+?`|<.+?>)|"
+ r"\b((?:(?i:ftp|https?)://|(?i:www)\d{0,3}[.])(?:[^\s()<>]+|"
+ r"\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()"
+ r"<>]+\)))*\)|[^\s`!()\[\]{};:" + r"'" + r'".,<>?«»“”‘’*]))'
+)
+PROTOCOL_RE = re.compile(r"^(?i:ftp|https?)://")
+
+ANCHOR_RE = re.compile(r"(\]\((?:[^)#]+\.md)?#)([^)]+\))")
+
+def handle_url(m):
+ code = m.group(1)
+ if code:
+ return code
+ href = m.group(2)
+ if not PROTOCOL_RE.match(href):
+ href = "http://%s" % href
+ return f'<{href}>'
+
+def handle_anchor(m):
+ page = m.group(1)
+ anchor = m.group(2)
+ return page + anchor.lower()
+
+def proc_items(items):
+ for item in items:
+ if 'Chapter' in item:
+ s = item['Chapter']['content']
+ s = s.replace('](README.md)', '](./)')
+ s = s.replace('](/INSTALL.md', '](INSTALL.md')
+ s = s.replace('](docs/', '](')
+ s = URL_RE.sub(handle_url, s)
+ item['Chapter']['content'] = ANCHOR_RE.sub(handle_anchor, s)
+ proc_items(item['Chapter']['sub_items'])
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1:
+ if sys.argv[1] == "supports":
+ sys.exit(0)
+
+ context, book = json.load(sys.stdin)
+ proc_items(book['items'])
+
+ print(json.dumps(book))
diff --git a/docs/local_mdbook/index.md b/docs/local_mdbook/index.md
index ef362dc45f..fa4b7f1e70 100644
--- a/docs/local_mdbook/index.md
+++ b/docs/local_mdbook/index.md
@@ -1,2 +1,2 @@
## Running documentation website locally
-- [Ubuntu WSL1/WSL2](/docs/local_mdbook/ubuntu_WSL.md)
+- [Ubuntu WSL1/WSL2](ubuntu_WSL.md)
diff --git a/docs/team_procedures/merge_checklist.md b/docs/team_procedures/merge_checklist.md
index ae3ff60b19..53e8d7683f 100644
--- a/docs/team_procedures/merge_checklist.md
+++ b/docs/team_procedures/merge_checklist.md
@@ -7,7 +7,7 @@ This document is a guide for maintainers to account for all the reccomended step
# Checklist
## Is the branch's theoretical functionality in scope?
-If you're not sure if a branch's functionality is [in scope](docs/team_procedures/scope.md), start a conversation on Discord to resolve.
+If you're not sure if a branch's functionality is [in scope](scope.md), start a conversation on Discord to resolve.
## Does the branch successfully compile?
From `make clean`, the branch should locally compile.
@@ -37,18 +37,18 @@ If you're not sure if something CAN be tested, start a discussion. Some contribu
If any new tests are `KNOWN_FAILING`, issues should be opened describing each of the `KNOWN_FAILING` tests and our understanding of why they fail.
-## Does the branch meet our [config philosophy](/docs/STYLEGUIDE.md#config-philosophy)?
+## Does the branch meet our [config philosophy](../STYLEGUIDE.md#config-philosophy)?
-## Does the branch meet our [saves philosophy](/docs/STYLEGUIDE.md#saves-philosophy)?
+## Does the branch meet our [saves philosophy](../STYLEGUIDE.md#saves-philosophy)?
-## Does the submitted code follow the [styleguide](/docs/STYLEGUIDE.md)?
+## Does the submitted code follow the [styleguide](../STYLEGUIDE.md)?
This applies to code that comes from other branches or games.
## Is the pull request appropriately labeled?
Without labels, the CHANGELOG will not be properly formatted. For specifically the `bugfix` label, an additional label, detailing what area the bug exists in is required.
## Is `pokeemerald-expansion` free from a merge freeze?
-Our [release schedule](/docs/team_procedures/schedule.md) prevents us from merging Big Features and non-bugfixes within certain dates close to a release. Please use `/release` in the RHH Discord to clarify when these are occuring.
+Our [release schedule](schedule.md) prevents us from merging Big Features and non-bugfixes within certain dates close to a release. Please use `/release` in the RHH Discord to clarify when these are occuring.
# Merging
diff --git a/docs/team_procedures/schedule.md b/docs/team_procedures/schedule.md
index c689e5b454..520c4af000 100644
--- a/docs/team_procedures/schedule.md
+++ b/docs/team_procedures/schedule.md
@@ -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.
diff --git a/docs/tutorials/dns.md b/docs/tutorials/dns.md
index 908329761e..6fdd1c1d77 100644
--- a/docs/tutorials/dns.md
+++ b/docs/tutorials/dns.md
@@ -23,8 +23,8 @@ When writing map scripts, `fadescreenswapbuffers` should be preferred over `fade
### Q: How do I make lightbulbs glow?
-
-
+
+
A: Making lamps glow is not part of the tileset itself. Instead, place certain object events on top of where you desire a glowing effect.
@@ -48,7 +48,7 @@ on separate lines to mark those colors as being light-blended, i.e:
During the day time, these color indices appear as normal, but will be blended with either yellow or the 0 index at night. These indices should only be used for things you expect to light up. If you are using [porytiles](https://github.com/grunt-lucas/porytiles/wiki), palette overrides and using slight alterations to a color will aid you in avoiding color conflicts where the wrong index is assigned.
-
+
The windows appear as normal during the day time (blue) and light up in the night. These use the default color.
diff --git a/docs/tutorials/how_to_follower_npc.md b/docs/tutorials/how_to_follower_npc.md
index 2e449adbc0..fbd0087859 100644
--- a/docs/tutorials/how_to_follower_npc.md
+++ b/docs/tutorials/how_to_follower_npc.md
@@ -2,7 +2,7 @@
*Written by Bivurnum*
*gif by ghoulslash*
-
+
## Configs
The configs for follower NPCs can be found in [include/config/follower_npc.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/upcoming/include/config/follower_npc.h).
diff --git a/docs/tutorials/how_to_new_pokemon.md b/docs/tutorials/how_to_new_pokemon.md
index da6add17bc..79f01acbd2 100644
--- a/docs/tutorials/how_to_new_pokemon.md
+++ b/docs/tutorials/how_to_new_pokemon.md
@@ -42,7 +42,7 @@ The main things that the Expansion changes are listed here.
# Useful resources
You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle.
-
+
# The Data - Part 1
@@ -73,7 +73,7 @@ We add this at the end so that no existing species change Id and so that we don'
Now, let's see how it looks in-game!
-
+
Hmmm, something's not right...
@@ -446,7 +446,7 @@ Now we can add the number and entry to our Mewthree:
},
};
```
-
+
The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex.
@@ -497,7 +497,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera
...
};
```
-
+
# The Graphics
@@ -1058,7 +1058,7 @@ What this allows us to do is to be able to get all forms of a Pokémon in our co
For example, in the HGSS dex, it lets us browse between the entries of every form available.:
- 
+ 
In addition, we have the `GET_BASE_SPECIES_ID` macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:
```c
@@ -1089,7 +1089,7 @@ The second value is the target form, to which the Pokémon will change into.
Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in `include/constants/form_change_types.h`.
## 3. Gender differences
-
+
You may have seen that there's a couple of duplicate fields with a "Female" suffix.
```diff
@@ -1262,8 +1262,8 @@ Either way, you may also create custom animation tables and use them here approp
### How to add the Pokémon Object Events to map
In Porymap, select the object you want to set the sprite to. Then, change the field "Sprite" to use `OBJ_EVENT_GFX_SPECIES(SPECIES)`, replacing SPECIES with the name of the species you want to use. If you get a compiler error, it's because it used the species define as part of the macro, so it needs to match how you defined it all the way back in [Declare a species constant](#1-Declare-a-species-constant).
-
-
+
+
If you want to use their shiny and/or female versions, use one of the following macros:
- `OBJ_EVENT_GFX_SPECIES_SHINY(name)`
diff --git a/docs/tutorials/vs_seeker.md b/docs/tutorials/vs_seeker.md
new file mode 100644
index 0000000000..136465a5b4
--- /dev/null
+++ b/docs/tutorials/vs_seeker.md
@@ -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.
diff --git a/graphics/battle_interface/healthbox_doubles_opponent.png b/graphics/battle_interface/healthbox_doubles_opponent.png
index def9d07af0..28fdbbbeb9 100644
Binary files a/graphics/battle_interface/healthbox_doubles_opponent.png and b/graphics/battle_interface/healthbox_doubles_opponent.png differ
diff --git a/graphics/battle_interface/healthbox_doubles_player.png b/graphics/battle_interface/healthbox_doubles_player.png
index f843c664b1..c3587ec3fd 100644
Binary files a/graphics/battle_interface/healthbox_doubles_player.png and b/graphics/battle_interface/healthbox_doubles_player.png differ
diff --git a/graphics/battle_interface/healthbox_safari.png b/graphics/battle_interface/healthbox_safari.png
index e48f380a4b..b2a9255c5a 100644
Binary files a/graphics/battle_interface/healthbox_safari.png and b/graphics/battle_interface/healthbox_safari.png differ
diff --git a/graphics/battle_interface/healthbox_singles_opponent.png b/graphics/battle_interface/healthbox_singles_opponent.png
index 52f70e8580..3047ed62d0 100644
Binary files a/graphics/battle_interface/healthbox_singles_opponent.png and b/graphics/battle_interface/healthbox_singles_opponent.png differ
diff --git a/graphics/battle_interface/healthbox_singles_player.png b/graphics/battle_interface/healthbox_singles_player.png
index 0862ca399d..cf65e67612 100644
Binary files a/graphics/battle_interface/healthbox_singles_player.png and b/graphics/battle_interface/healthbox_singles_player.png differ
diff --git a/graphics/fonts/latin_small_narrower.png b/graphics/fonts/latin_small_narrower.png
index 69bca4645a..ebb7c70d27 100644
Binary files a/graphics/fonts/latin_small_narrower.png and b/graphics/fonts/latin_small_narrower.png differ
diff --git a/graphics/pokemon/kingler/overworld.png b/graphics/pokemon/kingler/overworld.png
index 135f79b326..c67ce3a53a 100644
Binary files a/graphics/pokemon/kingler/overworld.png and b/graphics/pokemon/kingler/overworld.png differ
diff --git a/graphics/pokemon/krabby/overworld.png b/graphics/pokemon/krabby/overworld.png
index abe66ceb6f..f712b6f6ff 100644
Binary files a/graphics/pokemon/krabby/overworld.png and b/graphics/pokemon/krabby/overworld.png differ
diff --git a/graphics_file_rules.mk b/graphics_file_rules.mk
index 6b23768b5d..2e858e4e52 100644
--- a/graphics_file_rules.mk
+++ b/graphics_file_rules.mk
@@ -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
diff --git a/include/battle.h b/include/battle.h
index 8fe4fa8cce..15b37d62c8 100644
--- a/include/battle.h
+++ b/include/battle.h
@@ -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;
diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h
index 1b23dd7e1a..1964e45d53 100644
--- a/include/battle_ai_util.h
+++ b/include/battle_ai_util.h
@@ -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);
diff --git a/include/battle_gfx_sfx_util.h b/include/battle_gfx_sfx_util.h
index ae71eb5b50..eb713779c2 100644
--- a/include/battle_gfx_sfx_util.h
+++ b/include/battle_gfx_sfx_util.h
@@ -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);
diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h
index 42b8c7255a..84b1d6b26a 100644
--- a/include/battle_gimmick.h
+++ b/include/battle_gimmick.h
@@ -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);
diff --git a/include/battle_pyramid_bag.h b/include/battle_pyramid_bag.h
index 258ca56b21..4d8b87506e 100644
--- a/include/battle_pyramid_bag.h
+++ b/include/battle_pyramid_bag.h
@@ -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
diff --git a/include/battle_setup.h b/include/battle_setup.h
index aa01178d8d..1323971f67 100644
--- a/include/battle_setup.h
+++ b/include/battle_setup.h
@@ -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);
diff --git a/include/battle_util.h b/include/battle_util.h
index f22fd8b0ac..7bb8bc7292 100644
--- a/include/battle_util.h
+++ b/include/battle_util.h
@@ -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
diff --git a/include/config/battle.h b/include/config/battle.h
index 61603ba5b8..2b9e9fdecc 100644
--- a/include/config/battle.h
+++ b/include/config/battle.h
@@ -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.
diff --git a/include/config/item.h b/include/config/item.h
index 96156b0db8..4222754ead 100644
--- a/include/config/item.h
+++ b/include/config/item.h
@@ -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.
diff --git a/include/config/overworld.h b/include/config/overworld.h
index 190445ffb4..ff2ab3311e 100644
--- a/include/config/overworld.h
+++ b/include/config/overworld.h
@@ -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
diff --git a/include/config/summary_screen.h b/include/config/summary_screen.h
index 4b5f434cb3..07bae63533 100644
--- a/include/config/summary_screen.h
+++ b/include/config/summary_screen.h
@@ -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.
/*
diff --git a/include/constants/battle.h b/include/constants/battle.h
index 825487f24e..d2913d6fc6 100644
--- a/include/constants/battle.h
+++ b/include/constants/battle.h
@@ -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)) \
diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h
index 3d514b1936..74683a70b6 100644
--- a/include/constants/battle_anim.h
+++ b/include/constants/battle_anim.h
@@ -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)
diff --git a/include/constants/expansion.h b/include/constants/expansion.h
index 92973512b2..1b9fba88a1 100644
--- a/include/constants/expansion.h
+++ b/include/constants/expansion.h
@@ -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.
diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h
index 7a9aed85c3..7be7998d9e 100644
--- a/include/constants/generational_changes.h
+++ b/include/constants/generational_changes.h
@@ -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
};
diff --git a/include/constants/maps.h b/include/constants/maps.h
index d41f03c683..36e27d78b7 100644
--- a/include/constants/maps.h
+++ b/include/constants/maps.h
@@ -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)
diff --git a/include/dodrio_berry_picking.h b/include/dodrio_berry_picking.h
index a93fb27cd1..ec62e51947 100644
--- a/include/dodrio_berry_picking.h
+++ b/include/dodrio_berry_picking.h
@@ -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);
diff --git a/include/gametypes.h b/include/gametypes.h
new file mode 100644
index 0000000000..242fc9d9db
--- /dev/null
+++ b/include/gametypes.h
@@ -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
diff --git a/include/generational_changes.h b/include/generational_changes.h
index 30739a14f5..9b6b385c7e 100644
--- a/include/generational_changes.h
+++ b/include/generational_changes.h
@@ -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
diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h
index 37fb95e3fb..5e0f99031d 100644
--- a/include/global.fieldmap.h
+++ b/include/global.fieldmap.h
@@ -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;
diff --git a/include/global.h b/include/global.h
index 74a993679f..ff0b6f79e1 100644
--- a/include/global.h
+++ b/include/global.h
@@ -5,6 +5,7 @@
#include
#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"
diff --git a/include/global.tv.h b/include/global.tv.h
index 6bc6d08f86..3956decd4a 100644
--- a/include/global.tv.h
+++ b/include/global.tv.h
@@ -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;
diff --git a/include/item_menu.h b/include/item_menu.h
index 64d9f7628e..a16ccae5d4 100644
--- a/include/item_menu.h
+++ b/include/item_menu.h
@@ -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);
diff --git a/include/landmark.h b/include/landmark.h
index 395905033a..f5feb407bc 100644
--- a/include/landmark.h
+++ b/include/landmark.h
@@ -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
diff --git a/include/mail.h b/include/mail.h
index 403078f097..de29b21d42 100644
--- a/include/mail.h
+++ b/include/mail.h
@@ -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);
diff --git a/include/overworld.h b/include/overworld.h
index f4368ba57d..28cd4c93f7 100644
--- a/include/overworld.h
+++ b/include/overworld.h
@@ -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);
diff --git a/include/pokemon.h b/include/pokemon.h
index f58ebbba1a..8852ba3c97 100644
--- a/include/pokemon.h
+++ b/include/pokemon.h
@@ -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);
diff --git a/include/pokenav.h b/include/pokenav.h
index 2992a76d12..191355736c 100644
--- a/include/pokenav.h
+++ b/include/pokenav.h
@@ -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);
diff --git a/include/random.h b/include/random.h
index df3e1579ad..816cf9b40a 100644
--- a/include/random.h
+++ b/include/random.h
@@ -217,6 +217,7 @@ enum RandomTag
RNG_WRAP,
RNG_BALLTHROW_CRITICAL,
RNG_BALLTHROW_SHAKE,
+ RNG_PROTECT_FAIL,
};
#define RandomWeighted(tag, ...) \
diff --git a/include/rayquaza_scene.h b/include/rayquaza_scene.h
index 422b591273..6a51b9a45f 100644
--- a/include/rayquaza_scene.h
+++ b/include/rayquaza_scene.h
@@ -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
diff --git a/include/region_map.h b/include/region_map.h
index 2ca153e92f..571acbf351 100644
--- a/include/region_map.h
+++ b/include/region_map.h
@@ -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);
diff --git a/include/save.h b/include/save.h
index aef84c3158..1bc4c02404 100644
--- a/include/save.h
+++ b/include/save.h
@@ -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;
diff --git a/include/strings.h b/include/strings.h
index 6cf909d4f4..bd2619eb51 100644
--- a/include/strings.h
+++ b/include/strings.h
@@ -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
diff --git a/include/test/test.h b/include/test/test.h
index 48a6e84aa2..7b3225934d 100644
--- a/include/test/test.h
+++ b/include/test/test.h
@@ -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; };
diff --git a/include/vs_seeker.h b/include/vs_seeker.h
index d6795432b0..749aaeab57 100644
--- a/include/vs_seeker.h
+++ b/include/vs_seeker.h
@@ -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
diff --git a/spritesheet_rules.mk b/spritesheet_rules.mk
index ef5da9c091..7c05219f8a 100644
--- a/spritesheet_rules.mk
+++ b/spritesheet_rules.mk
@@ -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
diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c
index 1960088640..0c8c0bb1cd 100644
--- a/src/battle_ai_main.c
+++ b/src/battle_ai_main.c
@@ -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:
diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c
index 96cb7e30df..f300741675 100644
--- a/src/battle_ai_switch_items.c
+++ b/src/battle_ai_switch_items.c
@@ -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])
diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c
index 5913c0842c..8bed0234ee 100644
--- a/src/battle_ai_util.c
+++ b/src/battle_ai_util.c
@@ -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;
diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c
index 65de36237b..e30a8afd15 100644
--- a/src/battle_anim_effects_3.c
+++ b/src/battle_anim_effects_3.c
@@ -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);
}
}
diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c
index b59ed37432..1882a092c1 100644
--- a/src/battle_controller_player.c
+++ b/src/battle_controller_player.c
@@ -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);
diff --git a/src/battle_debug.c b/src/battle_debug.c
index 4f4f515a7a..4b87c97aae 100644
--- a/src/battle_debug.c
+++ b/src/battle_debug.c
@@ -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},
diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c
index 73ffa12023..d423659be9 100644
--- a/src/battle_end_turn.c
+++ b/src/battle_end_turn.c
@@ -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);
diff --git a/src/battle_factory_screen.c b/src/battle_factory_screen.c
index d6233c88fb..c7fef80029 100644
--- a/src/battle_factory_screen.c
+++ b/src/battle_factory_screen.c
@@ -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,
diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c
index d7004d7635..a6362bb24f 100644
--- a/src/battle_gfx_sfx_util.c
+++ b/src/battle_gfx_sfx_util.c
@@ -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);
}
diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c
index 76ea80a4f6..38d60d4365 100644
--- a/src/battle_gimmick.c
+++ b/src/battle_gimmick.c
@@ -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)
diff --git a/src/battle_interface.c b/src/battle_interface.c
index 57094a7a8e..e914792c72 100644
--- a/src/battle_interface.c
+++ b/src/battle_interface.c
@@ -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;
diff --git a/src/battle_main.c b/src/battle_main.c
index cd1dc7c279..16e0ad1ae1 100644
--- a/src/battle_main.c
+++ b/src/battle_main.c
@@ -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
diff --git a/src/battle_message.c b/src/battle_message.c
index 3dbec20467..b58fba0c14 100644
--- a/src/battle_message.c
+++ b/src/battle_message.c
@@ -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}那里!"),
diff --git a/src/battle_pyramid_bag.c b/src/battle_pyramid_bag.c
index 4a5d1c3a4e..cd74e6c40b 100644
--- a/src/battle_pyramid_bag.c
+++ b/src/battle_pyramid_bag.c
@@ -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);
diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c
index 45fa3f3e97..face6eeb08 100755
--- a/src/battle_script_commands.c
+++ b/src/battle_script_commands.c
@@ -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;
diff --git a/src/battle_setup.c b/src/battle_setup.c
index 6a6f529ea6..1505f6af65 100644
--- a/src/battle_setup.c
+++ b/src/battle_setup.c
@@ -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)
diff --git a/src/battle_tower.c b/src/battle_tower.c
index e1346bc08c..44ef64305c 100644
--- a/src/battle_tower.c
+++ b/src/battle_tower.c
@@ -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;
}
diff --git a/src/battle_util.c b/src/battle_util.c
index ee98c8ddb1..f6a61ffde4 100644
--- a/src/battle_util.c
+++ b/src/battle_util.c
@@ -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;
+}
diff --git a/src/berry_blender.c b/src/berry_blender.c
index 6775a99ea8..6582ba2fb9 100644
--- a/src/berry_blender.c
+++ b/src/berry_blender.c
@@ -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;
diff --git a/src/data/items.h b/src/data/items.h
index 5636b05f57..2600be4610 100644
--- a/src/data/items.h
+++ b/src/data/items.h
@@ -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,
diff --git a/src/data/moves_info.h b/src/data/moves_info.h
index 6803746391..e05f76acb6 100644
--- a/src/data/moves_info.h
+++ b/src/data/moves_info.h
@@ -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,
},
diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h
index ad2e6a040d..1af386a897 100644
--- a/src/data/pokemon/form_change_table_pointers.h
+++ b/src/data/pokemon/form_change_table_pointers.h
@@ -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},
diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h
index ad8a7e90ca..fd443458c7 100644
--- a/src/data/pokemon/form_change_tables.h
+++ b/src/data/pokemon/form_change_tables.h
@@ -925,6 +925,7 @@ static const struct FormChange sSilvallyFormChangeTable[] = {
#if P_FAMILY_MINIOR
static const struct FormChange sMiniorRedFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_RED},
@@ -933,6 +934,7 @@ static const struct FormChange sMiniorRedFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorBlueFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_BLUE},
@@ -941,6 +943,7 @@ static const struct FormChange sMiniorBlueFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorGreenFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_GREEN},
@@ -949,6 +952,7 @@ static const struct FormChange sMiniorGreenFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorIndigoFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_INDIGO},
@@ -957,6 +961,7 @@ static const struct FormChange sMiniorIndigoFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorOrangeFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_ORANGE},
@@ -965,6 +970,7 @@ static const struct FormChange sMiniorOrangeFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorVioletFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_VIOLET},
@@ -973,6 +979,7 @@ static const struct FormChange sMiniorVioletFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sMiniorYellowFormChangeTable[] = {
+ {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50},
{FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50},
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_YELLOW},
diff --git a/src/data/pokemon/species_info/gen_1_families.h b/src/data/pokemon/species_info/gen_1_families.h
index 015f134c71..e99cc277a8 100644
--- a/src/data/pokemon/species_info/gen_1_families.h
+++ b/src/data/pokemon/species_info/gen_1_families.h
@@ -197,12 +197,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Venusaur,
.iconSprite = gMonIcon_Venusaur,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 4,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_VenusaurF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_VenusaurF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(2, 6, SHADOW_SIZE_XL_BATTLE_ONLY)
FOOTPRINT(Venusaur)
@@ -1318,12 +1318,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Butterfree,
.iconSprite = gMonIcon_Butterfree,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_ButterfreeF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_ButterfreeF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-3, 13, SHADOW_SIZE_S)
FOOTPRINT(Butterfree)
@@ -2072,12 +2072,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Rattata,
.iconSprite = gMonIcon_Rattata,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_RattataF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 40),
.backPicFemale = gMonBackPic_RattataF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_FAST,
SHADOW(1, -3, SHADOW_SIZE_S)
FOOTPRINT(Rattata)
@@ -2163,12 +2163,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Raticate,
.iconSprite = gMonIcon_Raticate,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_RaticateF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_RaticateF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_FAST,
SHADOW(0, 8, SHADOW_SIZE_L)
FOOTPRINT(Raticate)
@@ -2881,7 +2881,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Pikachu,
.iconSprite = gMonIcon_Pikachu,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_PikachuF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 48),
.backPicFemale = gMonBackPic_PikachuF,
@@ -2890,7 +2890,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.iconSpriteFemale = gMonIcon_PikachuF,
.iconPalIndexFemale = 2,
#endif //P_CUSTOM_GENDER_DIFF_ICONS
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NORMAL,
SHADOW(-3, 5, SHADOW_SIZE_M)
OVERWORLD(
@@ -5536,12 +5536,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Zubat,
.iconSprite = gMonIcon_Zubat,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_ZubatF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 48),
.backPicFemale = gMonBackPic_ZubatF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-4, 11, SHADOW_SIZE_S)
FOOTPRINT(Zubat)
@@ -5634,12 +5634,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Golbat,
.iconSprite = gMonIcon_Golbat,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GolbatF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_GolbatF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 40),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(2, 14, SHADOW_SIZE_M)
FOOTPRINT(Golbat)
@@ -5882,12 +5882,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Gloom,
.iconSprite = gMonIcon_Gloom,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GloomF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 48),
.backPicFemale = gMonBackPic_GloomF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 3, SHADOW_SIZE_M)
FOOTPRINT(Gloom)
@@ -5974,12 +5974,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Vileplume,
.iconSprite = gMonIcon_Vileplume,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_VileplumeF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
.backPicFemale = gMonBackPic_VileplumeF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 7, SHADOW_SIZE_L)
FOOTPRINT(Vileplume)
@@ -8117,12 +8117,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Politoed,
.iconSprite = gMonIcon_Politoed,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_PolitoedF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 56),
.backPicFemale = gMonBackPic_PolitoedF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(1, 9, SHADOW_SIZE_M)
FOOTPRINT(Politoed)
@@ -8277,12 +8277,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Kadabra,
.iconSprite = gMonIcon_Kadabra,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_KadabraF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_KadabraF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(1, 6, SHADOW_SIZE_L)
FOOTPRINT(Kadabra)
@@ -8375,12 +8375,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Alakazam,
.iconSprite = gMonIcon_Alakazam,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_AlakazamF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_AlakazamF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 9, SHADOW_SIZE_L)
FOOTPRINT(Alakazam)
@@ -10843,12 +10843,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Doduo,
.iconSprite = gMonIcon_Doduo,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_DoduoF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_DoduoF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(6, 5, SHADOW_SIZE_M)
FOOTPRINT(Doduo)
@@ -10935,12 +10935,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Dodrio,
.iconSprite = gMonIcon_Dodrio,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_DodrioF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_DodrioF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(3, 12, SHADOW_SIZE_L)
FOOTPRINT(Dodrio)
@@ -12019,12 +12019,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Steelix,
.iconSprite = gMonIcon_Steelix,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_SteelixF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_SteelixF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(2, 12, SHADOW_SIZE_XL_BATTLE_ONLY)
FOOTPRINT(Steelix)
@@ -12258,12 +12258,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Hypno,
.iconSprite = gMonIcon_Hypno,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_HypnoF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_HypnoF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-3, 9, SHADOW_SIZE_L)
FOOTPRINT(Hypno)
@@ -12350,7 +12350,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
SIZE_32x32,
SHADOW_SIZE_M,
TRACKS_FOOT,
- sAnimTable_Following,
+ sAnimTable_Following_Asym,
gOverworldPalette_Krabby,
gShinyOverworldPalette_Krabby
)
@@ -12419,7 +12419,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
SIZE_32x32,
SHADOW_SIZE_M,
TRACKS_FOOT,
- sAnimTable_Following,
+ sAnimTable_Following_Asym,
gOverworldPalette_Kingler,
gShinyOverworldPalette_Kingler
)
@@ -13992,12 +13992,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Rhyhorn,
.iconSprite = gMonIcon_Rhyhorn,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_RhyhornF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 40),
.backPicFemale = gMonBackPic_RhyhornF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 0, SHADOW_SIZE_L)
FOOTPRINT(Rhyhorn)
@@ -14071,12 +14071,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Rhydon,
.iconSprite = gMonIcon_Rhydon,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_RhydonF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_RhydonF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(3, 9, SHADOW_SIZE_XL_BATTLE_ONLY)
FOOTPRINT(Rhydon)
@@ -15002,12 +15002,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Goldeen,
.iconSprite = gMonIcon_Goldeen,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GoldeenF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 40),
.backPicFemale = gMonBackPic_GoldeenF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-6, 0, SHADOW_SIZE_XL_BATTLE_ONLY)
FOOTPRINT(Goldeen)
@@ -15086,12 +15086,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Seaking,
.iconSprite = gMonIcon_Seaking,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_SeakingF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_SeakingF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 8, SHADOW_SIZE_XL_BATTLE_ONLY)
FOOTPRINT(Seaking)
@@ -16932,12 +16932,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Magikarp,
.iconSprite = gMonIcon_Magikarp,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_MagikarpF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 56),
.backPicFemale = gMonBackPic_MagikarpF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(1, 7, SHADOW_SIZE_M)
FOOTPRINT(Magikarp)
@@ -17013,12 +17013,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Gyarados,
.iconSprite = gMonIcon_Gyarados,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GyaradosF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_GyaradosF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(5, 9, SHADOW_SIZE_XL_BATTLE_ONLY)
FOOTPRINT(Gyarados)
@@ -17369,12 +17369,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Eevee,
.iconSprite = gMonIcon_Eevee,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_EeveeF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 48),
.backPicFemale = gMonBackPic_EeveeF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NORMAL,
SHADOW(-2, 2, SHADOW_SIZE_S)
FOOTPRINT(Eevee)
@@ -17523,7 +17523,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.shinyPalette = gMonShinyPalette_Eevee,
.iconSprite = gMonIcon_EeveePartner,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_EeveeF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 48),
.backPicFemale = gMonBackPic_EeveeF,
@@ -17532,7 +17532,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
.iconSpriteFemale = gMonIcon_EeveePartnerF,
.iconPalIndexFemale = 2,
#endif
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NORMAL,
SHADOW(-2, 2, SHADOW_SIZE_S)
FOOTPRINT(Eevee)
diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h
index c9b25eb813..6b851d6dc9 100644
--- a/src/data/pokemon/species_info/gen_2_families.h
+++ b/src/data/pokemon/species_info/gen_2_families.h
@@ -194,12 +194,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Meganium,
.iconSprite = gMonIcon_Meganium,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_MeganiumF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 64),
.backPicFemale = gMonBackPic_MeganiumF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 13, SHADOW_SIZE_M)
FOOTPRINT(Meganium)
@@ -1055,12 +1055,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Ledyba,
.iconSprite = gMonIcon_Ledyba,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_LedybaF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 48),
.backPicFemale = gMonBackPic_LedybaF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(2, 4, SHADOW_SIZE_M)
FOOTPRINT(Ledyba)
@@ -1138,12 +1138,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Ledian,
.iconSprite = gMonIcon_Ledian,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_LedianF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 56),
.backPicFemale = gMonBackPic_LedianF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 15, SHADOW_SIZE_S)
FOOTPRINT(Ledian)
@@ -2477,12 +2477,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Sudowoodo,
.iconSprite = gMonIcon_Sudowoodo,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_SudowoodoF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 56),
.backPicFemale = gMonBackPic_SudowoodoF,
.backPicSizeFemale = MON_COORDS_SIZE(48, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-2, 7, SHADOW_SIZE_S)
FOOTPRINT(Sudowoodo)
@@ -2791,12 +2791,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Aipom,
.iconSprite = gMonIcon_Aipom,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_AipomF,
.frontPicSizeFemale = MON_COORDS_SIZE(32, 64),
.backPicFemale = gMonBackPic_AipomF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 12, SHADOW_SIZE_S)
FOOTPRINT(Aipom)
@@ -2875,12 +2875,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Ambipom,
.iconSprite = gMonIcon_Ambipom,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_AmbipomF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_AmbipomF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 13, SHADOW_SIZE_S)
FOOTPRINT(Ambipom)
@@ -3289,12 +3289,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Wooper,
.iconSprite = gMonIcon_Wooper,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_WooperF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 32),
.backPicFemale = gMonBackPic_WooperF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 40),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_SLOW,
SHADOW(1, -2, SHADOW_SIZE_S)
FOOTPRINT(Wooper)
@@ -3370,12 +3370,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Quagsire,
.iconSprite = gMonIcon_Quagsire,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_QuagsireF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_QuagsireF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 8, SHADOW_SIZE_M)
FOOTPRINT(Quagsire)
@@ -3585,12 +3585,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Murkrow,
.iconSprite = gMonIcon_Murkrow,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_MurkrowF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 40),
.backPicFemale = gMonBackPic_MurkrowF,
.backPicSizeFemale = MON_COORDS_SIZE(40, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-2, 8, SHADOW_SIZE_S)
FOOTPRINT(Murkrow)
@@ -4065,7 +4065,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Wobbuffet,
.iconSprite = gMonIcon_Wobbuffet,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_WobbuffetF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_WobbuffetF,
@@ -4074,7 +4074,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.iconSpriteFemale = gMonIcon_WobbuffetF,
.iconPalIndexFemale = 0,
#endif
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-3, 8, SHADOW_SIZE_M)
FOOTPRINT(Wobbuffet)
@@ -4151,12 +4151,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Girafarig,
.iconSprite = gMonIcon_Girafarig,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GirafarigF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 64),
.backPicFemale = gMonBackPic_GirafarigF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(2, 13, SHADOW_SIZE_M)
FOOTPRINT(Girafarig)
@@ -4649,12 +4649,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Gligar,
.iconSprite = gMonIcon_Gligar,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 2 : 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GligarF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
.backPicFemale = gMonBackPic_GligarF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 15, SHADOW_SIZE_S)
FOOTPRINT(Gligar)
@@ -5244,12 +5244,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Heracross,
.iconSprite = gMonIcon_Heracross,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 2 : 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_HeracrossF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_HeracrossF,
.backPicSizeFemale = MON_COORDS_SIZE(48, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 10, SHADOW_SIZE_M)
FOOTPRINT(Heracross)
@@ -5402,12 +5402,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Sneasel,
.iconSprite = gMonIcon_Sneasel,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_SneaselF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 48),
.backPicFemale = gMonBackPic_SneaselF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 5, SHADOW_SIZE_S)
FOOTPRINT(Sneasel)
@@ -5791,12 +5791,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Ursaring,
.iconSprite = gMonIcon_Ursaring,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_UrsaringF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_UrsaringF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(1, 14, SHADOW_SIZE_L)
FOOTPRINT(Ursaring)
@@ -6221,12 +6221,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Piloswine,
.iconSprite = gMonIcon_Piloswine,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_PiloswineF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 56),
.backPicFemale = gMonBackPic_PiloswineF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 3, SHADOW_SIZE_M)
FOOTPRINT(Piloswine)
@@ -6682,12 +6682,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Octillery,
.iconSprite = gMonIcon_Octillery,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_OctilleryF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 48),
.backPicFemale = gMonBackPic_OctilleryF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(1, 4, SHADOW_SIZE_M)
FOOTPRINT(Octillery)
@@ -7112,12 +7112,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Houndoom,
.iconSprite = gMonIcon_Houndoom,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_HoundoomF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_HoundoomF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-5, 13, SHADOW_SIZE_L)
FOOTPRINT(Houndoom)
@@ -7336,12 +7336,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
.shinyPalette = gMonShinyPalette_Donphan,
.iconSprite = gMonIcon_Donphan,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_DonphanF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 48),
.backPicFemale = gMonBackPic_DonphanF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(7, 2, SHADOW_SIZE_L)
FOOTPRINT(Donphan)
diff --git a/src/data/pokemon/species_info/gen_3_families.h b/src/data/pokemon/species_info/gen_3_families.h
index 8e52db9671..0a1fbaa426 100644
--- a/src/data/pokemon/species_info/gen_3_families.h
+++ b/src/data/pokemon/species_info/gen_3_families.h
@@ -339,10 +339,10 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Torchic,
.iconSprite = gMonIcon_Torchic,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.backPicFemale = gMonBackPic_TorchicF,
.backPicSizeFemale = MON_COORDS_SIZE(40, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_SLOW,
SHADOW(-1, 1, SHADOW_SIZE_S)
FOOTPRINT(Torchic)
@@ -418,12 +418,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Combusken,
.iconSprite = gMonIcon_Combusken,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_CombuskenF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 64),
.backPicFemale = gMonBackPic_CombuskenF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-1, 8, SHADOW_SIZE_M)
FOOTPRINT(Combusken)
@@ -505,12 +505,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Blaziken,
.iconSprite = gMonIcon_Blaziken,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_BlazikenF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 64),
.backPicFemale = gMonBackPic_BlazikenF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(4, 8, SHADOW_SIZE_M)
FOOTPRINT(Blaziken)
@@ -1614,12 +1614,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Beautifly,
.iconSprite = gMonIcon_Beautifly,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_BeautiflyF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_BeautiflyF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-5, 12, SHADOW_SIZE_S)
FOOTPRINT(Beautifly)
@@ -1793,12 +1793,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Dustox,
.iconSprite = gMonIcon_Dustox,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 5,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_DustoxF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 48),
.backPicFemale = gMonBackPic_DustoxF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-2, 11, SHADOW_SIZE_S)
FOOTPRINT(Dustox)
@@ -2024,12 +2024,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Ludicolo,
.iconSprite = gMonIcon_Ludicolo,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_LudicoloF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 64),
.backPicFemale = gMonBackPic_LudicoloF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-3, 14, SHADOW_SIZE_M)
FOOTPRINT(Ludicolo)
@@ -2186,12 +2186,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Nuzleaf,
.iconSprite = gMonIcon_Nuzleaf,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_NuzleafF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 56),
.backPicFemale = gMonBackPic_NuzleafF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-3, 5, SHADOW_SIZE_S)
FOOTPRINT(Nuzleaf)
@@ -2277,12 +2277,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Shiftry,
.iconSprite = gMonIcon_Shiftry,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 0 : 5,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_ShiftryF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_ShiftryF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-5, 5, SHADOW_SIZE_M)
FOOTPRINT(Shiftry)
@@ -5115,12 +5115,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Meditite,
.iconSprite = gMonIcon_Meditite,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_MedititeF,
.frontPicSizeFemale = MON_COORDS_SIZE(48, 48),
.backPicFemale = gMonBackPic_MedititeF,
.backPicSizeFemale = MON_COORDS_SIZE(48, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_SLOW,
SHADOW(0, 1, SHADOW_SIZE_S)
FOOTPRINT(Meditite)
@@ -5200,12 +5200,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Medicham,
.iconSprite = gMonIcon_Medicham,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_MedichamF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 64),
.backPicFemale = gMonBackPic_MedichamF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(-2, 13, SHADOW_SIZE_S)
FOOTPRINT(Medicham)
@@ -5944,12 +5944,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Roselia,
.iconSprite = gMonIcon_Roselia,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 0 : 4,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_RoseliaF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 48),
.backPicFemale = gMonBackPic_RoseliaF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_SLOW,
SHADOW(-2, 3, SHADOW_SIZE_S)
FOOTPRINT(Roselia)
@@ -6122,12 +6122,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Gulpin,
.iconSprite = gMonIcon_Gulpin,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_GulpinF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 48),
.backPicFemale = gMonBackPic_GulpinF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 48),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_SLOW,
SHADOW(1, -2, SHADOW_SIZE_S)
FOOTPRINT(Gulpin)
@@ -6206,12 +6206,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Swalot,
.iconSprite = gMonIcon_Swalot,
.iconPalIndex = 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_SwalotF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
.backPicFemale = gMonBackPic_SwalotF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(4, 3, SHADOW_SIZE_L)
FOOTPRINT(Swalot)
@@ -6656,12 +6656,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Numel,
.iconSprite = gMonIcon_Numel,
.iconPalIndex = 1,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_NumelF,
.frontPicSizeFemale = MON_COORDS_SIZE(40, 48),
.backPicFemale = gMonBackPic_NumelF,
.backPicSizeFemale = MON_COORDS_SIZE(56, 56),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_SLOW,
SHADOW(4, 2, SHADOW_SIZE_S)
FOOTPRINT(Numel)
@@ -6746,12 +6746,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Camerupt,
.iconSprite = gMonIcon_Camerupt,
.iconPalIndex = 0,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_CameruptF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 56),
.backPicFemale = gMonBackPic_CameruptF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 40),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(2, 4, SHADOW_SIZE_L)
FOOTPRINT(Camerupt)
@@ -8960,12 +8960,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Milotic,
.iconSprite = gMonIcon_Milotic,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 0 : 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_MiloticF,
.frontPicSizeFemale = MON_COORDS_SIZE(64, 64),
.backPicFemale = gMonBackPic_MiloticF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 64),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 12, SHADOW_SIZE_M)
FOOTPRINT(Milotic)
@@ -11020,12 +11020,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.shinyPalette = gMonShinyPalette_Relicanth,
.iconSprite = gMonIcon_Relicanth,
.iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 2,
-#if P_GENDER_DIFFERENCES
+#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.frontPicFemale = gMonFrontPic_RelicanthF,
.frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
.backPicFemale = gMonBackPic_RelicanthF,
.backPicSizeFemale = MON_COORDS_SIZE(64, 40),
-#endif //P_GENDER_DIFFERENCES
+#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX
.pokemonJumpType = PKMN_JUMP_TYPE_NONE,
SHADOW(0, 3, SHADOW_SIZE_M)
FOOTPRINT(Relicanth)
diff --git a/src/data/region_map/region_map_layout.h b/src/data/region_map/region_map_layout.h
index 95a4e94f31..32ce91724d 100644
--- a/src/data/region_map/region_map_layout.h
+++ b/src/data/region_map/region_map_layout.h
@@ -1,4 +1,4 @@
-static const u8 sRegionMap_MapSectionLayout[MAP_HEIGHT][MAP_WIDTH] = {
+static const mapsec_u8_t sRegionMap_MapSectionLayout[MAP_HEIGHT][MAP_WIDTH] = {
{MAPSEC_NONE, MAPSEC_ROUTE_114, MAPSEC_ROUTE_114, MAPSEC_FALLARBOR_TOWN, MAPSEC_ROUTE_113, MAPSEC_ROUTE_113, MAPSEC_ROUTE_113, MAPSEC_ROUTE_113, MAPSEC_ROUTE_111, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_ROUTE_119, MAPSEC_FORTREE_CITY, MAPSEC_ROUTE_120, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE},
{MAPSEC_NONE, MAPSEC_ROUTE_114, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_MT_CHIMNEY, MAPSEC_MT_CHIMNEY, MAPSEC_ROUTE_111, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_ROUTE_119, MAPSEC_NONE, MAPSEC_ROUTE_120, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE},
{MAPSEC_ROUTE_115, MAPSEC_ROUTE_114, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_MT_CHIMNEY, MAPSEC_MT_CHIMNEY, MAPSEC_ROUTE_111, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_ROUTE_119, MAPSEC_NONE, MAPSEC_ROUTE_120, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_SAFARI_ZONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE},
diff --git a/src/daycare.c b/src/daycare.c
index fdb98d6630..2dec8a81f1 100644
--- a/src/daycare.c
+++ b/src/daycare.c
@@ -1107,7 +1107,7 @@ void CreateEgg(struct Pokemon *mon, u16 species, bool8 setHotSpringsLocation)
u8 metLevel;
enum PokeBall ball;
u8 language;
- u8 metLocation;
+ metloc_u8_t metLocation;
u8 isEgg;
CreateMon(mon, species, EGG_HATCH_LEVEL, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0);
diff --git a/src/dexnav.c b/src/dexnav.c
index 2404d3bc17..1fcb87ce53 100644
--- a/src/dexnav.c
+++ b/src/dexnav.c
@@ -472,8 +472,8 @@ static void AddSearchWindow(u8 width)
}
#define WINDOW_COL_0 (SPECIES_ICON_X + 4)
-#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH)))
-#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * 6))
+#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(FONT_SMALL, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH)))
+#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(FONT_SMALL, FONTATTR_MAX_LETTER_WIDTH) * 6))
#define SEARCH_ARROW_X (WINDOW_MOVE_NAME_X + 90)
#define SEARCH_ARROW_Y 0
@@ -485,19 +485,19 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8
if (hidden)
{
StringCopy(gStringVar4, sText_ThreeQmarks);
- AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
return;
}
else
{
StringCopy(gStringVar1, GetSpeciesName(species));
- AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1);
+ AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1);
}
//level - always present
ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->monLevel, STR_CONV_MODE_LEFT_ALIGN, 3);
StringExpandPlaceholders(gStringVar4, sText_MonLevel);
- AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
if (proximity <= SNEAKING_PROXIMITY)
{
@@ -507,21 +507,21 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8
{
StringCopy(gStringVar1, GetMoveName(sDexNavSearchDataPtr->moves[0]));
StringExpandPlaceholders(gStringVar4, sText_EggMove);
- AddTextPrinterParameterized3(windowId, 0, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
}
if (searchLevel > 2)
{
// ability name
StringCopy(gStringVar1, gAbilitiesInfo[GetAbilityBySpecies(species, sDexNavSearchDataPtr->abilityNum)].name);
- AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1);
+ AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1);
// item name
if (sDexNavSearchDataPtr->heldItem)
{
CopyItemName(sDexNavSearchDataPtr->heldItem, gStringVar1);
StringExpandPlaceholders(gStringVar4, sText_HeldItem);
- AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
}
}
}
@@ -532,7 +532,7 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8
StringExpandPlaceholders(gStringVar4, sText_DexNavChainLong);
else
StringExpandPlaceholders(gStringVar4, sText_DexNavChain);
- AddTextPrinterParameterized3(windowId, 0, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(windowId, FONT_SMALL, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2);
}
@@ -959,7 +959,7 @@ static void DexNavUpdateDirectionArrow(void)
str = sText_ArrowDown; //player above
}
- AddTextPrinterParameterized3(windowId, 1, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str);
+ AddTextPrinterParameterized3(windowId, FONT_NORMAL, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str);
CopyWindowToVram(windowId, 2);
}
@@ -1517,7 +1517,7 @@ static u8 GetEncounterLevelFromMapData(u16 species, enum EncounterType environme
{
u32 headerId = GetCurrentMapWildMonHeaderId();
enum TimeOfDay timeOfDay;
- u8 min = 100;
+ u8 min = MAX_LEVEL;
u8 max = 0;
u8 i;
@@ -2133,9 +2133,9 @@ static void PrintCurrentSpeciesInfo(void)
//species name
if (species == SPECIES_NONE)
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo);
else
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species));
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species));
//type icon(s)
type1 = GetSpeciesType(species, 0);
@@ -2157,34 +2157,34 @@ static void PrintCurrentSpeciesInfo(void)
//search level
if (species == SPECIES_NONE)
{
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo);
}
else
{
ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(species), 0, 4);
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4);
}
//hidden ability
if (species == SPECIES_NONE)
{
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo);
}
else if (GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT))
{
if (GetSpeciesAbility(species, 2) != ABILITY_NONE)
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[GetSpeciesAbility(species, 2)].name);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[GetSpeciesAbility(species, 2)].name);
else
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gText_None);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, gText_None);
}
else
{
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee);
}
//current chain
ConvertIntToDecimalStringN(gStringVar1, gSaveBlock3Ptr->dexNavChain, STR_CONV_MODE_LEFT_ALIGN, 3);
- AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1);
+ AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1);
CopyWindowToVram(WINDOW_INFO, 3);
PutWindowTilemap(WINDOW_INFO);
@@ -2193,7 +2193,7 @@ static void PrintCurrentSpeciesInfo(void)
static void PrintMapName(void)
{
GetMapName(gStringVar3, GetCurrentRegionMapSectionId(), 0);
- AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 108 +
+ AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 108 +
GetStringRightAlignXOffset(1, gStringVar3, MAP_NAME_LENGTH * GetFontAttribute(1, FONTATTR_MAX_LETTER_WIDTH)),
0, sFontColor_White, 0, gStringVar3);
CopyWindowToVram(WINDOW_REGISTERED, 3);
@@ -2205,13 +2205,13 @@ static void PrintSearchableSpecies(u16 species)
PutWindowTilemap(WINDOW_REGISTERED);
if (species == SPECIES_NONE)
{
- AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister);
+ AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister);
}
else
{
StringCopy(gStringVar1, GetSpeciesName(species));
StringExpandPlaceholders(gStringVar4, sText_DexNav_SearchForRegisteredSpecies);
- AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4);
}
PrintMapName();
@@ -2651,11 +2651,11 @@ static void DrawSearchIcon(void)
static void DrawHiddenSearchWindow(u8 width)
{
AddSearchWindow(width);
- AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks);
+ AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks);
ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->searchLevel, STR_CONV_MODE_LEFT_ALIGN, 2);
StringExpandPlaceholders(gStringVar4, sText_SearchLevel);
- AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
+ AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4);
CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2);
}
diff --git a/src/dodrio_berry_picking.c b/src/dodrio_berry_picking.c
index 2593719022..f4b339d417 100644
--- a/src/dodrio_berry_picking.c
+++ b/src/dodrio_berry_picking.c
@@ -11,7 +11,6 @@
#include "link.h"
#include "link_rfu.h"
#include "m4a.h"
-#include "main.h"
#include "palette.h"
#include "minigame_countdown.h"
#include "random.h"
@@ -224,7 +223,7 @@ struct DodrioGame_ScoreResults
struct DodrioGame
{
- /*0x0000*/ void (*exitCallback)(void);
+ /*0x0000*/ MainCallback exitCallback;
/*0x0004*/ u8 ALIGNED(4) taskId;
/*0x0008*/ u8 ALIGNED(4) playersReceived;
/*0x000C*/ u8 ALIGNED(4) startState;
@@ -662,7 +661,7 @@ static void (*const sMemberFuncs[])(void) =
[FUNC_WAIT_END_GAME] = WaitEndGame_Member
};
-void StartDodrioBerryPicking(u16 partyId, void (*exitCallback)(void))
+void StartDodrioBerryPicking(u16 partyId, MainCallback exitCallback)
{
sExitingGame = FALSE;
diff --git a/src/egg_hatch.c b/src/egg_hatch.c
index 545b7d75ea..6f858c8f8d 100644
--- a/src/egg_hatch.c
+++ b/src/egg_hatch.c
@@ -365,7 +365,7 @@ static void AddHatchedMonToParty(u8 id)
enum NationalDexOrder species;
u8 name[POKEMON_NAME_LENGTH + 1];
u16 metLevel;
- u8 metLocation;
+ metloc_u8_t metLocation;
struct Pokemon *mon = &gPlayerParty[id];
CreateHatchedMon(mon, &gEnemyParty[0]);
diff --git a/src/event_object_movement.c b/src/event_object_movement.c
index 84f810e95a..0c17f5c445 100644
--- a/src/event_object_movement.c
+++ b/src/event_object_movement.c
@@ -1927,8 +1927,15 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati
x += MAP_OFFSET;
y += MAP_OFFSET;
SetSpritePosToOffsetMapCoords(&x, &y, 8, 16);
- if (spriteTemplate.paletteTag != TAG_NONE)
+ if (spriteTemplate.paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC)
+ {
+ u32 paletteNum = LoadDynamicFollowerPaletteFromGraphicsId(graphicsId, &spriteTemplate);
+ spriteTemplate.paletteTag = GetSpritePaletteTagByPaletteNum(paletteNum);
+ }
+ else if (spriteTemplate.paletteTag != TAG_NONE)
+ {
LoadObjectEventPalette(spriteTemplate.paletteTag);
+ }
spriteId = CreateSpriteAtEnd(&spriteTemplate, x, y, 0);
if (spriteId != MAX_SPRITES)
@@ -1942,6 +1949,9 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati
sprite->sVirtualObjId = virtualObjId;
sprite->sVirtualObjElev = elevation;
+ if (OW_GFX_COMPRESS && graphicsInfo->compressed)
+ spriteTemplate.tileTag = LoadSheetGraphicsInfo(graphicsInfo, graphicsId, sprite);
+
if (subspriteTables != NULL)
{
SetSubspriteTables(sprite, subspriteTables);
@@ -6591,7 +6601,7 @@ bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent)
static u8 TryUpdateMovementActionOnStairs(struct ObjectEvent *objectEvent, u8 movementActionId)
{
- if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER)
+ if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER || objectEvent->localId == OBJ_EVENT_ID_NPC_FOLLOWER)
return movementActionId; // handled separately
if (!ObjectMovingOnRockStairs(objectEvent, objectEvent->movementDirection))
diff --git a/src/field_specials.c b/src/field_specials.c
index 5b8b885932..716503b55c 100644
--- a/src/field_specials.c
+++ b/src/field_specials.c
@@ -22,12 +22,13 @@
#include "item.h"
#include "item_icon.h"
#include "link.h"
-#include "load_save.h"
#include "list_menu.h"
+#include "load_save.h"
#include "main.h"
-#include "mystery_gift.h"
#include "match_call.h"
#include "menu.h"
+#include "metatile_behavior.h"
+#include "mystery_gift.h"
#include "overworld.h"
#include "party_menu.h"
#include "pokeblock.h"
@@ -984,7 +985,7 @@ void FieldShowRegionMap(void)
static bool32 IsBuildingPCTile(u32 tileId)
{
- return gMapHeader.mapLayout->primaryTileset == &gTileset_Building && (tileId == METATILE_Building_PC_On || tileId == METATILE_Building_PC_Off);
+ return (MetatileBehavior_IsPC(UNPACK_BEHAVIOR(GetMetatileAttributesById(tileId))));
}
static bool32 IsPlayerHousePCTile(u32 tileId)
diff --git a/src/fldeff_misc.c b/src/fldeff_misc.c
index 673a8a9caa..883463a9aa 100644
--- a/src/fldeff_misc.c
+++ b/src/fldeff_misc.c
@@ -339,9 +339,9 @@ bool8 IsComputerScreenCloseEffectActive(void)
#define tBlendCnt data[7]
#define tBlendY data[8]
-static void CreateComputerScreenEffectTask(void (*taskfunc) (u8), u16 increment, u16 unused, u8 priority)
+static void CreateComputerScreenEffectTask(TaskFunc func, u16 increment, u16 unused, u8 priority)
{
- u8 taskId = CreateTask(taskfunc, priority);
+ u8 taskId = CreateTask(func, priority);
gTasks[taskId].tState = 0;
gTasks[taskId].tHorzIncrement = increment == 0 ? 16 : increment;
diff --git a/src/fonts.c b/src/fonts.c
index be183035d4..2ac19c7015 100644
--- a/src/fonts.c
+++ b/src/fonts.c
@@ -229,7 +229,7 @@ ALIGNED(4) const u8 gFontSmallNarrowerLatinGlyphWidths[] = {
4, 5, 6, 7, 4, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 2, 4, 2,
- 4, 4, 4, 2, 2, 4, 4, 8, 2, 8, 5, 4, 4, 4, 4, 4,
+ 4, 4, 4, 2, 2, 4, 4, 8, 2, 8, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 3, 4,
2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7,
diff --git a/src/frontier_pass.c b/src/frontier_pass.c
index 90699506d4..a81d236157 100644
--- a/src/frontier_pass.c
+++ b/src/frontier_pass.c
@@ -106,7 +106,7 @@ enum {
struct FrontierPassData
{
- void (*callback)(void);
+ MainCallback callback;
u16 state;
u16 battlePoints;
s16 cursorX;
@@ -137,14 +137,14 @@ struct FrontierPassGfx
struct FrontierPassSaved
{
- void (*callback)(void);
+ MainCallback callback;
s16 cursorX;
s16 cursorY;
};
struct FrontierMapData
{
- void (*callback)(void);
+ MainCallback callback;
struct Sprite *cursorSprite;
struct Sprite *playerHeadSprite;
struct Sprite *mapIndicatorSprite;
@@ -160,8 +160,8 @@ static EWRAM_DATA struct FrontierPassGfx *sPassGfx = NULL;
static EWRAM_DATA struct FrontierMapData *sMapData = NULL;
static EWRAM_DATA struct FrontierPassSaved sSavedPassData = {0};
-static u32 AllocateFrontierPassData(void (*callback)(void));
-static void ShowFrontierMap(void (*callback)(void));
+static u32 AllocateFrontierPassData(MainCallback callback);
+static void ShowFrontierMap(MainCallback callback);
static void CB2_InitFrontierPass(void);
static void DrawFrontierPassBg(void);
static void FreeCursorAndSymbolSprites(void);
@@ -604,9 +604,11 @@ static void LeaveFrontierPass(void)
FreeFrontierPassData();
}
-static u32 AllocateFrontierPassData(void (*callback)(void))
+static u32 AllocateFrontierPassData(MainCallback callback)
{
- u8 i;
+ // This variable is a MAPSEC initially, but is recycled as a
+ // bare integer near the end of the function.
+ mapsec_u8_t i;
if (sPassData != NULL)
return ERR_ALREADY_DONE;
@@ -1363,7 +1365,7 @@ static void PrintOnFrontierMap(void);
static void InitFrontierMapSprites(void);
static void HandleFrontierMapCursorMove(u8 direction);
-static void ShowFrontierMap(void (*callback)(void))
+static void ShowFrontierMap(MainCallback callback)
{
if (sMapData != NULL)
SetMainCallback2(callback); // This line doesn't make sense at all, since it gets overwritten later anyway.
diff --git a/src/item_menu.c b/src/item_menu.c
index 30c11f39fe..e0f7d156b3 100755
--- a/src/item_menu.c
+++ b/src/item_menu.c
@@ -24,7 +24,6 @@
#include "list_menu.h"
#include "link.h"
#include "mail.h"
-#include "main.h"
#include "malloc.h"
#include "map_name_popup.h"
#include "menu.h"
@@ -614,7 +613,7 @@ void CB2_ChooseMulch(void)
}
// Choosing berry for Berry Blender or Berry Crush
-void ChooseBerryForMachine(void (*exitCallback)(void))
+void ChooseBerryForMachine(MainCallback exitCallback)
{
GoToBagMenu(ITEMMENULOCATION_BERRY_BLENDER_CRUSH, POCKET_BERRIES, exitCallback);
}
@@ -648,7 +647,7 @@ void QuizLadyOpenBagMenu(void)
gSpecialVar_Result = FALSE;
}
-void GoToBagMenu(u8 location, u8 pocket, void ( *exitCallback)())
+void GoToBagMenu(u8 location, u8 pocket, MainCallback exitCallback)
{
gBagMenu = AllocZeroed(sizeof(*gBagMenu));
if (gBagMenu == NULL)
@@ -1208,7 +1207,7 @@ u8 GetItemListPosition(u8 pocketId)
return gBagPosition.scrollPosition[pocketId] + gBagPosition.cursorPosition[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)
{
s16 *data = gTasks[taskId].data;
diff --git a/src/item_use.c b/src/item_use.c
index d64aeb0762..90b89b6778 100644
--- a/src/item_use.c
+++ b/src/item_use.c
@@ -97,7 +97,7 @@ static const u8 sText_PlayedPokeFlute[] = _("吹响了宝可梦之笛!");
static const u8 sText_PokeFluteAwakenedMon[] = _("听到宝可梦之笛声音的\n宝可梦醒来了!{PAUSE_UNTIL_PRESS}");
// EWRAM variables
-EWRAM_DATA static void(*sItemUseOnFieldCB)(u8 taskId) = NULL;
+EWRAM_DATA static TaskFunc sItemUseOnFieldCB = NULL;
// Below is set TRUE by UseRegisteredKeyItemOnField
#define tUsingRegisteredKeyItem data[3]
@@ -128,7 +128,7 @@ static void SetUpItemUseCallback(u8 taskId)
type = gTasks[taskId].tEnigmaBerryType - 1;
else
type = GetItemType(gSpecialVar_ItemId) - 1;
-
+
if (gTasks[taskId].tUsingRegisteredKeyItem && type == (ITEM_USE_PARTY_MENU - 1))
{
FadeScreen(FADE_TO_BLACK, 0);
@@ -1291,15 +1291,18 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
}
break;
case EFFECT_ITEM_INCREASE_ALL_STATS:
+ {
+ u32 ability = GetBattlerAbility(gBattlerInMenuId);
for (i = STAT_ATK; i < NUM_STATS; i++)
{
- if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL))
+ if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability))
{
cannotUse = TRUE;
break;
}
}
break;
+ }
case EFFECT_ITEM_RESTORE_HP:
if (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP))
cannotUse = TRUE;
diff --git a/src/landmark.c b/src/landmark.c
index 8b6c17d87b..3ea57ec60a 100644
--- a/src/landmark.c
+++ b/src/landmark.c
@@ -10,7 +10,7 @@ struct Landmark
struct LandmarkList
{
- u8 mapSection;
+ mapsec_u8_t mapSection;
u8 id;
const struct Landmark *const *landmarks;
};
@@ -392,9 +392,9 @@ static const struct LandmarkList sLandmarkLists[] =
{MAPSEC_NONE, 0, NULL},
};
-static const struct Landmark *const *GetLandmarks(u8 mapSection, u8 id);
+static const struct Landmark *const *GetLandmarks(mapsec_u8_t mapSection, u8 id);
-const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count)
+const u8 *GetLandmarkName(mapsec_u8_t mapSection, u8 id, u8 count)
{
const struct Landmark *const *landmarks = GetLandmarks(mapSection, id);
@@ -421,7 +421,7 @@ const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count)
return (*landmarks)->name;
}
-static const struct Landmark *const *GetLandmarks(u8 mapSection, u8 id)
+static const struct Landmark *const *GetLandmarks(mapsec_u8_t mapSection, u8 id)
{
u16 i = 0;
diff --git a/src/mail.c b/src/mail.c
index 34d1520ef4..67accf3031 100644
--- a/src/mail.c
+++ b/src/mail.c
@@ -1,7 +1,6 @@
#include "global.h"
#include "mail.h"
#include "constants/items.h"
-#include "main.h"
#include "overworld.h"
#include "task.h"
#include "scanline_effect.h"
@@ -443,7 +442,7 @@ static const struct MailLayout sMailLayouts_Tall[] = {
},
};
-void ReadMail(struct Mail *mail, void (*exitCallback)(void), bool8 hasText)
+void ReadMail(struct Mail *mail, MainCallback exitCallback, bool8 hasText)
{
u16 buffer[2];
u16 species;
diff --git a/src/map_name_popup.c b/src/map_name_popup.c
index 0a3ea7cde7..3c6b915085 100644
--- a/src/map_name_popup.c
+++ b/src/map_name_popup.c
@@ -615,7 +615,7 @@ static void LoadMapNamePopUpWindowBg(void)
{
u8 popUpThemeId;
u8 popupWindowId = GetMapNamePopUpWindowId();
- u16 regionMapSectionId = gMapHeader.regionMapSectionId;
+ mapsec_u16_t regionMapSectionId = gMapHeader.regionMapSectionId;
u8 secondaryPopUpWindowId;
if (OW_POPUP_GENERATION == GEN_5)
diff --git a/src/match_call.c b/src/match_call.c
index 221714de0e..9c2022524b 100644
--- a/src/match_call.c
+++ b/src/match_call.c
@@ -135,7 +135,7 @@ static u32 GetCurrentTotalMinutes(struct Time *);
static u32 GetNumRegisteredTrainers(void);
static u32 GetActiveMatchCallTrainerId(u32);
static int GetTrainerMatchCallId(int);
-static u16 GetRematchTrainerLocation(int);
+static mapsec_u16_t GetRematchTrainerLocation(int);
static bool32 TrainerIsEligibleForRematch(int);
static void StartMatchCall(void);
static void ExecuteMatchCall(u8);
@@ -1468,7 +1468,7 @@ static bool32 TrainerIsEligibleForRematch(int matchCallId)
#endif //FREE_MATCH_CALL
}
-static u16 GetRematchTrainerLocation(int matchCallId)
+static mapsec_u16_t GetRematchTrainerLocation(int matchCallId)
{
const struct MapHeader *mapHeader = Overworld_GetMapHeaderByGroupAndId(gRematchTable[matchCallId].mapGroup, gRematchTable[matchCallId].mapNum);
return mapHeader->regionMapSectionId;
diff --git a/src/overworld.c b/src/overworld.c
index 8c2eaf5d88..e70cae880d 100644
--- a/src/overworld.c
+++ b/src/overworld.c
@@ -211,7 +211,7 @@ EWRAM_DATA struct WarpData gLastUsedWarp = {0};
EWRAM_DATA static struct WarpData sWarpDestination = {0}; // new warp position
EWRAM_DATA static struct WarpData sFixedDiveWarp = {0};
EWRAM_DATA static struct WarpData sFixedHoleWarp = {0};
-EWRAM_DATA static u16 sLastMapSectionId = 0;
+EWRAM_DATA static mapsec_u16_t sLastMapSectionId = 0;
EWRAM_DATA static struct InitialPlayerAvatarState sInitialPlayerAvatarState = {0};
EWRAM_DATA static u16 sAmbientCrySpecies = 0;
EWRAM_DATA static bool8 sIsAmbientCryWaterMon = FALSE;
@@ -1464,12 +1464,12 @@ bool8 IsMapTypeIndoors(enum MapType mapType)
return FALSE;
}
-u8 GetSavedWarpRegionMapSectionId(void)
+mapsec_u8_t GetSavedWarpRegionMapSectionId(void)
{
return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->dynamicWarp.mapGroup, gSaveBlock1Ptr->dynamicWarp.mapNum)->regionMapSectionId;
}
-u8 GetCurrentRegionMapSectionId(void)
+mapsec_u8_t GetCurrentRegionMapSectionId(void)
{
return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->regionMapSectionId;
}
@@ -1716,8 +1716,7 @@ static void OverworldBasic(void)
|| bld0[1] != bld1[1]
|| bld0[2] != bld1[2])
{
- UpdateAltBgPalettes(PALETTES_BG);
- UpdatePalettesWithTime(PALETTES_ALL);
+ ApplyWeatherColorMapIfIdle(gWeatherPtr->colorMapIndex);
}
}
}
@@ -1795,6 +1794,10 @@ void CB2_NewGame(void)
SetFieldVBlankCallback();
SetMainCallback1(CB1_Overworld);
SetMainCallback2(CB2_Overworld);
+#if OW_USE_FAKE_RTC
+ // Wall clock now track local time so we set it to 10AM to match initial wall clock time
+ RtcCalcLocalTimeOffset(0, 10, 0, 0);
+#endif
}
void CB2_WhiteOut(void)
diff --git a/src/palette.c b/src/palette.c
index 4054e007de..013a10db47 100644
--- a/src/palette.c
+++ b/src/palette.c
@@ -1271,7 +1271,7 @@ void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTa
{
u8 taskId;
- taskId = CreateTask((void *)Task_BlendPalettesGradually, priority);
+ taskId = CreateTask(Task_BlendPalettesGradually, priority);
gTasks[taskId].tCoeff = coeff;
gTasks[taskId].tCoeffTarget = coeffTarget;
diff --git a/src/party_menu.c b/src/party_menu.c
index c37a5e5f92..e9455692bb 100644
--- a/src/party_menu.c
+++ b/src/party_menu.c
@@ -273,6 +273,7 @@ static bool8 IsMonAllowedInMinigame(u8);
static void DisplayPartyPokemonDataToTeachMove(u8, u16);
static u8 CanTeachMove(struct Pokemon *, u16);
static void DisplayPartyPokemonBarDetail(u8, const u8 *, u8, const u8 *);
+static void DisplayPartyPokemonBarDetailToFit(u8 windowId, const u8 *str, u8 color, const u8 *align, u32 width);
static void DisplayPartyPokemonLevel(u8, struct PartyMenuBox *);
static void DisplayPartyPokemonGender(u8, u16, u8 *, struct PartyMenuBox *);
static void DisplayPartyPokemonHP(u16 hp, u16 maxHp, struct PartyMenuBox *menuBox);
@@ -1167,7 +1168,7 @@ static void DisplayPartyPokemonDataForMultiBattle(u8 slot)
StringCopy(gStringVar1, gMultiPartnerParty[actualSlot].nickname);
StringGet_Nickname(gStringVar1);
ConvertInternationalPlayerName(gStringVar1);
- DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions);
+ DisplayPartyPokemonBarDetailToFit(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions, 50);
DisplayPartyPokemonLevel(gMultiPartnerParty[actualSlot].level, menuBox);
DisplayPartyPokemonGender(gMultiPartnerParty[actualSlot].gender, gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].nickname, menuBox);
DisplayPartyPokemonHP(gMultiPartnerParty[actualSlot].hp, gMultiPartnerParty[actualSlot].maxhp, menuBox);
@@ -1510,12 +1511,14 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
}
case PARTY_ACTION_SEND_MON_TO_BOX:
{
- u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr);
- if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 2)
- || ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)))
+ u8 partyId = (u8)*slotPtr;
+ if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2))
{
- // Can't select if mon is currently on the field, or doesn't belong to you
+ // Can't select if mon doesn't belong to you
PlaySE(SE_FAILURE);
+ DisplayPartyMenuMessage(gText_CannotSendMonToBoxPartner, FALSE);
+ ScheduleBgCopyTilemapToVram(2);
+ gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
}
else if (DoesSelectedMonKnowHM((u8 *)slotPtr))
{
@@ -1527,7 +1530,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
else
{
PlaySE(SE_SELECT);
- gSelectedMonPartyId = partyId;
+ gSelectedMonPartyId = GetPartyIdFromBattleSlot(partyId);
Task_ClosePartyMenu(taskId);
}
break;
@@ -6420,11 +6423,11 @@ static void Task_TryItemUseFusionChange(u8 taskId)
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
- SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, FUSE_MON);
+ SwapFusionMonMoves(mon, gKyuremWhiteSwapMoveTable, FUSE_MON);
#endif //P_FAMILY_RESHIRAM
#if P_FAMILY_ZEKROM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK)
- SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, FUSE_MON);
+ SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, FUSE_MON);
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
if (gTasks[taskId].moveToLearn != 0)
@@ -6435,11 +6438,11 @@ static void Task_TryItemUseFusionChange(u8 taskId)
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
- SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, UNFUSE_MON);
+ SwapFusionMonMoves(mon, gKyuremWhiteSwapMoveTable, UNFUSE_MON);
#endif //P_FAMILY_RESHIRAM
#if P_FAMILY_ZEKROM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK)
- SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, UNFUSE_MON);
+ SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, UNFUSE_MON);
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES)
diff --git a/src/pokeblock.c b/src/pokeblock.c
index e650472763..05404ef348 100644
--- a/src/pokeblock.c
+++ b/src/pokeblock.c
@@ -62,7 +62,7 @@ enum {
struct PokeblockMenuStruct
{
u8 tilemap[BG_SCREEN_SIZE];
- void (*callbackOnUse)(void);
+ MainCallback callbackOnUse;
const u8 *pokeblockActionIds;
u8 numActions;
u8 caseId;
@@ -80,7 +80,7 @@ struct PokeblockMenuStruct
struct PokeblockSavedData
{
- void (*callback)(void);
+ MainCallback callback;
u16 selectedRow;
u16 scrollOffset;
};
diff --git a/src/pokedex_area_screen.c b/src/pokedex_area_screen.c
index 4fff8bbaa3..8d2f0b8c50 100644
--- a/src/pokedex_area_screen.c
+++ b/src/pokedex_area_screen.c
@@ -75,7 +75,7 @@ struct OverworldArea
{
u8 mapGroup;
u8 mapNum;
- u16 regionMapSectionId;
+ mapsec_u16_t regionMapSectionId;
};
struct
@@ -96,7 +96,7 @@ struct
/*0x61C*/ u16 areaShadeBldArgHi;
/*0x61E*/ bool8 showingMarkers;
/*0x61F*/ u8 markerFlashCounter;
- /*0x620*/ u16 specialAreaRegionMapSectionIds[MAX_AREA_MARKERS];
+ /*0x620*/ mapsec_u16_t specialAreaRegionMapSectionIds[MAX_AREA_MARKERS];
/*0x660*/ struct Sprite *areaMarkerSprites[MAX_AREA_MARKERS];
/*0x6E0*/ u16 numAreaMarkerSprites;
/*0x6E2*/ u16 alteringCaveCounter;
@@ -116,7 +116,7 @@ static void FindMapsWithMon(u16);
static void BuildAreaGlowTilemap(void);
static void SetAreaHasMon(u16, u16);
static void SetSpecialMapHasMon(u16, u16);
-static u16 GetRegionMapSectionId(u8, u8);
+static mapsec_u16_t GetRegionMapSectionId(u8, u8);
static bool8 MapHasSpecies(const struct WildEncounterTypes *, u16);
static bool8 MonListHasSpecies(const struct WildPokemonInfo *, u16, u16);
static void DoAreaGlow(void);
@@ -144,7 +144,7 @@ static void LoadHGSSScreenSelectBarSubmenu(void);
static const u16 sSpeciesHiddenFromAreaScreen[] = { SPECIES_WYNAUT };
-static const u16 sMovingRegionMapSections[3] =
+static const mapsec_u16_t sMovingRegionMapSections[3] =
{
MAPSEC_MARINE_CAVE,
MAPSEC_UNDERWATER_MARINE_CAVE,
@@ -157,7 +157,7 @@ static const u16 sFeebasData[][3] =
{NUM_SPECIES}
};
-static const u16 sLandmarkData[][2] =
+static const mapsec_u16_t sLandmarkData[][2] =
{
{MAPSEC_SKY_PILLAR, FLAG_LANDMARK_SKY_PILLAR},
{MAPSEC_SEAFLOOR_CAVERN, FLAG_LANDMARK_SEAFLOOR_CAVERN},
@@ -389,7 +389,7 @@ static void SetSpecialMapHasMon(u16 mapGroup, u16 mapNum)
if (sPokedexAreaScreen->numSpecialAreas < MAX_AREA_MARKERS)
{
- u16 regionMapSectionId = GetRegionMapSectionId(mapGroup, mapNum);
+ mapsec_u16_t regionMapSectionId = GetRegionMapSectionId(mapGroup, mapNum);
if (regionMapSectionId < MAPSEC_NONE)
{
// Don't highlight the area if it's a moving area (Marine/Terra Cave)
@@ -423,7 +423,7 @@ static void SetSpecialMapHasMon(u16 mapGroup, u16 mapNum)
}
}
-static u16 GetRegionMapSectionId(u8 mapGroup, u8 mapNum)
+static mapsec_u16_t GetRegionMapSectionId(u8 mapGroup, u8 mapNum)
{
return Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->regionMapSectionId;
}
@@ -947,7 +947,7 @@ static void CreateAreaMarkerSprites(void)
s16 x;
s16 y;
s16 i;
- s16 mapSecId;
+ mapsec_u16_t mapSecId;
s16 numSprites;
LoadSpriteSheet(&sAreaMarkerSpriteSheet);
diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c
index dc29ce4b8f..c88795693c 100644
--- a/src/pokedex_plus_hgss.c
+++ b/src/pokedex_plus_hgss.c
@@ -2423,6 +2423,8 @@ static bool8 LoadPokedexListPage(u8 page)
// when returning to search results after selecting an evo, we have to restore
// the original dexNum because the search results page doesn't rebuild the list
sPokedexListItem->dexNum = sPokedexView->originalSearchSelectionNum;
+ sPokedexListItem->seen = GetSetPokedexFlag(sPokedexView->originalSearchSelectionNum, FLAG_GET_SEEN);
+ sPokedexListItem->owned = GetSetPokedexFlag(sPokedexView->originalSearchSelectionNum, FLAG_GET_CAUGHT);
sPokedexView->originalSearchSelectionNum = 0;
}
CreateMonSpritesAtPos(sPokedexView->selectedPokemon, 0xE);
@@ -4540,7 +4542,7 @@ static u32 GetPokedexMonPersonality(u16 species)
static u16 CreateMonSpriteFromNationalDexNumberHGSS(u16 nationalNum, s16 x, s16 y, u16 paletteSlot)
{
u32 species = NationalPokedexNumToSpeciesHGSS(nationalNum);
- return CreateMonPicSprite(nationalNum, FALSE, GetPokedexMonPersonality(species), TRUE, x, y, paletteSlot, TAG_NONE);
+ return CreateMonPicSprite(species, FALSE, GetPokedexMonPersonality(species), TRUE, x, y, paletteSlot, TAG_NONE);
}
static u16 GetPokemonScaleFromNationalDexNumber(u16 nationalNum)
@@ -6335,6 +6337,17 @@ static void HandlePreEvolutionSpeciesPrint(u8 taskId, u16 preSpecies, u16 specie
}
}
+static bool32 HasTwoPreEvolutions(u32 species)
+{
+ switch (species)
+ {
+ case SPECIES_GHOLDENGO:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
static u8 PrintPreEvolutions(u8 taskId, u16 species)
{
u16 i;
@@ -6386,13 +6399,39 @@ static u8 PrintPreEvolutions(u8 taskId, u16 species)
{
if (evolutions[j].targetSpecies == species)
{
- preEvolutionOne = i;
- numPreEvolutions += 1;
- break;
+ if (numPreEvolutions == 0)
+ {
+ preEvolutionOne = i;
+ numPreEvolutions += 1;
+ if (!HasTwoPreEvolutions(species))
+ break;
+ }
+ else
+ {
+ preEvolutionTwo = i;
+ numPreEvolutions += 1;
+ break;
+ }
}
}
}
+ if (HasTwoPreEvolutions(species))
+ {
+ CreateCaughtBallEvolutionScreen(preEvolutionOne, base_x - 9, base_y + base_y_offset*0, 0);
+ HandlePreEvolutionSpeciesPrint(taskId, preEvolutionOne, species, base_x, base_y, base_y_offset, 0);
+
+ CreateCaughtBallEvolutionScreen(preEvolutionTwo, base_x - 9, base_y + base_y_offset*(numPreEvolutions - 1), 0);
+ HandlePreEvolutionSpeciesPrint(taskId, preEvolutionTwo, species, base_x, base_y, base_y_offset, numPreEvolutions - 1);
+
+ sPokedexView->sEvoScreenData.targetSpecies[0] = preEvolutionOne;
+ sPokedexView->sEvoScreenData.targetSpecies[1] = preEvolutionTwo;
+
+ sPokedexView->numPreEvolutions = numPreEvolutions;
+ sPokedexView->sEvoScreenData.numAllEvolutions += numPreEvolutions;
+ return numPreEvolutions;
+ }
+
//Calculate if previous evolution also has a previous evolution
if (numPreEvolutions != 0)
{
diff --git a/src/pokemon.c b/src/pokemon.c
index b6529ad615..c356ac8a97 100644
--- a/src/pokemon.c
+++ b/src/pokemon.c
@@ -1691,32 +1691,10 @@ static u16 CalculateBoxMonChecksumReencrypt(struct BoxPokemon *boxMon)
return checksum;
}
-#define CALC_STAT(base, iv, ev, statIndex, field) \
-{ \
- u8 baseStat = gSpeciesInfo[species].base; \
- s32 n = (((2 * baseStat + iv + ev / 4) * level) / 100) + 5; \
- n = ModifyStatByNature(nature, n, statIndex); \
- if (B_FRIENDSHIP_BOOST == TRUE) \
- n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100));\
- SetMonData(mon, field, &n); \
-}
-
void CalculateMonStats(struct Pokemon *mon)
{
s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL);
s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL);
- s32 hpIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_HP_IV, NULL);
- s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
- s32 attackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_ATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_ATK_IV, NULL);
- s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
- s32 defenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_DEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_DEF_IV, NULL);
- s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
- s32 speedIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPEED) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPEED_IV, NULL);
- s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
- s32 spAttackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPATK_IV, NULL);
- s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
- s32 spDefenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPDEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
- s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
u8 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
s32 level = GetLevelFromMonExp(mon);
@@ -1726,28 +1704,55 @@ void CalculateMonStats(struct Pokemon *mon)
SetMonData(mon, MON_DATA_LEVEL, &level);
+ bool32 hyperTrained[NUM_STATS]; //In a battle test, hyper training flag indicates a fixed stat
+ s32 iv[NUM_STATS];
+ s32 ev[NUM_STATS];
+ for (u32 i = 0; i < NUM_STATS; i++)
+ {
+ hyperTrained[i] = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + i);
+ iv[i] = GetMonData(mon, MON_DATA_HP_IV + i);
+ ev[i] = GetMonData(mon, MON_DATA_HP_EV + i);
+
+ if (hyperTrained[i])
+ {
+ #if TESTING
+ if (gMain.inBattle)
+ continue;
+ #endif
+ iv[i] = MAX_PER_STAT_IVS;
+ }
+
+ if (i == STAT_HP)
+ continue;
+
+ u8 baseStat = GetSpeciesBaseStat(species, i);
+ s32 n = (((2 * baseStat + iv[i] + ev[i] / 4) * level) / 100) + 5;
+ n = ModifyStatByNature(nature, n, i);
+ if (B_FRIENDSHIP_BOOST == TRUE)
+ n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100));
+ SetMonData(mon, MON_DATA_MAX_HP + i, &n);
+ }
+
+#if TESTING
+ if (hyperTrained[STAT_HP] && gMain.inBattle)
+ return;
+#endif
+
if (species == SPECIES_SHEDINJA)
{
newMaxHP = 1;
}
else
{
- s32 n = 2 * GetSpeciesBaseHP(species) + hpIV;
- newMaxHP = (((n + hpEV / 4) * level) / 100) + level + 10;
+ s32 n = 2 * GetSpeciesBaseHP(species) + iv[STAT_HP];
+ newMaxHP = (((n + ev[STAT_HP] / 4) * level) / 100) + level + 10;
}
gBattleScripting.levelUpHP = newMaxHP - oldMaxHP;
if (gBattleScripting.levelUpHP == 0)
gBattleScripting.levelUpHP = 1;
-
SetMonData(mon, MON_DATA_MAX_HP, &newMaxHP);
- CALC_STAT(baseAttack, attackIV, attackEV, STAT_ATK, MON_DATA_ATK)
- CALC_STAT(baseDefense, defenseIV, defenseEV, STAT_DEF, MON_DATA_DEF)
- CALC_STAT(baseSpeed, speedIV, speedEV, STAT_SPEED, MON_DATA_SPEED)
- CALC_STAT(baseSpAttack, spAttackIV, spAttackEV, STAT_SPATK, MON_DATA_SPATK)
- CALC_STAT(baseSpDefense, spDefenseIV, spDefenseEV, STAT_SPDEF, MON_DATA_SPDEF)
-
// Since a pokemon's maxHP data could either not have
// been initialized at this point or this pokemon is
// just fainted, the check for oldMaxHP is important.
@@ -2837,6 +2842,19 @@ u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field)
#define SET8(lhs) (lhs) = *data
#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
+//
+// Prefer SET_BY_WIDTH for fields whose types might be extended (e.g.
+// anything whose typedef is in gametypes.h).
+//
+#define SET_BY_WIDTH(lhs) \
+ do { \
+ if (sizeof(lhs) == 1) \
+ SET8(lhs); \
+ else if (sizeof(lhs) == 2) \
+ SET16(lhs); \
+ else if (sizeof(lhs) == 4) \
+ SET32(lhs); \
+ } while (0)
void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg)
{
@@ -3567,6 +3585,26 @@ u32 GetSpeciesBaseSpeed(u16 species)
return gSpeciesInfo[SanitizeSpeciesId(species)].baseSpeed;
}
+u32 GetSpeciesBaseStat(u16 species, u32 statIndex)
+{
+ switch (statIndex)
+ {
+ case STAT_HP:
+ return GetSpeciesBaseHP(species);
+ case STAT_ATK:
+ return GetSpeciesBaseAttack(species);
+ case STAT_DEF:
+ return GetSpeciesBaseDefense(species);
+ case STAT_SPEED:
+ return GetSpeciesBaseSpeed(species);
+ case STAT_SPATK:
+ return GetSpeciesBaseSpAttack(species);
+ case STAT_SPDEF:
+ return GetSpeciesBaseSpDefense(species);
+ }
+ return 0;
+}
+
const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species)
{
const struct LevelUpMove *learnset = gSpeciesInfo[SanitizeSpeciesId(species)].levelUpLearnset;
diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c
index fe663e0fde..cf2d2e14b4 100644
--- a/src/pokemon_summary_screen.c
+++ b/src/pokemon_summary_screen.c
@@ -142,7 +142,7 @@ static EWRAM_DATA struct PokemonSummaryScreenData
u8 ribbonCount; // 0x6
u8 ailment; // 0x7
u8 abilityNum; // 0x8
- u8 metLocation; // 0x9
+ metloc_u8_t metLocation; // 0x9
u8 metLevel; // 0xA
u8 metGame; // 0xB
u32 pid; // 0xC
@@ -334,6 +334,7 @@ static const u8 *GetLetterGrade(u32 stat);
static u8 AddWindowFromTemplateList(const struct WindowTemplate *template, u8 templateId);
static u8 IncrementSkillsStatsMode(u8 mode);
static void ClearStatLabel(u32 length, u32 statsCoordX, u32 statsCoordY);
+u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat);
static const struct BgTemplate sBgTemplates[] =
{
@@ -734,7 +735,7 @@ static void (*const sTextPrinterFunctions[])(void) =
[PSS_PAGE_CONTEST_MOVES] = PrintContestMoves
};
-static void (*const sTextPrinterTasks[])(u8 taskId) =
+static const TaskFunc sTextPrinterTasks[] =
{
[PSS_PAGE_INFO] = Task_PrintInfoPage,
[PSS_PAGE_SKILLS] = Task_PrintSkillsPage,
@@ -1173,6 +1174,13 @@ static void DestroyCategoryIcon(void)
sMonSummaryScreen->categoryIconSpriteId = 0xFF;
}
+u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat)
+{
+ if (P_SUMMARY_SCREEN_IV_HYPERTRAIN && GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + stat))
+ return MAX_PER_STAT_IVS;
+ return GetMonData(mon, MON_DATA_HP_IV + stat);
+}
+
void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void))
{
sMonSummaryScreen = AllocZeroed(sizeof(*sMonSummaryScreen));
@@ -1864,12 +1872,12 @@ void ExtractMonSkillStatsData(struct Pokemon *mon, struct PokeSummary *sum)
void ExtractMonSkillIvData(struct Pokemon *mon, struct PokeSummary *sum)
{
- sum->currentHP = GetMonData(mon, MON_DATA_HP_IV);
- sum->atk = GetMonData(mon, MON_DATA_ATK_IV);
- sum->def = GetMonData(mon, MON_DATA_DEF_IV);
- sum->spatk = GetMonData(mon, MON_DATA_SPATK_IV);
- sum->spdef = GetMonData(mon, MON_DATA_SPDEF_IV);
- sum->speed = GetMonData(mon, MON_DATA_SPEED_IV);
+ sum->currentHP = GetAdjustedIvData(mon, STAT_HP);
+ sum->atk = GetAdjustedIvData(mon, STAT_ATK);
+ sum->def = GetAdjustedIvData(mon, STAT_DEF);
+ sum->spatk = GetAdjustedIvData(mon, STAT_SPATK);
+ sum->spdef = GetAdjustedIvData(mon, STAT_SPDEF);
+ sum->speed = GetAdjustedIvData(mon, STAT_SPEED);
}
void ExtractMonSkillEvData(struct Pokemon *mon, struct PokeSummary *sum)
@@ -3792,18 +3800,18 @@ static const u8 *GetLetterGrade(u32 stat)
static const u8 gText_GradeA[] = _("A");
static const u8 gText_GradeS[] = _("S");
- if (stat > 0 && stat <= 15)
- return gText_GradeD;
- else if (stat > 15 && stat <= 25)
- return gText_GradeC;
- else if (stat > 26 && stat <= 29)
- return gText_GradeB;
- else if (stat == 30)
- return gText_GradeA;
- else if (stat == 31)
- return gText_GradeS;
- else
+ if (stat <= 0)
return gText_GradeF;
+ else if (stat <= 15)
+ return gText_GradeD;
+ else if (stat <= 25)
+ return gText_GradeC;
+ else if (stat <= 29)
+ return gText_GradeB;
+ else if (stat <= 30)
+ return gText_GradeA;
+ else
+ return gText_GradeS;
}
static void BufferLeftColumnStats(void)
diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c
index 655d28a2d7..114f3bb813 100644
--- a/src/pokenav_match_call_data.c
+++ b/src/pokenav_match_call_data.c
@@ -34,13 +34,13 @@ typedef struct MatchCallTextDataStruct {
struct MatchCallStructCommon {
u8 type;
- u8 mapSec;
+ mapsec_u8_t mapSec;
u16 flag;
};
struct MatchCallStructNPC {
u8 type;
- u8 mapSec;
+ mapsec_u8_t mapSec;
u16 flag;
const u8 *desc;
const u8 *name;
@@ -50,7 +50,7 @@ struct MatchCallStructNPC {
// Shared by MC_TYPE_TRAINER and MC_TYPE_LEADER
struct MatchCallStructTrainer {
u8 type;
- u8 mapSec;
+ mapsec_u8_t mapSec;
u16 flag;
u16 rematchTableIdx;
const u8 *desc;
@@ -60,12 +60,12 @@ struct MatchCallStructTrainer {
struct MatchCallLocationOverride {
u16 flag;
- u8 mapSec;
+ mapsec_u8_t mapSec;
};
struct MatchCallWally {
u8 type;
- u8 mapSec;
+ mapsec_u8_t mapSec;
u16 flag;
u16 rematchTableIdx;
const u8 *desc;
@@ -75,7 +75,7 @@ struct MatchCallWally {
struct MatchCallBirch {
u8 type;
- u8 mapSec;
+ mapsec_u8_t mapSec;
u16 flag;
const u8 *desc;
const u8 *name;
@@ -117,11 +117,11 @@ static bool32 MatchCall_GetEnabled_Wally(match_call_t);
static bool32 MatchCall_GetEnabled_Birch(match_call_t);
static bool32 MatchCall_GetEnabled_Rival(match_call_t);
-static u8 MatchCall_GetMapSec_NPC(match_call_t);
-static u8 MatchCall_GetMapSec_Trainer(match_call_t);
-static u8 MatchCall_GetMapSec_Wally(match_call_t);
-static u8 MatchCall_GetMapSec_Birch(match_call_t);
-static u8 MatchCall_GetMapSec_Rival(match_call_t);
+static mapsec_u8_t MatchCall_GetMapSec_NPC(match_call_t);
+static mapsec_u8_t MatchCall_GetMapSec_Trainer(match_call_t);
+static mapsec_u8_t MatchCall_GetMapSec_Wally(match_call_t);
+static mapsec_u8_t MatchCall_GetMapSec_Birch(match_call_t);
+static mapsec_u8_t MatchCall_GetMapSec_Rival(match_call_t);
static bool32 MatchCall_IsRematchable_NPC(match_call_t);
static bool32 MatchCall_IsRematchable_Trainer(match_call_t);
@@ -613,7 +613,7 @@ static bool32 (*const sMatchCallGetEnabledFuncs[])(match_call_t) = {
MatchCall_GetEnabled_Birch
};
-static u8 (*const sMatchCallGetMapSecFuncs[])(match_call_t) = {
+static mapsec_u8_t (*const sMatchCallGetMapSecFuncs[])(match_call_t) = {
MatchCall_GetMapSec_NPC,
MatchCall_GetMapSec_Trainer,
MatchCall_GetMapSec_Wally,
@@ -796,7 +796,7 @@ static bool32 MatchCall_GetEnabled_Birch(match_call_t matchCall)
return FlagGet(matchCall.birch->flag);
}
-u8 MatchCall_GetMapSec(u32 idx)
+mapsec_u8_t MatchCall_GetMapSec(u32 idx)
{
match_call_t matchCall;
u32 i;
@@ -808,17 +808,17 @@ u8 MatchCall_GetMapSec(u32 idx)
return sMatchCallGetMapSecFuncs[i](matchCall);
}
-static u8 MatchCall_GetMapSec_NPC(match_call_t matchCall)
+static mapsec_u8_t MatchCall_GetMapSec_NPC(match_call_t matchCall)
{
return matchCall.npc->mapSec;
}
-static u8 MatchCall_GetMapSec_Trainer(match_call_t matchCall)
+static mapsec_u8_t MatchCall_GetMapSec_Trainer(match_call_t matchCall)
{
return matchCall.trainer->mapSec;
}
-static u8 MatchCall_GetMapSec_Wally(match_call_t matchCall)
+static mapsec_u8_t MatchCall_GetMapSec_Wally(match_call_t matchCall)
{
s32 i;
@@ -830,12 +830,12 @@ static u8 MatchCall_GetMapSec_Wally(match_call_t matchCall)
return matchCall.wally->locationData[i].mapSec;
}
-static u8 MatchCall_GetMapSec_Rival(match_call_t matchCall)
+static mapsec_u8_t MatchCall_GetMapSec_Rival(match_call_t matchCall)
{
return MAPSEC_NONE;
}
-static u8 MatchCall_GetMapSec_Birch(match_call_t matchCall)
+static mapsec_u8_t MatchCall_GetMapSec_Birch(match_call_t matchCall)
{
return MAPSEC_NONE;
}
diff --git a/src/pokenav_match_call_gfx.c b/src/pokenav_match_call_gfx.c
index f58c7ca2e4..58758d17b2 100755
--- a/src/pokenav_match_call_gfx.c
+++ b/src/pokenav_match_call_gfx.c
@@ -1027,7 +1027,7 @@ static void PrintMatchCallLocation(struct Pokenav_MatchCallGfx *gfx, int delta)
u8 mapName[32];
int x;
int index = PokenavList_GetSelectedIndex() + delta;
- int mapSec = GetMatchCallMapSec(index);
+ mapsec_s32_t mapSec = GetMatchCallMapSec(index);
if (mapSec != MAPSEC_NONE)
GetMapName(mapName, mapSec, 0);
else
diff --git a/src/pokenav_match_call_list.c b/src/pokenav_match_call_list.c
index e919d72efe..e257b56c1e 100755
--- a/src/pokenav_match_call_list.c
+++ b/src/pokenav_match_call_list.c
@@ -308,7 +308,7 @@ struct PokenavMatchCallEntry *GetMatchCallList(void)
return state->matchCallEntries;
}
-u16 GetMatchCallMapSec(int index)
+mapsec_u16_t GetMatchCallMapSec(int index)
{
struct Pokenav_MatchCallMenu *state = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_MAIN);
return state->matchCallEntries[index].mapSec;
@@ -430,7 +430,7 @@ void BufferMatchCallNameAndDesc(struct PokenavMatchCallEntry *matchCallEntry, u8
}
}
-u8 GetMatchTableMapSectionId(int rematchIndex)
+mapsec_u8_t GetMatchTableMapSectionId(int rematchIndex)
{
int mapGroup = gRematchTable[rematchIndex].mapGroup;
int mapNum = gRematchTable[rematchIndex].mapNum;
diff --git a/src/pokenav_region_map.c b/src/pokenav_region_map.c
index 750a9b5b76..633ecdb056 100755
--- a/src/pokenav_region_map.c
+++ b/src/pokenav_region_map.c
@@ -44,7 +44,7 @@ struct Pokenav_RegionMapGfx
struct CityMapEntry
{
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
u16 index;
const u32 *tilemap;
};
@@ -66,8 +66,8 @@ static bool32 IsDma3ManagerBusyWithBgCopy_(struct Pokenav_RegionMapGfx *);
static void ChangeBgYForZoom(bool32);
static bool32 IsChangeBgYForZoomActive(void);
static void CreateCityZoomTextSprites(void);
-static void DrawCityMap(struct Pokenav_RegionMapGfx *, int, int);
-static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *, int, int);
+static void DrawCityMap(struct Pokenav_RegionMapGfx *, mapsec_s32_t, int);
+static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *, mapsec_s32_t, int);
static void SetCityZoomTextInvisibility(bool32);
static void Task_ChangeBgYForZoom(u8 taskId);
static void UpdateCityZoomTextPosition(void);
@@ -664,7 +664,7 @@ static u32 LoopedTask_DecompressCityMaps(s32 taskState)
return LT_FINISH;
}
-static void DrawCityMap(struct Pokenav_RegionMapGfx *state, int mapSecId, int pos)
+static void DrawCityMap(struct Pokenav_RegionMapGfx *state, mapsec_s32_t mapSecId, int pos)
{
int i;
for (i = 0; i < NUM_CITY_MAPS && (sPokenavCityMaps[i].mapSecId != mapSecId || sPokenavCityMaps[i].index != pos); i++)
@@ -677,7 +677,7 @@ static void DrawCityMap(struct Pokenav_RegionMapGfx *state, int mapSecId, int po
CopyToBgTilemapBufferRect(1, state->cityZoomPics[i], 18, 6, 10, 10);
}
-static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *state, int mapSecId, int pos)
+static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *state, mapsec_s32_t mapSecId, int pos)
{
int i = 0;
while (1)
diff --git a/src/pokenav_ribbons_summary.c b/src/pokenav_ribbons_summary.c
index 9bd477726c..30a39222bb 100644
--- a/src/pokenav_ribbons_summary.c
+++ b/src/pokenav_ribbons_summary.c
@@ -585,7 +585,11 @@ static u32 LoopedTask_OpenRibbonsSummaryMenu(s32 state)
DecompressAndCopyTileDataToVram(1, sRibbonIconsSmall_Gfx, 0, 1, 0);
SetBgTilemapBuffer(1, menu->tilemapBuffers[1]);
FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 32, 20);
- CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, BG_PLTT_ID(2), 5 * PLTT_SIZE_4BPP);
+ CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, BG_PLTT_ID(2), PLTT_SIZE_4BPP);
+ CopyPaletteIntoBufferUnfaded(sRibbonIcons2_Pal, BG_PLTT_ID(3), PLTT_SIZE_4BPP);
+ CopyPaletteIntoBufferUnfaded(sRibbonIcons3_Pal, BG_PLTT_ID(4), PLTT_SIZE_4BPP);
+ CopyPaletteIntoBufferUnfaded(sRibbonIcons4_Pal, BG_PLTT_ID(5), PLTT_SIZE_4BPP);
+ CopyPaletteIntoBufferUnfaded(sRibbonIcons5_Pal, BG_PLTT_ID(6), PLTT_SIZE_4BPP);
CopyPaletteIntoBufferUnfaded(sMonInfo_Pal, BG_PLTT_ID(10), sizeof(sMonInfo_Pal));
CopyBgTilemapBufferToVram(1);
return LT_INC_AND_PAUSE;
diff --git a/src/rayquaza_scene.c b/src/rayquaza_scene.c
index 5440ad802d..62060128fa 100644
--- a/src/rayquaza_scene.c
+++ b/src/rayquaza_scene.c
@@ -5,7 +5,6 @@
#include "task.h"
#include "graphics.h"
#include "bg.h"
-#include "main.h"
#include "malloc.h"
#include "palette.h"
#include "scanline_effect.h"
@@ -1287,7 +1286,7 @@ static const struct BgTemplate sBgTemplates_ChasesAway[] =
}
};
-void DoRayquazaScene(u8 animId, bool8 endEarly, void (*exitCallback)(void))
+void DoRayquazaScene(u8 animId, bool8 endEarly, MainCallback exitCallback)
{
sRayScene = AllocZeroed(sizeof(*sRayScene));
sRayScene->animId = animId;
diff --git a/src/region_map.c b/src/region_map.c
index 501623d3b3..889f38533a 100644
--- a/src/region_map.c
+++ b/src/region_map.c
@@ -63,7 +63,7 @@ enum {
struct MultiNameFlyDest
{
const u8 *const *name;
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
u16 flag;
};
@@ -72,7 +72,7 @@ static EWRAM_DATA struct RegionMap *sRegionMap = NULL;
static EWRAM_DATA struct {
void (*callback)(void);
u16 state;
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
struct RegionMap regionMap;
u8 tileBuffer[0x1c0];
u8 nameBuffer[0x26]; // never read
@@ -86,15 +86,15 @@ static u8 MoveRegionMapCursor_Full(void);
static u8 ProcessRegionMapInput_Zoomed(void);
static u8 MoveRegionMapCursor_Zoomed(void);
static void CalcZoomScrollParams(s16 scrollX, s16 scrollY, s16 c, s16 d, u16 e, u16 f, u8 rotation);
-static u16 GetMapSecIdAt(u16 x, u16 y);
+static mapsec_u16_t GetMapSecIdAt(u16 x, u16 y);
static void RegionMap_SetBG2XAndBG2Y(s16 x, s16 y);
static void InitMapBasedOnPlayerLocation(void);
static void RegionMap_InitializeStateBasedOnSSTidalLocation(void);
-static u8 GetMapsecType(u16 mapSecId);
-static u16 CorrectSpecialMapSecId_Internal(u16 mapSecId);
-static u16 GetTerraOrMarineCaveMapSecId(void);
+static u8 GetMapsecType(mapsec_u16_t mapSecId);
+static mapsec_u16_t CorrectSpecialMapSecId_Internal(mapsec_u16_t mapSecId);
+static mapsec_u16_t GetTerraOrMarineCaveMapSecId(void);
static void GetMarineCaveCoords(u16 *x, u16 *y);
-static bool32 IsPlayerInAquaHideout(u8 mapSecId);
+static bool32 IsPlayerInAquaHideout(mapsec_u8_t mapSecId);
static void GetPositionOfCursorWithinMapSec(void);
static bool8 RegionMap_IsMapSecIdInNextRow(u16 y);
static void SpriteCB_CursorMapFull(struct Sprite *sprite);
@@ -130,7 +130,7 @@ static const u8 sRegionMapPlayerIcon_MayGfx[] = INCBIN_U8("graphics/pokenav/regi
#include "data/region_map/region_map_layout.h"
#include "data/region_map/region_map_entries.h"
-static const u16 sRegionMap_SpecialPlaceLocations[][2] =
+static const mapsec_u16_t sRegionMap_SpecialPlaceLocations[][2] =
{
{MAPSEC_UNDERWATER_105, MAPSEC_ROUTE_105},
{MAPSEC_UNDERWATER_124, MAPSEC_ROUTE_124},
@@ -162,14 +162,14 @@ static const u16 sRegionMap_SpecialPlaceLocations[][2] =
{MAPSEC_NONE, MAPSEC_NONE}
};
-static const u16 sMarineCaveMapSecIds[] =
+static const mapsec_u16_t sMarineCaveMapSecIds[] =
{
MAPSEC_MARINE_CAVE,
MAPSEC_UNDERWATER_MARINE_CAVE,
MAPSEC_UNDERWATER_MARINE_CAVE
};
-static const u16 sTerraOrMarineCaveMapSecIds[ABNORMAL_WEATHER_LOCATIONS] =
+static const mapsec_u16_t sTerraOrMarineCaveMapSecIds[ABNORMAL_WEATHER_LOCATIONS] =
{
[ABNORMAL_WEATHER_ROUTE_114_NORTH - 1] = MAPSEC_ROUTE_114,
[ABNORMAL_WEATHER_ROUTE_114_SOUTH - 1] = MAPSEC_ROUTE_114,
@@ -203,7 +203,7 @@ static const struct UCoords16 sMarineCaveLocationCoords[MARINE_CAVE_LOCATIONS] =
[MARINE_CAVE_COORD(ROUTE_129_EAST)] = {24, 10}
};
-static const u8 sMapSecAquaHideoutOld[] =
+static const mapsec_u8_t sMapSecAquaHideoutOld[] =
{
MAPSEC_AQUA_HIDEOUT_OLD
};
@@ -273,7 +273,7 @@ static const union AnimCmd *const sRegionMapPlayerIconAnimTable[] =
};
// Event islands that don't appear on map. (Southern Island does)
-static const u8 sMapSecIdsOffMap[] =
+static const mapsec_u8_t sMapSecIdsOffMap[] =
{
MAPSEC_BIRTH_ISLAND,
MAPSEC_FARAWAY_ISLAND,
@@ -421,7 +421,7 @@ static const struct SpritePalette sFlyTargetIconsSpritePalette =
.tag = TAG_FLY_ICON
};
-static const u16 sRedOutlineFlyDestinations[][2] =
+static const mapsec_u16_t sRedOutlineFlyDestinations[][2] =
{
{
FLAG_LANDMARK_BATTLE_FRONTIER,
@@ -694,7 +694,7 @@ static u8 ProcessRegionMapInput_Full(void)
static u8 MoveRegionMapCursor_Full(void)
{
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
if (sRegionMap->cursorMovementFrameCounter != 0)
return MAP_INPUT_MOVE_CONT;
@@ -779,7 +779,7 @@ static u8 MoveRegionMapCursor_Zoomed(void)
{
u16 x;
u16 y;
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
sRegionMap->scrollY += sRegionMap->zoomedCursorDeltaY;
sRegionMap->scrollX += sRegionMap->zoomedCursorDeltaX;
@@ -962,7 +962,7 @@ void PokedexAreaScreen_UpdateRegionMapVariablesAndVideoRegs(s16 x, s16 y)
}
}
-static u16 GetMapSecIdAt(u16 x, u16 y)
+static mapsec_u16_t GetMapSecIdAt(u16 x, u16 y)
{
if (y < MAPCURSOR_Y_MIN || y > MAPCURSOR_Y_MAX || x < MAPCURSOR_X_MIN || x > MAPCURSOR_X_MAX)
{
@@ -1180,7 +1180,7 @@ static void RegionMap_InitializeStateBasedOnSSTidalLocation(void)
sRegionMap->cursorPosY = gRegionMapEntries[sRegionMap->mapSecId].y + y + MAPCURSOR_Y_MIN;
}
-static u8 GetMapsecType(u16 mapSecId)
+static u8 GetMapsecType(mapsec_u16_t mapSecId)
{
switch (mapSecId)
{
@@ -1227,12 +1227,12 @@ static u8 GetMapsecType(u16 mapSecId)
}
}
-u16 GetRegionMapSecIdAt(u16 x, u16 y)
+mapsec_u16_t GetRegionMapSecIdAt(u16 x, u16 y)
{
return GetMapSecIdAt(x, y);
}
-static u16 CorrectSpecialMapSecId_Internal(u16 mapSecId)
+static mapsec_u16_t CorrectSpecialMapSecId_Internal(mapsec_u16_t mapSecId)
{
u32 i;
@@ -1253,7 +1253,7 @@ static u16 CorrectSpecialMapSecId_Internal(u16 mapSecId)
return mapSecId;
}
-static u16 GetTerraOrMarineCaveMapSecId(void)
+static mapsec_u16_t GetTerraOrMarineCaveMapSecId(void)
{
s16 idx;
@@ -1282,7 +1282,7 @@ static void GetMarineCaveCoords(u16 *x, u16 *y)
// Probably meant to be an "IsPlayerInIndoorDungeon" function, but in practice it only has the one mapsec
// Additionally, because the mapsec doesnt exist in Emerald, this function always returns FALSE
-static bool32 IsPlayerInAquaHideout(u8 mapSecId)
+static bool32 IsPlayerInAquaHideout(mapsec_u8_t mapSecId)
{
u32 i;
@@ -1294,7 +1294,7 @@ static bool32 IsPlayerInAquaHideout(u8 mapSecId)
return FALSE;
}
-u16 CorrectSpecialMapSecId(u16 mapSecId)
+mapsec_u16_t CorrectSpecialMapSecId(mapsec_u16_t mapSecId)
{
return CorrectSpecialMapSecId_Internal(mapSecId);
}
@@ -1573,7 +1573,7 @@ void TrySetPlayerIconBlink(void)
#undef sVisible
#undef sTimer
-u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength)
+u8 *GetMapName(u8 *dest, mapsec_u16_t regionMapId, u16 padLength)
{
u8 *str;
u16 i;
@@ -1606,7 +1606,7 @@ u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength)
}
// TODO: probably needs a better name
-u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId)
+u8 *GetMapNameGeneric(u8 *dest, mapsec_u16_t mapSecId)
{
switch (mapSecId)
{
@@ -1619,7 +1619,7 @@ u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId)
}
}
-u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId)
+u8 *GetMapNameHandleAquaHideout(u8 *dest, mapsec_u16_t mapSecId)
{
if (mapSecId == MAPSEC_AQUA_HIDEOUT_OLD)
return StringCopy(dest, gText_Hideout);
@@ -1627,7 +1627,7 @@ u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId)
return GetMapNameGeneric(dest, mapSecId);
}
-static void GetMapSecDimensions(u16 mapSecId, u16 *x, u16 *y, u16 *width, u16 *height)
+static void GetMapSecDimensions(mapsec_u16_t mapSecId, u16 *x, u16 *y, u16 *width, u16 *height)
{
*x = gRegionMapEntries[mapSecId].x;
*y = gRegionMapEntries[mapSecId].y;
@@ -1640,7 +1640,7 @@ bool8 IsRegionMapZoomed(void)
return sRegionMap->zoomed;
}
-bool32 IsEventIslandMapSecId(u8 mapSecId)
+bool32 IsEventIslandMapSecId(mapsec_u8_t mapSecId)
{
u32 i;
@@ -1847,7 +1847,7 @@ static void LoadFlyDestIcons(void)
static void CreateFlyDestIcons(void)
{
u16 canFlyFlag;
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
u16 x;
u16 y;
u16 width;
@@ -1895,7 +1895,7 @@ static void TryCreateRedOutlineFlyDestIcons(void)
u16 y;
u16 width;
u16 height;
- u16 mapSecId;
+ mapsec_u16_t mapSecId;
u8 spriteId;
for (i = 0; sRedOutlineFlyDestinations[i][1] != MAPSEC_NONE; i++)
diff --git a/src/rtc.c b/src/rtc.c
index 34f84183cf..fb2518f210 100644
--- a/src/rtc.c
+++ b/src/rtc.c
@@ -348,6 +348,7 @@ void RtcCalcLocalTimeOffset(s32 days, s32 hours, s32 minutes, s32 seconds)
gLocalTime.hours = hours;
gLocalTime.minutes = minutes;
gLocalTime.seconds = seconds;
+ FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds);
RtcGetInfo(&sRtc);
RtcCalcTimeDifference(&sRtc, &gSaveBlock2Ptr->localTimeOffset, &gLocalTime);
}
diff --git a/src/save.c b/src/save.c
index 4e6c41b6f9..5d17cea210 100644
--- a/src/save.c
+++ b/src/save.c
@@ -9,7 +9,6 @@
#include "overworld.h"
#include "hall_of_fame.h"
#include "pokemon_storage_system.h"
-#include "main.h"
#include "trainer_hill.h"
#include "link.h"
#include "constants/game_stat.h"
@@ -92,7 +91,7 @@ COMMON_DATA struct SaveSector *gReadWriteSector = NULL; // Pointer to a buffer f
COMMON_DATA u16 gIncrementalSectorId = 0;
COMMON_DATA u16 gSaveUnusedVar = 0;
COMMON_DATA u16 gSaveFileStatus = 0;
-COMMON_DATA void (*gGameContinueCallback)(void) = NULL;
+COMMON_DATA MainCallback gGameContinueCallback = NULL;
COMMON_DATA struct SaveSectorLocation gRamSaveSectorLocations[NUM_SECTORS_PER_SLOT] = {0};
COMMON_DATA u16 gSaveUnusedVar2 = 0;
COMMON_DATA u16 gSaveAttemptStatus = 0;
@@ -897,7 +896,7 @@ u8 LoadGameSave(u8 saveType)
status = TryLoadSaveSlot(FULL_SAVE_SLOT, gRamSaveSectorLocations);
CopyPartyAndObjectsFromSave();
gSaveFileStatus = status;
- gGameContinueCallback = 0;
+ gGameContinueCallback = NULL;
break;
case SAVE_HALL_OF_FAME:
if (gHoFSaveBuffer != NULL)
diff --git a/src/save_failed_screen.c b/src/save_failed_screen.c
index 6315ef6f70..64848239b4 100644
--- a/src/save_failed_screen.c
+++ b/src/save_failed_screen.c
@@ -336,7 +336,7 @@ static void CB2_ReturnToTitleScreen(void)
}
else
{
- SetMainCallback2((MainCallback)gGameContinueCallback);
+ SetMainCallback2(gGameContinueCallback);
gGameContinueCallback = NULL;
}
}
diff --git a/src/scrcmd.c b/src/scrcmd.c
index 767f41c6d5..54edd76c61 100644
--- a/src/scrcmd.c
+++ b/src/scrcmd.c
@@ -3154,11 +3154,13 @@ bool8 Scrcmd_getobjectfacingdirection(struct ScriptContext *ctx)
return FALSE;
}
-bool8 ScrFunc_hidefollower(struct ScriptContext *ctx)
+bool8 ScrCmd_hidefollower(struct ScriptContext *ctx)
{
bool16 wait = VarGet(ScriptReadHalfword(ctx));
struct ObjectEvent *obj;
+ Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE);
+
if ((obj = ScriptHideFollower()) != NULL && wait)
{
sMovingNpcId = obj->localId;
diff --git a/src/shop.c b/src/shop.c
index 6eaa0ba01a..e983213963 100644
--- a/src/shop.c
+++ b/src/shop.c
@@ -458,7 +458,7 @@ static void Task_GoToBuyOrSellMenu(u8 taskId)
if (!gPaletteFade.active)
{
DestroyTask(taskId);
- SetMainCallback2((void *)((u16)tCallbackHi << 16 | (u16)tCallbackLo));
+ SetMainCallback2((MainCallback)((u16)tCallbackHi << 16 | (u16)tCallbackLo));
}
}
diff --git a/src/slot_machine.c b/src/slot_machine.c
index 5e6406f0a8..e35087e513 100644
--- a/src/slot_machine.c
+++ b/src/slot_machine.c
@@ -314,6 +314,11 @@ enum {
DIG_DISPLAY_BONUS_BIG
};
+// IDs for the text windows
+enum {
+ WIN_MSG,
+ WIN_INFO,
+};
// How ReelTime works
// ==================
@@ -1249,7 +1254,7 @@ static void SlotMachineSetup_LoadGfxAndTilemaps(void)
LoadSlotMachineGfx();
LoadMessageBoxGfx(0, 0x200, BG_PLTT_ID(15));
LoadUserWindowBorderGfx(0, 0x214, BG_PLTT_ID(14));
- PutWindowTilemap(0);
+ PutWindowTilemap(WIN_MSG);
}
static void CreateSlotMachineSprites(void)
@@ -1390,9 +1395,9 @@ static bool8 SlotTask_HandleBetInput(struct Task *task)
// SLOTTASK_MSG_NEED_3_COINS
static bool8 SlotTask_PrintMsg_Need3Coins(struct Task *task)
{
- DrawDialogueFrame(0, FALSE);
- AddTextPrinterParameterized(0, FONT_NORMAL, gText_YouDontHaveThreeCoins, 0, 1, 0, 0);
- CopyWindowToVram(0, COPYWIN_FULL);
+ DrawDialogueFrame(WIN_MSG, FALSE);
+ AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_YouDontHaveThreeCoins, 0, 1, 0, 0);
+ CopyWindowToVram(WIN_MSG, COPYWIN_FULL);
sSlotMachine->state = SLOTTASK_WAIT_MSG_NEED_3_COINS;
return FALSE;
}
@@ -1402,7 +1407,7 @@ static bool8 SlotTask_WaitMsg_Need3Coins(struct Task *task)
{
if (JOY_NEW(A_BUTTON | B_BUTTON))
{
- ClearDialogWindowAndFrame(0, TRUE);
+ ClearDialogWindowAndFrame(WIN_MSG, TRUE);
sSlotMachine->state = SLOTTASK_BET_INPUT;
}
return FALSE;
@@ -1655,9 +1660,9 @@ static bool8 SlotTask_NoMatches(struct Task *task)
// SLOTTASK_ASK_QUIT
static bool8 SlotTask_AskQuit(struct Task *task)
{
- DrawDialogueFrame(0, FALSE);
- AddTextPrinterParameterized(0, FONT_NORMAL, gText_QuitTheGame, 0, 1, 0, 0);
- CopyWindowToVram(0, COPYWIN_FULL);
+ DrawDialogueFrame(WIN_MSG, FALSE);
+ AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_QuitTheGame, 0, 1, 0, 0);
+ CopyWindowToVram(WIN_MSG, COPYWIN_FULL);
CreateYesNoMenuParameterized(0x15, 7, 0x214, 0x180, 0xE, 0xF);
sSlotMachine->state = SLOTTASK_HANDLE_QUIT_INPUT;
return FALSE;
@@ -1669,16 +1674,16 @@ static bool8 SlotTask_HandleQuitInput(struct Task *task)
s8 input = Menu_ProcessInputNoWrapClearOnChoose();
if (input == 0) // Chose to quit
{
- ClearDialogWindowAndFrame(0, TRUE);
+ ClearDialogWindowAndFrame(WIN_MSG, TRUE);
DarkenBetTiles(0);
DarkenBetTiles(1);
DarkenBetTiles(2);
sSlotMachine->coins += sSlotMachine->bet;
sSlotMachine->state = SLOTTASK_END;
}
- else if (input == 1 || input == -1) // Chose not to quit
+ else if (input == 1 || input == MENU_B_PRESSED) // Chose not to quit
{
- ClearDialogWindowAndFrame(0, TRUE);
+ ClearDialogWindowAndFrame(WIN_MSG, TRUE);
sSlotMachine->state = SLOTTASK_BET_INPUT;
}
return FALSE;
@@ -1687,9 +1692,9 @@ static bool8 SlotTask_HandleQuitInput(struct Task *task)
// SLOTTASK_MSG_MAX_COINS
static bool8 SlotTask_PrintMsg_MaxCoins(struct Task *task)
{
- DrawDialogueFrame(0, FALSE);
- AddTextPrinterParameterized(0, FONT_NORMAL, gText_YouveGot9999Coins, 0, 1, 0, 0);
- CopyWindowToVram(0, COPYWIN_FULL);
+ DrawDialogueFrame(WIN_MSG, FALSE);
+ AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_YouveGot9999Coins, 0, 1, 0, 0);
+ CopyWindowToVram(WIN_MSG, COPYWIN_FULL);
sSlotMachine->state = SLOTTASK_WAIT_MSG_MAX_COINS;
return FALSE;
}
@@ -1699,7 +1704,7 @@ static bool8 SlotTask_WaitMsg_MaxCoins(struct Task *task)
{
if (JOY_NEW(A_BUTTON | B_BUTTON))
{
- ClearDialogWindowAndFrame(0, TRUE);
+ ClearDialogWindowAndFrame(WIN_MSG, TRUE);
sSlotMachine->state = SLOTTASK_BET_INPUT;
}
return FALSE;
@@ -1708,9 +1713,9 @@ static bool8 SlotTask_WaitMsg_MaxCoins(struct Task *task)
// SLOTTASK_MSG_NO_MORE_COINS
static bool8 SlotTask_PrintMsg_NoMoreCoins(struct Task *task)
{
- DrawDialogueFrame(0, FALSE);
- AddTextPrinterParameterized(0, FONT_NORMAL, gText_YouveRunOutOfCoins, 0, 1, 0, 0);
- CopyWindowToVram(0, COPYWIN_FULL);
+ DrawDialogueFrame(WIN_MSG, FALSE);
+ AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_YouveRunOutOfCoins, 0, 1, 0, 0);
+ CopyWindowToVram(WIN_MSG, COPYWIN_FULL);
sSlotMachine->state = SLOTTASK_WAIT_MSG_NO_MORE_COINS;
return FALSE;
}
@@ -1720,7 +1725,7 @@ static bool8 SlotTask_WaitMsg_NoMoreCoins(struct Task *task)
{
if (JOY_NEW(A_BUTTON | B_BUTTON))
{
- ClearDialogWindowAndFrame(0, TRUE);
+ ClearDialogWindowAndFrame(WIN_MSG, TRUE);
sSlotMachine->state = SLOTTASK_END;
}
return FALSE;
@@ -3922,15 +3927,15 @@ static void InfoBox_DrawWindow(struct Task *task)
DestroyDigitalDisplayScene();
LoadInfoBoxTilemap();
AddWindow(&sWindowTemplate_InfoBox);
- PutWindowTilemap(1);
- FillWindowPixelBuffer(1, PIXEL_FILL(0));
+ PutWindowTilemap(WIN_INFO);
+ FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0));
task->tState++;
}
static void InfoBox_AddText(struct Task *task)
{
- AddTextPrinterParameterized3(1, FONT_NORMAL, 2, 5, sColors_ReeltimeHelp, 0, gText_ReelTimeHelp);
- CopyWindowToVram(1, COPYWIN_FULL);
+ AddTextPrinterParameterized3(WIN_INFO, FONT_NORMAL, 2, 5, sColors_ReeltimeHelp, 0, gText_ReelTimeHelp);
+ CopyWindowToVram(WIN_INFO, COPYWIN_FULL);
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
task->tState++;
}
@@ -3939,10 +3944,10 @@ static void InfoBox_WaitInput(struct Task *task)
{
if (JOY_NEW(B_BUTTON | SELECT_BUTTON))
{
- FillWindowPixelBuffer(1, PIXEL_FILL(0));
- ClearWindowTilemap(1);
- CopyWindowToVram(1, COPYWIN_MAP);
- RemoveWindow(1);
+ FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0));
+ ClearWindowTilemap(WIN_INFO);
+ CopyWindowToVram(WIN_INFO, COPYWIN_MAP);
+ RemoveWindow(WIN_INFO);
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
task->tState++;
}
diff --git a/src/strings.c b/src/strings.c
index 102e5641ac..41b128d1da 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -1299,4 +1299,6 @@ const u8 gText_AM[] = _("AM");
const u8 gText_PM[] = _("PM");
const u8 gText_Relearn[] = _("{START_BUTTON} 回忆"); // future note: don't decap this, because it mimics the summary screen BG graphics which will not get decapped
const u8 gText_Rename[] = _("更改昵称");
-const u8 gText_CannotSendMonToBoxHM[] = _("不能把那只宝可梦送回电脑,\n因为它学会了秘传招式。{PAUSE_UNTIL_PRESS}");
+const u8 gText_CannotSendMonToBoxHM[] = _("无法将那只宝可梦送回盒子,\n因为它学会了秘传招式。{PAUSE_UNTIL_PRESS}");
+const u8 gText_CannotSendMonToBoxActive[] = _("无法将正在战斗的宝可梦\n送回盒子。{PAUSE_UNTIL_PRESS}");
+const u8 gText_CannotSendMonToBoxPartner[] = _("无法将其他人的宝可梦\n送回盒子。{PAUSE_UNTIL_PRESS}");
\ No newline at end of file
diff --git a/src/trade.c b/src/trade.c
index 5fd04e1f82..2a0d920a91 100644
--- a/src/trade.c
+++ b/src/trade.c
@@ -4535,7 +4535,7 @@ static void CreateInGameTradePokemonInternal(u8 whichPlayerMon, u8 whichInGameTr
u8 level = GetMonData(&gPlayerParty[whichPlayerMon], MON_DATA_LEVEL);
struct Mail mail;
- u8 metLocation = METLOC_IN_GAME_TRADE;
+ metloc_u8_t metLocation = METLOC_IN_GAME_TRADE;
u8 mailNum;
struct Pokemon *pokemon = &gEnemyParty[0];
diff --git a/src/trainer_card.c b/src/trainer_card.c
index b275bef8aa..3cdcdd3309 100755
--- a/src/trainer_card.c
+++ b/src/trainer_card.c
@@ -78,7 +78,7 @@ struct TrainerCardData
u8 cardType;
bool8 isHoenn;
u16 blendColor;
- void (*callback2)(void);
+ MainCallback callback2;
struct TrainerCard trainerCard;
u16 frontTilemap[600];
u16 backTilemap[600];
diff --git a/src/trainer_see.c b/src/trainer_see.c
index 71237b4e29..2ec23dfa4f 100644
--- a/src/trainer_see.c
+++ b/src/trainer_see.c
@@ -19,6 +19,7 @@
#include "constants/event_objects.h"
#include "constants/event_object_movement.h"
#include "constants/field_effects.h"
+#include "constants/script_commands.h"
#include "constants/trainer_types.h"
// this file's functions
@@ -380,6 +381,7 @@ bool8 CheckForTrainersWantingBattle(void)
if (numTrainers == 0xFF) // non-trainerbatle script
{
u32 objectEventId = gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId;
+ gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);
gSelectedObjectEvent = objectEventId;
gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId;
ScriptContext_SetupScript(EventScript_ObjectApproachPlayer);
@@ -429,7 +431,7 @@ bool8 CheckForTrainersWantingBattle(void)
static u8 CheckTrainer(u8 objectEventId)
{
- const u8 *scriptPtr, *trainerBattlePtr;
+ const u8 *trainerBattlePtr;
u8 numTrainers = 1;
u8 approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]);
@@ -438,15 +440,15 @@ static u8 CheckTrainer(u8 objectEventId)
if (InTrainerHill() == TRUE)
{
- trainerBattlePtr = scriptPtr = GetTrainerHillTrainerScript();
+ trainerBattlePtr = GetTrainerHillTrainerScript();
}
else
{
- trainerBattlePtr = scriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);
+ trainerBattlePtr = GetObjectEventScriptPointerByObjectEventId(objectEventId);
struct ScriptContext ctx;
- if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, scriptPtr, &ctx))
+ if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, trainerBattlePtr, &ctx))
{
- if (*ctx.scriptPtr == 0x5c) // trainerbattle
+ if (*ctx.scriptPtr == SCR_OP_TRAINERBATTLE)
trainerBattlePtr = ctx.scriptPtr;
else
trainerBattlePtr = NULL;
@@ -470,7 +472,18 @@ static u8 CheckTrainer(u8 objectEventId)
else if (trainerBattlePtr)
{
if (GetTrainerFlagFromScriptPointer(trainerBattlePtr))
- return 0;
+ {
+ //If there is a rematch, we want to trigger the approach sequence
+ if (GetRematchFromScriptPointer(trainerBattlePtr))
+ {
+ trainerBattlePtr = NULL;
+ numTrainers = 0xFF;
+ }
+ else
+ {
+ return 0;
+ }
+ }
}
else
{
@@ -492,7 +505,7 @@ static u8 CheckTrainer(u8 objectEventId)
}
gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = objectEventId;
- gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = scriptPtr;
+ gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = trainerBattlePtr;
gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance;
InitTrainerApproachTask(&gObjectEvents[objectEventId], approachDistance - 1);
gNoOfApproachingTrainers++;
@@ -951,13 +964,17 @@ u8 FldEff_HeartIcon(void)
return 0;
}
-
u8 FldEff_DoubleExclMarkIcon(void)
{
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53);
if (spriteId != MAX_SPRITES)
- SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 2);
+ {
+ struct Sprite *sprite = &gSprites[spriteId];
+
+ SetIconSpriteData(sprite, FLDEFF_DOUBLE_EXCL_MARK_ICON, 2);
+ UpdateSpritePaletteByTemplate(&sSpriteTemplate_ExclamationQuestionMark, sprite);
+ }
return 0;
}
@@ -967,7 +984,12 @@ u8 FldEff_XIcon(void)
u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53);
if (spriteId != MAX_SPRITES)
- SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 3);
+ {
+ struct Sprite *sprite = &gSprites[spriteId];
+
+ SetIconSpriteData(sprite, FLDEFF_X_ICON, 3);
+ UpdateSpritePaletteByTemplate(&sSpriteTemplate_ExclamationQuestionMark, sprite);
+ }
return 0;
}
diff --git a/src/use_pokeblock.c b/src/use_pokeblock.c
index f22068d2e8..953c6e964b 100644
--- a/src/use_pokeblock.c
+++ b/src/use_pokeblock.c
@@ -50,7 +50,7 @@ enum {
struct UsePokeblockSession
{
void (*callback)(void);
- void (*exitCallback)(void);
+ MainCallback exitCallback;
struct Pokeblock *pokeblock;
struct Pokemon *mon;
u8 stringBuffer[64];
@@ -162,7 +162,7 @@ extern const u16 gConditionText_Pal[];
// The below 3 are saved for returning to the screen after feeding a pokeblock to a mon
// so that the rest of the data can be freed
static EWRAM_DATA struct UsePokeblockSession *sInfo = NULL;
-static EWRAM_DATA void (*sExitCallback)(void) = NULL;
+static EWRAM_DATA MainCallback sExitCallback = NULL;
static EWRAM_DATA struct Pokeblock *sPokeblock = NULL;
EWRAM_DATA u8 gPokeblockMonId = 0;
EWRAM_DATA s16 gPokeblockGain = 0;
diff --git a/src/vs_seeker.c b/src/vs_seeker.c
index 1a7dc68c72..1fcf5fdfcc 100644
--- a/src/vs_seeker.c
+++ b/src/vs_seeker.c
@@ -28,9 +28,12 @@
#include "constants/items.h"
#include "constants/maps.h"
#include "constants/songs.h"
+#include "constants/script_commands.h"
#include "constants/trainer_types.h"
#include "constants/field_effects.h"
+// Documentation for the Vs. Seeker can be found in docs/tutorials/vs_seeker.md.
+
enum
{
VSSEEKER_NOT_CHARGED,
@@ -230,6 +233,9 @@ bool8 UpdateVsSeekerStepCounter(void)
if (!I_VS_SEEKER_CHARGING) return FALSE;
+ // This condition helps in case your save file is switching between vs seeker and matchcall
+ if (gSaveBlock1Ptr->trainerRematchStepCounter > VSSEEKER_RECHARGE_STEPS && gSaveBlock1Ptr->trainerRematchStepCounter <= 0xFF)
+ gSaveBlock1Ptr->trainerRematchStepCounter = 0;
if (CheckBagHasItem(ITEM_VS_SEEKER, 1))
{
if ((gSaveBlock1Ptr->trainerRematchStepCounter & 0xFF) < VSSEEKER_RECHARGE_STEPS)
@@ -376,8 +382,11 @@ static void GatherNearbyTrainerInfo(void)
if (templates[objectEventIdx].trainerType != TRAINER_TYPE_NORMAL && templates[objectEventIdx].trainerType != TRAINER_TYPE_BURIED)
continue;
+ u16 trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script);
+ if (trainerIdx == TRAINER_NONE)
+ continue;
sVsSeeker->trainerInfo[vsSeekerObjectIdx].script = templates[objectEventIdx].script;
- sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script);
+ sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = trainerIdx;
sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = templates[objectEventIdx].localId;
TryGetObjectEventIdByLocalIdAndMap(templates[objectEventIdx].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectEventId);
sVsSeeker->trainerInfo[vsSeekerObjectIdx].objectEventId = objectEventId;
@@ -707,9 +716,31 @@ static u16 GetTrainerFlagFromScript(const u8 *script)
{
// The trainer flag is located 3 bytes (command + flags + localIdA) from the script pointer, assuming the trainerbattle command is first in the script.
// Because scripts are unaligned, and because the ARM processor requires shorts to be 16-bit aligned, this function needs to perform explicit bitwise operations to get the correct flag.
- script += 3;
- u16 trainerFlag = script[0];
- trainerFlag |= script[1] << 8;
+ u16 trainerFlag;
+ switch (script[0])
+ {
+ case SCR_OP_TRAINERBATTLE:
+ script += 3;
+ trainerFlag = script[0];
+ trainerFlag |= script[1] << 8;
+ break;
+ case SCR_OP_CALLNATIVE:
+ u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1];
+ if (callnativeFunc == ((u32)NativeVsSeekerRematchId | 0xA000000)) // | 0xA000000 corresponds to the request_effects=1 version of the function
+ {
+ script += 5;
+ trainerFlag = script[0];
+ trainerFlag |= script[1] << 8;
+ }
+ else
+ {
+ trainerFlag = TRAINER_NONE;
+ }
+ break;
+ default:
+ trainerFlag = TRAINER_NONE;
+ break;
+ }
return trainerFlag;
}
@@ -788,6 +819,14 @@ static u8 GetCurVsSeekerResponse(s32 vsSeekerIdx, u16 trainerIdx)
}
#endif //FREE_MATCH_CALL
+void NativeVsSeekerRematchId(struct ScriptContext *ctx)
+{
+ u16 trainerId = ScriptReadHalfword(ctx);
+ if (ctx->breakOnTrainerBattle && HasTrainerBeenFought(trainerId) && !ShouldTryRematchBattleForTrainerId(trainerId))
+ StopScript(ctx);
+}
+
+
static void StartAllRespondantIdleMovements(void)
{
#if FREE_MATCH_CALL == FALSE
diff --git a/src/wallclock.c b/src/wallclock.c
index 28c96fc972..bca008ac24 100644
--- a/src/wallclock.c
+++ b/src/wallclock.c
@@ -692,13 +692,13 @@ void CB2_StartWallClock(void)
DecompressDataWithHeaderVram(gWallClockStart_Tilemap, (u16 *)BG_SCREEN_ADDR(7));
taskId = CreateTask(Task_SetClock_WaitFadeIn, 0);
- gTasks[taskId].tHours = 10;
- gTasks[taskId].tMinutes = 0;
+ gTasks[taskId].tHours = gLocalTime.hours;
+ gTasks[taskId].tMinutes = gLocalTime.minutes;
gTasks[taskId].tMoveDir = 0;
- gTasks[taskId].tPeriod = 0;
+ gTasks[taskId].tPeriod = gTasks[taskId].tHours / 12;
gTasks[taskId].tMoveSpeed = 0;
- gTasks[taskId].tMinuteHandAngle = 0;
- gTasks[taskId].tHourHandAngle = 300;
+ gTasks[taskId].tMinuteHandAngle = gTasks[taskId].tMinutes * 6;
+ gTasks[taskId].tHourHandAngle = (gTasks[taskId].tHours % 12) * 30 + (gTasks[taskId].tMinutes / 10) * 5;
spriteId = CreateSprite(&sSpriteTemplate_MinuteHand, 120, 80, 1);
gSprites[spriteId].sTaskId = taskId;
diff --git a/src/wild_encounter.c b/src/wild_encounter.c
index 0ace7499a4..5d8e22c11b 100644
--- a/src/wild_encounter.c
+++ b/src/wild_encounter.c
@@ -1,20 +1,23 @@
#include "global.h"
-#include "wild_encounter.h"
-#include "pokemon.h"
-#include "metatile_behavior.h"
+#include "battle_setup.h"
+#include "battle_pike.h"
+#include "battle_pyramid.h"
+#include "event_data.h"
#include "fieldmap.h"
#include "follower_npc.h"
#include "random.h"
#include "field_player_avatar.h"
-#include "event_data.h"
-#include "safari_zone.h"
+#include "link.h"
+#include "metatile_behavior.h"
#include "overworld.h"
#include "pokeblock.h"
-#include "battle_setup.h"
+#include "pokemon.h"
+#include "random.h"
#include "roamer.h"
-#include "tv.h"
-#include "link.h"
+#include "safari_zone.h"
#include "script.h"
+#include "tv.h"
+#include "wild_encounter.h"
#include "battle_debug.h"
#include "battle_pike.h"
#include "battle_pyramid.h"
diff --git a/test/battle/ability/anticipation.c b/test/battle/ability/anticipation.c
index 223512710f..a4a3bef77f 100644
--- a/test/battle/ability/anticipation.c
+++ b/test/battle/ability/anticipation.c
@@ -16,6 +16,18 @@ SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a super-effe
}
}
+SINGLE_BATTLE_TEST("Anticipation does not trigger even when a move is super effective on only 1 type")
+{
+ GIVEN {
+ PLAYER(SPECIES_WHISCASH) { Ability(ABILITY_ANTICIPATION); }
+ OPPONENT(SPECIES_PIKACHU) { Moves(MOVE_CELEBRATE, MOVE_THUNDERBOLT); }
+ } WHEN {
+ TURN { }
+ } SCENE {
+ NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION);
+ }
+}
+
SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a One-hit KO move")
{
GIVEN {
@@ -59,28 +71,21 @@ SINGLE_BATTLE_TEST("Anticipation doesn't consider Normalize into their effective
SINGLE_BATTLE_TEST("Anticipation doesn't consider Scrappy into their effectiveness (Gen5+)")
{
- KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveType(MOVE_CLOSE_COMBAT) == TYPE_FIGHTING);
- ASSUME(GetSpeciesType(SPECIES_EEVEE, 0) == TYPE_NORMAL);
- ASSUME(GetSpeciesType(SPECIES_EEVEE, 1) == TYPE_NORMAL);
- PLAYER(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); }
- OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_TRICK_OR_TREAT, MOVE_SKILL_SWAP, MOVE_CELEBRATE); }
+ ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 0) == TYPE_STEEL);
+ ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 1) == TYPE_GHOST);
+ PLAYER(SPECIES_DOUBLADE) { Ability(ABILITY_ANTICIPATION); }
+ OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_CELEBRATE); }
} WHEN {
- TURN { MOVE(opponent, MOVE_TRICK_OR_TREAT); MOVE(player, MOVE_SKILL_SWAP); }
- TURN { MOVE(opponent, MOVE_SKILL_SWAP); }
+ TURN { }
} SCENE {
- ABILITY_POPUP(player, ABILITY_ANTICIPATION);
- ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK_OR_TREAT, opponent);
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player);
- ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent);
NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION);
}
}
SINGLE_BATTLE_TEST("Anticipation doesn't consider Gravity into their effectiveness (Gen5+)")
{
- KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_SKARMORY);
OPPONENT(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); Moves(MOVE_EARTHQUAKE, MOVE_GRAVITY, MOVE_SCRATCH, MOVE_POUND); }
diff --git a/test/battle/ability/aura_break.c b/test/battle/ability/aura_break.c
index 93b21421e4..389ab327b1 100644
--- a/test/battle/ability/aura_break.c
+++ b/test/battle/ability/aura_break.c
@@ -1,6 +1,115 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("Aura Break inverts Fairy Aura's effect");
-TO_DO_BATTLE_TEST("Aura Break inverts Dark Aura's effect");
-TO_DO_BATTLE_TEST("Aura Break ignores Mold Breaker abilities");
+DOUBLE_BATTLE_TEST("Aura Break inverts Fairy Aura's effect")
+{
+ s16 damage[3];
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); }
+ TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(playerRight, 2); }
+ TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(opponentRight, 2); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[0]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[1]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[2]);
+
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Aura Break inverts Dark Aura's effect")
+{
+ s16 damage[3];
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); }
+ TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(playerRight, 2); }
+ TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(opponentRight, 2); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[0]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[1]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[2]);
+
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Aura Break ignores Mold Breaker abilities")
+{
+ s16 damage[6];
+ u32 species = SPECIES_WOBBUFFET, ability = ABILITY_NONE;
+
+ PARAMETRIZE { species = SPECIES_WOBBUFFET, ability = ABILITY_SHADOW_TAG; }
+ PARAMETRIZE { species = SPECIES_CRANIDOS, ability = ABILITY_MOLD_BREAKER; }
+ PARAMETRIZE { species = SPECIES_ZEKROM, ability = ABILITY_TERAVOLT; }
+ PARAMETRIZE { species = SPECIES_RESHIRAM, ability = ABILITY_TURBOBLAZE; }
+
+ GIVEN {
+ PLAYER(species) { Ability(ability); Level(50); }
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
+ PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); }
+ TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); }
+ TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); }
+ TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); }
+ TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); SWITCH(opponentRight, 2); }
+ TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[0]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[1]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[2]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[3]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[4]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[5]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[2]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[4]);
+ EXPECT_MUL_EQ(damage[1], UQ_4_12(1.33), damage[3]);
+ EXPECT_MUL_EQ(damage[1], UQ_4_12(0.75), damage[5]);
+ }
+}
diff --git a/test/battle/ability/battery.c b/test/battle/ability/battery.c
index cecfa81d0c..7ddc0b837c 100644
--- a/test/battle/ability/battery.c
+++ b/test/battle/ability/battery.c
@@ -1,5 +1,51 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("Battery increases Sp. Attack damage of allies by ~30%"); // 5325/4096
-TO_DO_BATTLE_TEST("Battery does not increase its own Sp. Attack damage");
+DOUBLE_BATTLE_TEST("Battery increases Sp. Attack damage of allies by ~30%")
+{
+ s16 damage[2];
+
+ GIVEN {
+ PLAYER(SPECIES_CHARJABUG) { Speed(1); Ability(ABILITY_BATTERY); }
+ PLAYER(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_SHOCK_WAVE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
+ } WHEN {
+ TURN { MOVE(playerRight, MOVE_SHOCK_WAVE, target: opponentLeft); }
+ TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target: playerLeft); MOVE(playerRight, MOVE_SHOCK_WAVE, target: opponentLeft); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerRight);
+ HP_BAR(opponentLeft, captureDamage: &damage[0]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerRight);
+ HP_BAR(opponentLeft, captureDamage: &damage[1]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[1], UQ_4_12(1.3), damage[0]);
+ }
+
+}
+
+DOUBLE_BATTLE_TEST("Battery does not increase its own Sp. Attack damage")
+{
+ s16 damage[2];
+
+ GIVEN {
+ PLAYER(SPECIES_CHARJABUG) { Speed(1); Ability(ABILITY_BATTERY); }
+ PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SHOCK_WAVE, target: opponentLeft); }
+ TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target: playerLeft); MOVE(playerLeft, MOVE_SHOCK_WAVE, target: opponentLeft); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[0]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerLeft);
+ HP_BAR(opponentLeft, captureDamage: &damage[1]);
+ } THEN {
+ EXPECT_EQ(damage[0], damage[1]);
+ }
+}
diff --git a/test/battle/ability/contrary.c b/test/battle/ability/contrary.c
index c69360a1e1..850ca60734 100644
--- a/test/battle/ability/contrary.c
+++ b/test/battle/ability/contrary.c
@@ -242,4 +242,31 @@ SINGLE_BATTLE_TEST("Sticky Web raises Speed by 1 for Contrary mon on switch-in")
}
}
+AI_SINGLE_BATTLE_TEST("AI sees Contrary-effected moves correctly in MoveEffectInPlus instead of as a neutral effect")
+{
+ GIVEN{
+ AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
+ PLAYER(SPECIES_HERACROSS){
+ Level(44);
+ HP(1);
+ Speed(5);
+ Nature(NATURE_ADAMANT);
+ Item(ITEM_LOADED_DICE);
+ Moves(MOVE_PIN_MISSILE);
+ }
+ OPPONENT(SPECIES_SERPERIOR){
+ Level(44);
+ Speed(10);
+ Nature(NATURE_TIMID);
+ Ability(ABILITY_CONTRARY);
+ Moves(MOVE_DRAGON_PULSE, MOVE_SPIN_OUT, MOVE_HIDDEN_POWER, MOVE_GLARE);
+ }
+ } WHEN {
+ TURN{
+ MOVE(player, MOVE_PIN_MISSILE);
+ EXPECT_MOVE(opponent, MOVE_SPIN_OUT); // previously all 107, now sees speed can rise w/ Contrary
+ }
+ }
+}
+
TO_DO_BATTLE_TEST("Contrary does not invert stat changes that have been Baton-passed")
diff --git a/test/battle/ability/cursed_body.c b/test/battle/ability/cursed_body.c
index 25dbdc4b5b..b3bc886a3d 100644
--- a/test/battle/ability/cursed_body.c
+++ b/test/battle/ability/cursed_body.c
@@ -16,10 +16,77 @@ SINGLE_BATTLE_TEST("Cursed Body triggers 30% of the time")
}
}
-TO_DO_BATTLE_TEST("Cursed Body cannot disable Struggle")
-TO_DO_BATTLE_TEST("Cursed Body can trigger if the attacker is behind a Substitute")
-TO_DO_BATTLE_TEST("Cursed Body cannot trigger if the target is behind a Substitute")
-TO_DO_BATTLE_TEST("Cursed Body does not stop a multistrike move mid-execution")
+SINGLE_BATTLE_TEST("Cursed Body cannot disable Struggle")
+{
+ GIVEN {
+ ASSUME(GetItemHoldEffect(ITEM_CHOICE_SCARF) == HOLD_EFFECT_CHOICE_SCARF);
+ ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT);
+ ASSUME(GetMoveCategory(MOVE_CELEBRATE) == DAMAGE_CATEGORY_STATUS);
+ PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_CHOICE_SCARF); Moves(MOVE_CELEBRATE); }
+ OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TAUNT); }
+ TURN { FORCED_MOVE(player); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_CURSED_BODY);
+ MESSAGE("Wobbuffet's Struggle was disabled by the opposing Frillish's Cursed Body!");
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Cursed Body can trigger if the attacker is behind a Substitute")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SUBSTITUTE); }
+ TURN { MOVE(player, MOVE_AQUA_JET); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player);
+ ABILITY_POPUP(opponent, ABILITY_CURSED_BODY);
+ MESSAGE("Wobbuffet's Aqua Jet was disabled by the opposing Frillish's Cursed Body!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Cursed Body cannot trigger if the target is behind a Substitute")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SUBSTITUTE); }
+ TURN { MOVE(player, MOVE_AQUA_JET); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player);
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_CURSED_BODY);
+ MESSAGE("Wobbuffet's Aqua Jet was disabled by the opposing Frillish's Cursed Body!");
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Cursed Body does not stop a multistrike move mid-execution")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_ROCK_BLAST) == EFFECT_MULTI_HIT);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_ROCK_BLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player);
+ HP_BAR(opponent);
+ ABILITY_POPUP(opponent, ABILITY_CURSED_BODY);
+ MESSAGE("Wobbuffet's Rock Blast was disabled by the opposing Frillish's Cursed Body!");
+ HP_BAR(opponent);
+ }
+}
+
TO_DO_BATTLE_TEST("Cursed Body disables the move that called another move instead of the called move")
TO_DO_BATTLE_TEST("Cursed Body disables damaging Z-Moves, but not the base move") // Rotom Powers can restore Z-Moves
TO_DO_BATTLE_TEST("Cursed Body disables the base move of a status Z-Move")
diff --git a/test/battle/ability/emergency_exit.c b/test/battle/ability/emergency_exit.c
index 5027d929ec..1dcd0be21b 100644
--- a/test/battle/ability/emergency_exit.c
+++ b/test/battle/ability/emergency_exit.c
@@ -108,3 +108,69 @@ SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and fal
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
}
}
+
+WILD_BATTLE_TEST("Emergency Exit makes the pokemon flee during wild battle")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); };
+ } WHEN {
+ TURN { MOVE(player, MOVE_SUPER_FANG);}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, player);
+ HP_BAR(opponent);
+ ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
+ } THEN {
+ EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED);
+ }
+}
+
+WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle)")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); Status1(STATUS1_BURN); };
+ } WHEN {
+ TURN { }
+ } SCENE {
+ HP_BAR(opponent);
+ ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
+ } THEN {
+ EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED);
+ }
+}
+
+WILD_BATTLE_TEST("Emergency Exit makes the player ran during wild battle")
+{
+ GIVEN {
+ PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); };
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SUPER_FANG);}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, opponent);
+ HP_BAR(player);
+ ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT);
+ } THEN {
+ EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED);
+ }
+}
+
+WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle player side)")
+{
+ GIVEN {
+ PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); };
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SANDSTORM);}
+ } SCENE {
+ HP_BAR(player);
+ ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT);
+ } THEN {
+ EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED);
+ }
+}
diff --git a/test/battle/ability/flower_gift.c b/test/battle/ability/flower_gift.c
index 8f897e4668..29f5820191 100644
--- a/test/battle/ability/flower_gift.c
+++ b/test/battle/ability/flower_gift.c
@@ -199,4 +199,23 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back when it uses a move that
}
}
+DOUBLE_BATTLE_TEST("Flower Gift reverts Cherrim back after Teraform Zero clears weather")
+{
+ GIVEN {
+ PLAYER(SPECIES_TERAPAGOS_TERASTAL);
+ PLAYER(SPECIES_CHERRIM) { Ability(ABILITY_FLOWER_GIFT); }
+ OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); }
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
+ } SCENE {
+ ABILITY_POPUP(opponentLeft, ABILITY_DROUGHT);
+ ABILITY_POPUP(playerRight, ABILITY_FLOWER_GIFT);
+ ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO);
+ ABILITY_POPUP(playerRight, ABILITY_FLOWER_GIFT);
+ } THEN {
+ EXPECT_EQ(playerRight->species, SPECIES_CHERRIM);
+ }
+}
+
TO_DO_BATTLE_TEST("Flower Gift does not transform Cherrim back to normal when suppressed if Cherrim is Dynamaxed");
diff --git a/test/battle/ability/flower_veil.c b/test/battle/ability/flower_veil.c
index cb72cd33ae..78d15df1bd 100644
--- a/test/battle/ability/flower_veil.c
+++ b/test/battle/ability/flower_veil.c
@@ -15,7 +15,7 @@ ASSUMPTIONS
ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP);
}
-DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right target")
+DOUBLE_BATTLE_TEST("Flower Veil prevents status on allied Grass-types - right target")
{
u32 move;
@@ -39,7 +39,7 @@ DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right tar
}
}
-DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - left target")
+DOUBLE_BATTLE_TEST("Flower Veil prevents status on allied Grass-types - left target")
{
u32 move;
diff --git a/test/battle/ability/forecast.c b/test/battle/ability/forecast.c
index c462a3a634..418c8058d2 100644
--- a/test/battle/ability/forecast.c
+++ b/test/battle/ability/forecast.c
@@ -419,3 +419,22 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform when Cloud Nine ability user le
MESSAGE("Castform transformed!");
}
}
+
+DOUBLE_BATTLE_TEST("Forecast reverts Castform back after Teraform Zero clears weather")
+{
+ GIVEN {
+ PLAYER(SPECIES_TERAPAGOS_TERASTAL);
+ PLAYER(SPECIES_CASTFORM) { Ability(ABILITY_FORECAST); }
+ OPPONENT(SPECIES_KYOGRE) { Ability(ABILITY_DRIZZLE); }
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
+ } SCENE {
+ ABILITY_POPUP(opponentLeft, ABILITY_DRIZZLE);
+ ABILITY_POPUP(playerRight, ABILITY_FORECAST);
+ ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO);
+ ABILITY_POPUP(playerRight, ABILITY_FORECAST);
+ } THEN {
+ EXPECT_EQ(playerRight->species, SPECIES_CASTFORM_NORMAL);
+ }
+}
diff --git a/test/battle/ability/hunger_switch.c b/test/battle/ability/hunger_switch.c
index 1892de4424..469cf02d3c 100644
--- a/test/battle/ability/hunger_switch.c
+++ b/test/battle/ability/hunger_switch.c
@@ -53,7 +53,6 @@ SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastalli
SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form after switching out while Terastallized")
{
- KNOWN_FAILING; // #7062
GIVEN {
ASSUME(GetMoveEffect(MOVE_ROAR) == EFFECT_ROAR);
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); }
diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c
index c7308bfdaf..b0e4b19a19 100644
--- a/test/battle/ability/intimidate.c
+++ b/test/battle/ability/intimidate.c
@@ -240,6 +240,7 @@ DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out thr
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft);
HP_BAR(opponentLeft);
+ NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
MESSAGE("2 sent out Treecko!");
MESSAGE("2 sent out Torchic!");
NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
@@ -264,7 +265,7 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutral
}
}
-SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves")
+DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves")
{
u32 move;
PARAMETRIZE { move = MOVE_U_TURN; }
@@ -276,19 +277,24 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral
ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS);
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
+ PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
+ OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
- TURN { MOVE(player, move); SEND_OUT(player, 1); }
+ TURN {
+ if (move == MOVE_U_TURN)
+ MOVE(playerLeft, move, target: opponentLeft);
+ else
+ MOVE(playerLeft, move);
+ SEND_OUT(playerLeft, 2);
+ }
} SCENE {
- ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
+ ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing gas filled the area!");
- ANIMATION(ANIM_TYPE_MOVE, move, player);
+ ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
MESSAGE("The effects of the neutralizing gas wore off!");
- ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
+ ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
SEND_IN_MESSAGE("Wobbuffet");
- } THEN {
- if (move == MOVE_HEALING_WISH)
- EXPECT_EQ(player->hp, player->maxHP);
}
}
@@ -330,23 +336,25 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral
}
}
-SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted")
+DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_FELL_STINGER) == EFFECT_FELL_STINGER);
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); }
PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
+ OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
- TURN { MOVE(opponent, MOVE_FELL_STINGER); SEND_OUT(player, 1); }
+ TURN { MOVE(opponentLeft, MOVE_FELL_STINGER, target: playerLeft); SEND_OUT(playerLeft, 2); }
} SCENE {
- ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
+ ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing gas filled the area!");
- ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponentLeft);
MESSAGE("The effects of the neutralizing gas wore off!");
- ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
+ ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
MESSAGE("Weezing fainted!");
- ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
SEND_IN_MESSAGE("Wobbuffet");
}
}
diff --git a/test/battle/ability/liquid_ooze.c b/test/battle/ability/liquid_ooze.c
index 2c89702911..ca7c88ebdd 100644
--- a/test/battle/ability/liquid_ooze.c
+++ b/test/battle/ability/liquid_ooze.c
@@ -187,3 +187,34 @@ SINGLE_BATTLE_TEST("Liquid Ooze does not cause Dream Eater users to lose HP inst
EXPECT_LT(damage, 0); // Negative damage = Heal
}
}
+
+SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Absorb is blocked by Magic Guard")
+{
+ GIVEN {
+ PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); }
+ OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_ABSORB); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
+ HP_BAR(opponent);
+ NONE_OF {
+ HP_BAR(player);
+ MESSAGE("Wobbuffet sucked up the liquid ooze!");
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Leech Seed is blocked by Magic Guard")
+{
+ GIVEN {
+ PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); }
+ OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_LEECH_SEED); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, player);
+ HP_BAR(opponent);
+ NOT HP_BAR(player);
+ }
+}
diff --git a/test/battle/ability/magician.c b/test/battle/ability/magician.c
index 9e80120025..a54ba2df7d 100644
--- a/test/battle/ability/magician.c
+++ b/test/battle/ability/magician.c
@@ -25,3 +25,31 @@ SINGLE_BATTLE_TEST("Magician gets self-damage recoil after stealing Life Orb")
}
}
+DOUBLE_BATTLE_TEST("Magician steal the item from the fastest possible target")
+{
+ u32 playerRightSpeed = 0;
+ u32 opponentLeftSpeed = 0;
+ u32 opponentRightSpeed = 0;
+
+ PARAMETRIZE { playerRightSpeed = 4; opponentLeftSpeed = 2; opponentRightSpeed = 3; }
+ PARAMETRIZE { playerRightSpeed = 3; opponentLeftSpeed = 4; opponentRightSpeed = 2; }
+ PARAMETRIZE { playerRightSpeed = 2; opponentLeftSpeed = 3; opponentRightSpeed = 4; }
+
+ GIVEN {
+ PLAYER(SPECIES_DELPHOX) { Speed(1); Ability(ABILITY_MAGICIAN); Item(ITEM_NONE); }
+ PLAYER(SPECIES_WOBBUFFET) { Speed(playerRightSpeed); Item(ITEM_POKE_BALL); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(opponentLeftSpeed); Item(ITEM_GREAT_BALL); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(opponentRightSpeed); Item(ITEM_ULTRA_BALL); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SURF); }
+ } SCENE {
+ ABILITY_POPUP(playerLeft, ABILITY_MAGICIAN);
+ } THEN {
+ if (playerRightSpeed == 4)
+ EXPECT(playerLeft->item == ITEM_POKE_BALL);
+ else if (opponentLeftSpeed == 4)
+ EXPECT(playerLeft->item == ITEM_GREAT_BALL);
+ else if (playerRightSpeed == 4)
+ EXPECT(playerLeft->item == ITEM_ULTRA_BALL);
+ }
+}
diff --git a/test/battle/ability/power_construct.c b/test/battle/ability/power_construct.c
index 6a8ca9db5f..07d52c1767 100644
--- a/test/battle/ability/power_construct.c
+++ b/test/battle/ability/power_construct.c
@@ -2,3 +2,56 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Power Construct (Ability) test titles")
+
+SINGLE_BATTLE_TEST("Power Construct switches Zygarde's form when HP is below half")
+{
+ u16 baseSpecies;
+ PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; }
+ PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; }
+
+ GIVEN {
+ PLAYER(baseSpecies)
+ {
+ Ability(ABILITY_POWER_CONSTRUCT);
+ HP((GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP) / 2) + 1);
+ }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SCRATCH); MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ MESSAGE("You sense the presence of many!");
+ ABILITY_POPUP(player, ABILITY_POWER_CONSTRUCT);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, player);
+ } THEN {
+ EXPECT_EQ(player->species, SPECIES_ZYGARDE_COMPLETE);
+ }
+}
+
+WILD_BATTLE_TEST("Power Construct Zygarde reverts to its original form upon catching")
+{
+ u16 baseSpecies;
+ PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; }
+ PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(baseSpecies)
+ {
+ Ability(ABILITY_POWER_CONSTRUCT);
+ HP((GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP) / 2) + 1);
+ }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SCRATCH); }
+ TURN { USE_ITEM(player, ITEM_MASTER_BALL); }
+ } SCENE {
+ // Turn 1
+ MESSAGE("You sense the presence of many!");
+ ABILITY_POPUP(opponent, ABILITY_POWER_CONSTRUCT);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, opponent);
+
+ // Turn 2
+ ANIMATION(ANIM_TYPE_SPECIAL, B_ANIM_BALL_THROW, player);
+ } THEN {
+ EXPECT_EQ(GetMonData(&gPlayerParty[1], MON_DATA_SPECIES), baseSpecies);
+ }
+}
diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c
index 9bf6d85f43..bad29b4c9a 100644
--- a/test/battle/ability/protosynthesis.c
+++ b/test/battle/ability/protosynthesis.c
@@ -200,3 +200,21 @@ SINGLE_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on
NOT ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
}
}
+
+SINGLE_BATTLE_TEST("Protosynthesis activates after weather was reset")
+{
+ GIVEN {
+ PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_SUNNY_DAY); }
+ TURN { MOVE(player, MOVE_RAIN_DANCE); }
+ TURN { MOVE(player, MOVE_SUNNY_DAY); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
+ ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
+ ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
+ }
+}
diff --git a/test/battle/ability/receiver.c b/test/battle/ability/receiver.c
index a9155b7244..7e6b7076ba 100644
--- a/test/battle/ability/receiver.c
+++ b/test/battle/ability/receiver.c
@@ -1,4 +1,63 @@
#include "global.h"
#include "test/battle.h"
+DOUBLE_BATTLE_TEST("Receiver copies ally's ability when they faint and immediately activates it")
+{
+ GIVEN {
+ ASSUME(!gAbilitiesInfo[ABILITY_INTIMIDATE].cantBeCopied);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); }
+ OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); HP(1); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); }
+ } SCENE {
+ ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
+ ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER);
+ ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
+ } THEN {
+ EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
+ EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Receiver copies ally's ability when they faint and can activate it on future moves")
+{
+ GIVEN {
+ ASSUME(!gAbilitiesInfo[ABILITY_WATER_ABSORB].cantBeCopied);
+ ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); }
+ OPPONENT(SPECIES_LANTURN) { Ability(ABILITY_WATER_ABSORB); HP(1); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
+ ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER);
+ ABILITY_POPUP(opponentLeft, ABILITY_WATER_ABSORB);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerRight);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Receiver copies ally's Soul Heart and immediately activates it")
+{
+ GIVEN {
+ ASSUME(!gAbilitiesInfo[ABILITY_SOUL_HEART].cantBeCopied);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); }
+ OPPONENT(SPECIES_MAGEARNA) { Ability(ABILITY_SOUL_HEART); HP(1); }
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
+ ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER);
+ ABILITY_POPUP(opponentLeft, ABILITY_SOUL_HEART);
+ } THEN {
+ EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
+ }
+}
+
TO_DO_BATTLE_TEST("TODO: Write Receiver (Ability) test titles")
diff --git a/test/battle/ability/shield_dust.c b/test/battle/ability/shield_dust.c
index 2d7358335d..2fc4f51d5c 100644
--- a/test/battle/ability/shield_dust.c
+++ b/test/battle/ability/shield_dust.c
@@ -188,3 +188,31 @@ SINGLE_BATTLE_TEST("Shield Dust does not prevent ability stat changes")
MESSAGE("Vivillon's Speed fell!");
}
}
+
+AI_SINGLE_BATTLE_TEST("AI will score secondary effects against shield dust correctly")
+{
+ AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
+ GIVEN {
+ PLAYER(SPECIES_DUSTOX){ Ability(ABILITY_SHIELD_DUST); Moves(MOVE_GUST); }
+ OPPONENT(SPECIES_SUNFLORA){ Ability(ABILITY_EARLY_BIRD); Moves(MOVE_MYSTICAL_FIRE, MOVE_FIERY_DANCE); }
+ } WHEN {
+ TURN {
+ MOVE(player, MOVE_GUST);
+ EXPECT_MOVE(opponent, MOVE_FIERY_DANCE);
+ }
+ }
+}
+
+AI_SINGLE_BATTLE_TEST("AI will score secondary effects against shield dust correctly when it has Mold Breaker")
+{
+ AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
+ GIVEN {
+ PLAYER(SPECIES_DUSTOX){ Ability(ABILITY_SHIELD_DUST); Moves(MOVE_GUST); }
+ OPPONENT(SPECIES_SUNFLORA){ Ability(ABILITY_MOLD_BREAKER); Moves(MOVE_MYSTICAL_FIRE, MOVE_FIERY_DANCE); }
+ } WHEN {
+ TURN {
+ MOVE(player, MOVE_GUST);
+ EXPECT_MOVE(opponent, MOVE_MYSTICAL_FIRE);
+ }
+ }
+}
diff --git a/test/battle/ability/shields_down.c b/test/battle/ability/shields_down.c
index f9def2991d..554e67b0d8 100644
--- a/test/battle/ability/shields_down.c
+++ b/test/battle/ability/shields_down.c
@@ -1,28 +1,30 @@
#include "global.h"
#include "test/battle.h"
-SINGLE_BATTLE_TEST("Minior Meteor transforms into Minior Core on switch-in if it has 1/2 or less health")
+SINGLE_BATTLE_TEST("Minior Core doesn't transform into Minior Meteor on switch-in if it has 1/2 or less health")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
- OPPONENT(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); HP(1); }
+ OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(50); MaxHP(100); }
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); }
} SCENE {
- ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
- ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ NONE_OF {
+ ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ }
} THEN {
EXPECT_EQ(opponent->species, SPECIES_MINIOR_CORE);
}
}
-SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it more then 1/2 health")
+SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it has more than 1/2 health")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
- OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); }
+ OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(51); MaxHP(101); }
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); }
} SCENE {
@@ -32,3 +34,44 @@ SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it
EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR);
}
}
+
+SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on battle start if it has more than 1/2 health")
+{
+ GIVEN {
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(51); MaxHP(101); }
+ } WHEN {
+ TURN { }
+ } SCENE {
+ ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ } THEN {
+ EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR);
+ }
+}
+
+SINGLE_BATTLE_TEST("Shields Down protects Minior Meteor from status conditions")
+{
+ u32 species, hp;
+ PARAMETRIZE { species = SPECIES_MINIOR_METEOR; hp = 300; }
+ PARAMETRIZE { species = SPECIES_MINIOR_CORE; hp = 100; }
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_NON_VOLATILE_STATUS);
+ ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(species) { Ability(ABILITY_SHIELDS_DOWN); HP(hp); MaxHP(300); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_WILL_O_WISP); }
+ } SCENE {
+ if (species == SPECIES_MINIOR_METEOR)
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player);
+ else
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player);
+ } THEN {
+ if (species == SPECIES_MINIOR_METEOR)
+ EXPECT_EQ(opponent->status1, STATUS1_NONE);
+ else
+ EXPECT(opponent->status1 & STATUS1_BURN);
+ }
+}
diff --git a/test/battle/ability/tangling_hair.c b/test/battle/ability/tangling_hair.c
index 13a0f27cb0..849c83df64 100644
--- a/test/battle/ability/tangling_hair.c
+++ b/test/battle/ability/tangling_hair.c
@@ -8,7 +8,6 @@ ASSUMPTIONS
ASSUME(MoveMakesContact(MOVE_SCRATCH) == TRUE);
}
-
SINGLE_BATTLE_TEST("Tangling Hair drops opposing mon's speed if ability user got hit by a contact move")
{
u32 move;
@@ -85,3 +84,16 @@ SINGLE_BATTLE_TEST("Tangling Hair does not activate on confusion damage")
}
}
}
+
+SINGLE_BATTLE_TEST("Tangling Hair does not trigger on Clear Body")
+{
+ GIVEN {
+ PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_TANGLING_HAIR); }
+ OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); };
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SCRATCH); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
+ NOT ABILITY_POPUP(player, ABILITY_TANGLING_HAIR);
+ }
+}
diff --git a/test/battle/ability/toxic_debris.c b/test/battle/ability/toxic_debris.c
index 61e128e985..4661849905 100644
--- a/test/battle/ability/toxic_debris.c
+++ b/test/battle/ability/toxic_debris.c
@@ -120,3 +120,23 @@ SINGLE_BATTLE_TEST("Air Balloon is popped after Toxic Debris activates")
MESSAGE("Glimmora's Air Balloon popped!");
}
}
+
+DOUBLE_BATTLE_TEST("Toxic Debris sets Toxic Spikes on the opposing side even when hit by an ally")
+{
+ struct BattlePokemon *user = NULL;
+
+ PARAMETRIZE{ user = opponentLeft; }
+ PARAMETRIZE{ user = opponentRight; }
+ PARAMETRIZE{ user = playerRight; }
+ GIVEN {
+ PLAYER(SPECIES_GLIMMORA) { Ability(ABILITY_TOXIC_DEBRIS); }
+ PLAYER(SPECIES_WYNAUT) { }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT) { }
+ } WHEN {
+ TURN { MOVE(user, MOVE_SCRATCH, target: playerLeft); }
+ } SCENE {
+ ABILITY_POPUP(playerLeft, ABILITY_TOXIC_DEBRIS);
+ MESSAGE("Poison spikes were scattered on the ground all around the opposing team!");
+ }
+}
diff --git a/test/battle/badge_boost.c b/test/battle/badge_boost.c
new file mode 100644
index 0000000000..eb8cecb240
--- /dev/null
+++ b/test/battle/badge_boost.c
@@ -0,0 +1,166 @@
+#include "global.h"
+#include "event_data.h"
+#include "test/battle.h"
+
+WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_ATTACK boost Attack", s16 dmg)
+{
+ u32 badge = 0;
+ u32 genConfig = 0;
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ PARAMETRIZE{badge = FALSE; genConfig = gen;}
+ PARAMETRIZE{badge = TRUE; genConfig = gen;}
+ }
+ GIVEN {
+ if (badge)
+ FlagSet(B_FLAG_BADGE_BOOST_ATTACK);
+ else
+ FlagClear(B_FLAG_BADGE_BOOST_ATTACK);
+ WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) {}
+ OPPONENT(SPECIES_WOBBUFFET) {}
+ } WHEN {
+ TURN { MOVE(player, MOVE_SCRATCH); }
+ } SCENE {
+ HP_BAR(opponent, captureDamage: &results[i].dmg);
+ } FINALLY {
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ if (gen <= GEN_3)
+ EXPECT_GT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ else
+ EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ }
+ }
+}
+
+WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPATK boost Special Attack", s16 dmg)
+{
+ u32 badge = 0;
+ u32 genConfig = 0;
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ PARAMETRIZE{badge = FALSE; genConfig = gen;}
+ PARAMETRIZE{badge = TRUE; genConfig = gen;}
+ }
+ GIVEN {
+ if (badge)
+ FlagSet(B_FLAG_BADGE_BOOST_SPATK);
+ else
+ FlagClear(B_FLAG_BADGE_BOOST_SPATK);
+ WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) {}
+ OPPONENT(SPECIES_WOBBUFFET) {}
+ } WHEN {
+ TURN { MOVE(player, MOVE_THUNDER_SHOCK); }
+ } SCENE {
+ HP_BAR(opponent, captureDamage: &results[i].dmg);
+ } FINALLY {
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ if (gen <= GEN_3)
+ EXPECT_GT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ else
+ EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ }
+ }
+}
+
+WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_DEFENSE boost Defense", s16 dmg)
+{
+ u32 badge = 0;
+ u32 genConfig = 0;
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ PARAMETRIZE{badge = FALSE; genConfig = gen;}
+ PARAMETRIZE{badge = TRUE; genConfig = gen;}
+ }
+
+ GIVEN {
+ if (badge)
+ FlagSet(B_FLAG_BADGE_BOOST_DEFENSE);
+ else
+ FlagClear(B_FLAG_BADGE_BOOST_DEFENSE);
+ WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) {}
+ OPPONENT(SPECIES_WOBBUFFET) {}
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SCRATCH); }
+ } SCENE {
+ HP_BAR(player, captureDamage: &results[i].dmg);
+ } FINALLY {
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ if (gen <= GEN_3)
+ EXPECT_LT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ else
+ EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ }
+ }
+}
+
+WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPDEF boost Special Defense", s16 dmg)
+{
+ u32 badge = 0;
+ u32 genConfig = 0;
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ PARAMETRIZE{badge = FALSE; genConfig = gen;}
+ PARAMETRIZE{badge = TRUE; genConfig = gen;}
+ }
+
+ GIVEN {
+ if (badge)
+ FlagSet(B_FLAG_BADGE_BOOST_SPDEF);
+ else
+ FlagClear(B_FLAG_BADGE_BOOST_SPDEF);
+ WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) {}
+ OPPONENT(SPECIES_WOBBUFFET) {}
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_THUNDER_SHOCK); }
+ } SCENE {
+ HP_BAR(player, captureDamage: &results[i].dmg);
+ } FINALLY {
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ if (gen <= GEN_3)
+ EXPECT_LT(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ else
+ EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg);
+ }
+ }
+}
+
+WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPEED boost Speed", s16 dmg)
+{
+ u32 badge = 0;
+ u32 genConfig = 0;
+ for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++)
+ {
+ PARAMETRIZE{badge = FALSE; genConfig = gen;}
+ PARAMETRIZE{badge = TRUE; genConfig = gen;}
+ }
+ GIVEN {
+ if (badge)
+ FlagSet(B_FLAG_BADGE_BOOST_SPEED);
+ else
+ FlagClear(B_FLAG_BADGE_BOOST_SPEED);
+ WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig);
+ PLAYER(SPECIES_WOBBUFFET) { Speed(100); HP(1); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(101); HP(1); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SCRATCH);}
+ } THEN {
+ if (badge && genConfig <= GEN_3)
+ {
+ EXPECT_EQ(opponent->hp, 0);
+ EXPECT_EQ(player->hp, 1);
+ }
+ else
+ {
+ EXPECT_EQ(opponent->hp, 1);
+ EXPECT_EQ(player->hp, 0);
+ }
+ }
+}
diff --git a/test/battle/form_change/faint.c b/test/battle/form_change/faint.c
index 8fc95cdc65..316ecca0bf 100644
--- a/test/battle/form_change/faint.c
+++ b/test/battle/form_change/faint.c
@@ -1,9 +1,24 @@
#include "global.h"
#include "test/battle.h"
-SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
+SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Shield)")
+{
+ GIVEN {
+ PLAYER(SPECIES_AEGISLASH_SHIELD) { HP(1); }
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); }
+ } SCENE {
+ MESSAGE("The opposing Wobbuffet used Gust!");
+ MESSAGE("Aegislash fainted!");
+ } THEN {
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD);
+ }
+}
+
+SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Blade)")
{
- KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_AEGISLASH_BLADE) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
@@ -14,7 +29,7 @@ SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
MESSAGE("The opposing Wobbuffet used Gust!");
MESSAGE("Aegislash fainted!");
} THEN {
- EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD);
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD);
}
}
diff --git a/test/battle/form_change/mega_evolution.c b/test/battle/form_change/mega_evolution.c
index 8cce1a084d..3e27a84608 100644
--- a/test/battle/form_change/mega_evolution.c
+++ b/test/battle/form_change/mega_evolution.c
@@ -75,10 +75,10 @@ SINGLE_BATTLE_TEST("Mega Evolution doesn't affect turn order (Gen6)")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_6);
- PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); }
- OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
+ PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); }
+ OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
- TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
+ TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("The opposing Wobbuffet used Celebrate!");
MESSAGE("Gardevoir used Celebrate!");
@@ -91,10 +91,10 @@ SINGLE_BATTLE_TEST("Mega Evolution affects turn order (Gen7+)")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_7);
- PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); }
- OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
+ PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE);}
+ OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
- TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
+ TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Gardevoir used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
@@ -117,7 +117,7 @@ SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn orde
MESSAGE("Sableye used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
} THEN {
- ASSUME(player->speed == 45);
+ ASSUME(player->speed == 105);
}
}
diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c
index 9d7ec1c396..4b1bf6a8e3 100644
--- a/test/battle/form_change/ultra_burst.c
+++ b/test/battle/form_change/ultra_burst.c
@@ -59,10 +59,10 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_7);
- PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); }
- OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
+ PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z);}
+ OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
- TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
+ TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
} SCENE {
MESSAGE("Necrozma used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
diff --git a/test/battle/hazards.c b/test/battle/hazards.c
index 9c974bb7b1..17ecb41f5c 100644
--- a/test/battle/hazards.c
+++ b/test/battle/hazards.c
@@ -38,3 +38,26 @@ SINGLE_BATTLE_TEST("Hazards are applied based on order of set up")
EXPECT_EQ(gBattleStruct->hazardsQueue[0][5], HAZARDS_NONE);
}
}
+
+SINGLE_BATTLE_TEST("Hazards are applied correctly after a battler faints")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_FINAL_GAMBIT) == EFFECT_FINAL_GAMBIT);
+ PLAYER(SPECIES_WYNAUT);
+ PLAYER(SPECIES_WOBBUFFET) { HP(1); }
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_STEALTH_ROCK);
+ MOVE(player, MOVE_FINAL_GAMBIT);
+ SEND_OUT(player, 1);
+ SEND_OUT(player, 2); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FINAL_GAMBIT, player);
+ MESSAGE("Wynaut fainted!");
+ MESSAGE("Pointed stones dug into Wobbuffet!");
+ MESSAGE("Wobbuffet fainted!");
+ MESSAGE("Pointed stones dug into Wynaut!");
+ }
+}
diff --git a/test/battle/hold_effect/destiny_knot.c b/test/battle/hold_effect/destiny_knot.c
new file mode 100644
index 0000000000..b8097c6012
--- /dev/null
+++ b/test/battle/hold_effect/destiny_knot.c
@@ -0,0 +1,66 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gItemsInfo[ITEM_DESTINY_KNOT].holdEffect == HOLD_EFFECT_DESTINY_KNOT);
+}
+
+SINGLE_BATTLE_TEST("Destiny Knot infatuates back when holder is targeted")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); Item(ITEM_DESTINY_KNOT); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_ATTRACT); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ MESSAGE("Wobbuffet fell in love because of the Destiny Knot!");
+ } THEN {
+ EXPECT(player->volatiles.infatuation);
+ }
+}
+
+SINGLE_BATTLE_TEST("Destiny Knot infatuates back when holder is attacking")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); Item(ITEM_DESTINY_KNOT);}
+ OPPONENT(SPECIES_CLEFAIRY) { Gender(MON_FEMALE); Ability(ABILITY_CUTE_CHARM);}
+ } WHEN {
+ TURN { MOVE(player, MOVE_TACKLE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
+ MESSAGE("The opposing Clefairy fell in love because of the Destiny Knot!");
+ } THEN {
+ EXPECT(opponent->volatiles.infatuation);
+ }
+}
+
+
+SINGLE_BATTLE_TEST("Destiny Knot procs but fails if the target is already infatuated")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); }
+ OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); Item(ITEM_DESTINY_KNOT); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_ATTRACT); MOVE(player, MOVE_ATTRACT, WITH_RNG(RNG_INFATUATION, FALSE)); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ MESSAGE("But it failed!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Destiny Knot procs but fails if the target is oblivious")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); Ability(ABILITY_OBLIVIOUS); }
+ OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); Item(ITEM_DESTINY_KNOT); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_ATTRACT); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
+ ABILITY_POPUP(player, ABILITY_OBLIVIOUS);
+ } THEN {
+ EXPECT(!player->volatiles.infatuation);
+ }
+}
diff --git a/test/battle/move_effect/belly_drum.c b/test/battle/move_effect/belly_drum.c
index 20024f5b91..194ee5b9d0 100644
--- a/test/battle/move_effect/belly_drum.c
+++ b/test/battle/move_effect/belly_drum.c
@@ -159,7 +159,6 @@ SINGLE_BATTLE_TEST("Belly Drum maximizes the user's Attack stat, even when below
SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even with Contrary")
{
- KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveEffect(MOVE_CHARM) == EFFECT_ATTACK_DOWN_2);
PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); }
@@ -190,7 +189,6 @@ SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even
SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6")
{
- KNOWN_FAILING;
GIVEN {
ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2);
PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); }
@@ -215,6 +213,6 @@ SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6"
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player);
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
HP_BAR(player, hp: maxHP / 2);
- MESSAGE("Wobbuffet cut its own HP and maximized its Attack!");
+ MESSAGE("Serperior cut its own HP and maximized its Attack!");
}
}
diff --git a/test/battle/move_effect/echoed_voice.c b/test/battle/move_effect/echoed_voice.c
index 3c36270454..29a2b6e487 100644
--- a/test/battle/move_effect/echoed_voice.c
+++ b/test/battle/move_effect/echoed_voice.c
@@ -1,7 +1,169 @@
#include "global.h"
#include "test/battle.h"
-TO_DO_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5");
-TO_DO_BATTLE_TEST("Echoed Voice's power is reset when using a different move");
-TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it misses");
-TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect");
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_ECHOED_VOICE) == EFFECT_ECHOED_VOICE);
+}
+
+SINGLE_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5")
+{
+ s16 damage[6];
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SOFT_BOILED) == EFFECT_SOFTBOILED);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_BLISSEY);
+ } WHEN {
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[0]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[2]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[3]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[4]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[5]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(3.0), damage[2]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[3]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(5.0), damage[4]);
+ EXPECT_EQ(damage[4], damage[5]);
+ }
+}
+
+SINGLE_BATTLE_TEST("Echoed Voice's power increases even if used by another battler")
+{
+ s16 damage[2];
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent);
+ HP_BAR(player, captureDamage: &damage[0]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
+ }
+}
+
+SINGLE_BATTLE_TEST("Echoed Voice's power does not change until the end of the turn")
+{
+ s16 damage[3];
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[0]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent);
+ HP_BAR(player, captureDamage: &damage[1]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[2]);
+ } THEN {
+ EXPECT_EQ(damage[0], damage[1]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[2]);
+ }
+}
+
+SINGLE_BATTLE_TEST("Echoed Voice's power increase is reset when no battler uses it successfully during a turn")
+{
+ s16 damage[3];
+
+ GIVEN {
+ ASSUME(MoveHasAdditionalEffect(MOVE_BITE, MOVE_EFFECT_FLINCH));
+ PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(opponent, MOVE_BITE); MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent);
+ HP_BAR(player, captureDamage: &damage[0]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent);
+ MESSAGE("Wobbuffet flinched and couldn't move!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[2]);
+ } THEN {
+ EXPECT_EQ(damage[0], damage[2]);
+ EXPECT_NE(damage[1], damage[2]);
+ }
+}
+
+SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it misses")
+{
+ s16 damage[3];
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SAND_ATTACK) == EFFECT_ACCURACY_DOWN);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SAND_ATTACK); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE, hit: FALSE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[0]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+ MESSAGE("Wobbuffet's attack missed!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[2]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]);
+ }
+}
+
+SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect")
+{
+ s16 damage[3];
+
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_PROTECT); }
+ TURN { MOVE(player, MOVE_ECHOED_VOICE); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[0]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player);
+ HP_BAR(opponent, captureDamage: &damage[2]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]);
+ EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]);
+ }
+}
diff --git a/test/battle/move_effect/focus_energy.c b/test/battle/move_effect/focus_energy.c
index 48924132ef..17a08ba7f3 100644
--- a/test/battle/move_effect/focus_energy.c
+++ b/test/battle/move_effect/focus_energy.c
@@ -23,7 +23,34 @@ SINGLE_BATTLE_TEST("Focus Energy increases the user's critical hit ratio by 1 st
}
PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT);
GIVEN {
- WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig);
+ WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, (genConfig == GEN_1)? GEN_2 : genConfig);
+ WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig);
+ ASSUME(GetSpeciesBaseSpeed(SPECIES_WOBBUFFET) == 33);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ if (useFocusEnergy)
+ TURN { MOVE(player, MOVE_FOCUS_ENERGY); }
+ TURN { MOVE(player, MOVE_SCRATCH); }
+ } SCENE {
+ if (useFocusEnergy)
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
+ MESSAGE("A critical hit!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Focus Energy multiplies crit chance by 4 with gen 1 crit chance")
+{
+ bool32 useFocusEnergy = 0;
+ u32 genConfig = 0, chance = 0;
+ for (u32 j = GEN_1; j <= GEN_9; j++) {
+ PARAMETRIZE { genConfig = j; useFocusEnergy = FALSE; chance = 16;}
+ PARAMETRIZE { genConfig = j; useFocusEnergy = TRUE; chance = 4;}
+ }
+ PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT);
+ GIVEN {
+ WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, GEN_1);
WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig);
ASSUME(GetSpeciesBaseSpeed(SPECIES_WOBBUFFET) == 33);
PLAYER(SPECIES_WOBBUFFET);
diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c
index 0ced64d7e5..f587d078a0 100644
--- a/test/battle/move_effect/hit_escape.c
+++ b/test/battle/move_effect/hit_escape.c
@@ -113,7 +113,7 @@ SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out after Ice Face acti
}
}
-SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: player side")
+SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: player side")
{
GIVEN {
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
@@ -126,7 +126,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon
ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
HP_BAR(opponent);
- ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("2 sent out Wynaut!");
@@ -136,7 +135,7 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon
}
}
-SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side")
+SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: opposing side")
{
GIVEN {
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
@@ -149,7 +148,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon
ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
HP_BAR(opponent);
- ABILITY_POPUP(player, ABILITY_INTIMIDATE);
MESSAGE("2 sent out Wynaut!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c
index 3fdeadcecc..eb0874d7bf 100644
--- a/test/battle/move_effect/knock_off.c
+++ b/test/battle/move_effect/knock_off.c
@@ -6,6 +6,33 @@ ASSUMPTIONS
ASSUME(GetMoveEffect(MOVE_KNOCK_OFF) == EFFECT_KNOCK_OFF);
}
+WILD_BATTLE_TEST("Knock Off does not remove item when used by Wild Pokemon (Gen 5+)")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); }
+ OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_EVIOLITE); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_KNOCK_OFF); }
+ TURN { MOVE(player, MOVE_KNOCK_OFF); }
+ } SCENE {
+ // Turn 1
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent);
+ if (B_KNOCK_OFF_REMOVAL >= GEN_5)
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player);
+ else
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player);
+ // Turn 2
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, opponent);
+ } THEN {
+ EXPECT(player->item == ITEM_LEFTOVERS);
+ if (B_KNOCK_OFF_REMOVAL >= GEN_5)
+ EXPECT(opponent->item == ITEM_NONE);
+ else
+ EXPECT(opponent->item == ITEM_EVIOLITE);
+ }
+}
+
SINGLE_BATTLE_TEST("Knock Off knocks a healing berry before it has the chance to activate")
{
GIVEN {
diff --git a/test/battle/move_effect/misty_terrain.c b/test/battle/move_effect/misty_terrain.c
index b96f0c650d..cd9a92eb37 100644
--- a/test/battle/move_effect/misty_terrain.c
+++ b/test/battle/move_effect/misty_terrain.c
@@ -87,3 +87,16 @@ SINGLE_BATTLE_TEST("Misty Terrain lasts for 5 turns")
MESSAGE("The mist disappeared from the battlefield.");
}
}
+
+SINGLE_BATTLE_TEST("Misty Terrain will fail if there is one already on the field")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_MISTY_TERRAIN); MOVE(opponent, MOVE_MISTY_TERRAIN); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_TERRAIN, player);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_TERRAIN, opponent);
+ }
+}
diff --git a/test/battle/move_effect/psychic_terrain.c b/test/battle/move_effect/psychic_terrain.c
index b85653a0be..2c5089c704 100644
--- a/test/battle/move_effect/psychic_terrain.c
+++ b/test/battle/move_effect/psychic_terrain.c
@@ -114,6 +114,38 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves")
}
}
+SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves against semi-invulnerable targets")
+{
+ u32 move = 0, shouldWork = 0;
+ PARAMETRIZE { move = MOVE_SOLAR_BEAM; shouldWork = FALSE;}
+ PARAMETRIZE { move = MOVE_FLY; shouldWork = TRUE;}
+ GIVEN {
+ PLAYER(SPECIES_SHROODLE) { Ability(ABILITY_PRANKSTER); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); MOVE(opponent,move);}
+ TURN { MOVE(player, MOVE_TOXIC); SKIP_TURN(opponent);}
+ } SCENE {
+ if (shouldWork)
+ {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
+ }
+ else
+ {
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
+ ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
+ }
+ }
+ } THEN {
+ if (shouldWork)
+ EXPECT(opponent->status1 & STATUS1_TOXIC_POISON);
+ else
+ EXPECT(!(opponent->status1 & STATUS1_TOXIC_POISON));
+ }
+}
+
SINGLE_BATTLE_TEST("Psychic Terrain lasts for 5 turns")
{
GIVEN {
diff --git a/test/battle/move_effect/rest.c b/test/battle/move_effect/rest.c
index 4d0d85b560..fcf2b15567 100644
--- a/test/battle/move_effect/rest.c
+++ b/test/battle/move_effect/rest.c
@@ -1,4 +1,121 @@
#include "global.h"
#include "test/battle.h"
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
+}
+
+SINGLE_BATTLE_TEST("Rest causes the user to fall asleep and restores HP to full")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_REST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
+ } THEN {
+ EXPECT(player->status1 & STATUS1_SLEEP);
+ EXPECT_EQ(player->hp, player->maxHP);
+ }
+}
+
+SINGLE_BATTLE_TEST("Rest fails if the user is at full HP")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_REST); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
+ } THEN {
+ EXPECT(!(player->status1 & STATUS1_SLEEP));
+ }
+}
+
+SINGLE_BATTLE_TEST("Rest fails if the user is protected by Leaf Guard")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SUNNY_DAY) == EFFECT_SUNNY_DAY);
+ ASSUME(B_LEAF_GUARD_PREVENTS_REST >= GEN_5);
+ PLAYER(SPECIES_CHIKORITA) { Ability(ABILITY_LEAF_GUARD); HP(1); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
+ } THEN {
+ EXPECT(!(player->status1 & STATUS1_SLEEP));
+ }
+}
+
+SINGLE_BATTLE_TEST("Rest fails if the user is protected by Shields Down")
+{
+ GIVEN {
+ PLAYER(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); HP(299); MaxHP(300); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_REST); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
+ } THEN {
+ EXPECT(!(player->status1 & STATUS1_SLEEP));
+ }
+}
+
+SINGLE_BATTLE_TEST("Rest fails if the user is protected by Electric/Misty Terrain")
+{
+ u32 move;
+ PARAMETRIZE { move = MOVE_ELECTRIC_TERRAIN; }
+ PARAMETRIZE { move = MOVE_MISTY_TERRAIN; }
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_ELECTRIC_TERRAIN) == EFFECT_ELECTRIC_TERRAIN);
+ ASSUME(GetMoveEffect(MOVE_MISTY_TERRAIN) == EFFECT_MISTY_TERRAIN);
+ ASSUME(GetSpeciesType(SPECIES_WYNAUT, 0) != TYPE_FLYING && GetSpeciesType(SPECIES_WYNAUT, 1) != TYPE_FLYING);
+ PLAYER(SPECIES_WYNAUT) { HP(1); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, move); MOVE(player, MOVE_REST); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
+ } THEN {
+ EXPECT(!(player->status1 & STATUS1_SLEEP));
+ }
+}
+
+SINGLE_BATTLE_TEST("Rest doesn't fail if the user is protected by Safeguard")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_SAFEGUARD) == EFFECT_SAFEGUARD);
+ PLAYER(SPECIES_WOBBUFFET) { HP(1); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_SAFEGUARD); }
+ TURN { MOVE(player, MOVE_REST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
+ } THEN {
+ EXPECT(player->status1 & STATUS1_SLEEP);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Rest doesn't fail if the user is protected by Flower Veil")
+{
+ GIVEN {
+ ASSUME(GetSpeciesType(SPECIES_CHIKORITA, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_CHIKORITA, 1) == TYPE_GRASS);
+ PLAYER(SPECIES_CHIKORITA) { HP(1); }
+ PLAYER(SPECIES_FLORGES) { Ability(ABILITY_FLOWER_VEIL); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_REST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, playerLeft);
+ } THEN {
+ EXPECT(playerLeft->status1 & STATUS1_SLEEP);
+ }
+}
+
TO_DO_BATTLE_TEST("TODO: Write Rest (Move Effect) test titles")
diff --git a/test/battle/move_effect/stomping_tantrum.c b/test/battle/move_effect/stomping_tantrum.c
index 95d93e07e6..06b29334d7 100644
--- a/test/battle/move_effect/stomping_tantrum.c
+++ b/test/battle/move_effect/stomping_tantrum.c
@@ -90,6 +90,31 @@ SINGLE_BATTLE_TEST("Stomping Tantrum will not deal double damage if target prote
}
}
+SINGLE_BATTLE_TEST("Stomping Tantrum will deal double damage if user failed a Protect")
+{
+ s16 damage[2];
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
+ TURN { MOVE(player, MOVE_PROTECT); }
+ TURN { MOVE(player, MOVE_PROTECT, WITH_RNG(RNG_PROTECT_FAIL, USHRT_MAX)); }
+ TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
+ HP_BAR(opponent, captureDamage: &damage[0]);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, player);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, player);
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
+ HP_BAR(opponent, captureDamage: &damage[1]);
+ } THEN {
+ EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
+ }
+}
+
SINGLE_BATTLE_TEST("Stomping Tantrum will not deal double if it missed")
{
s16 damage[2];
diff --git a/test/battle/move_effect_secondary/recharge.c b/test/battle/move_effect_secondary/recharge.c
new file mode 100644
index 0000000000..9146083675
--- /dev/null
+++ b/test/battle/move_effect_secondary/recharge.c
@@ -0,0 +1,52 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(MoveHasAdditionalEffectSelf(MOVE_METEOR_ASSAULT, MOVE_EFFECT_RECHARGE) == TRUE);
+}
+
+SINGLE_BATTLE_TEST("Recharge moves make the user unable to attack for exactly one turn")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { }
+ OPPONENT(SPECIES_WOBBUFFET) { }
+ } WHEN {
+ TURN { MOVE(player, MOVE_METEOR_ASSAULT);}
+ TURN { SKIP_TURN(player);}
+ TURN { MOVE(player, MOVE_TACKLE);}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_METEOR_ASSAULT, player);
+ MESSAGE("Wobbuffet must recharge!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
+ }
+}
+
+SINGLE_BATTLE_TEST("Recharge moves don't timeout when all battlers are recharging")
+{
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { }
+ OPPONENT(SPECIES_WOBBUFFET) { }
+ } WHEN {
+ TURN { MOVE(player, MOVE_METEOR_ASSAULT); MOVE(opponent, MOVE_METEOR_ASSAULT);}
+ }
+}
+
+DOUBLE_BATTLE_TEST("Recharge moves don't timeout when all battlers are recharging (doubles")
+{
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { }
+ PLAYER(SPECIES_WYNAUT) { }
+ OPPONENT(SPECIES_WOBBUFFET) { }
+ OPPONENT(SPECIES_WYNAUT) { }
+ } WHEN {
+ TURN {
+ MOVE(playerLeft, MOVE_METEOR_ASSAULT, target: opponentLeft);
+ MOVE(playerRight, MOVE_METEOR_ASSAULT, target: opponentRight);
+ MOVE(opponentLeft, MOVE_METEOR_ASSAULT, target: playerLeft);
+ MOVE(opponentRight, MOVE_METEOR_ASSAULT, target: playerRight);
+ }
+ }
+}
diff --git a/test/battle/test_runner_features.c b/test/battle/test_runner_features.c
index bb4840799f..2c6d73abcc 100644
--- a/test/battle/test_runner_features.c
+++ b/test/battle/test_runner_features.c
@@ -25,3 +25,58 @@ SINGLE_BATTLE_TEST("Forced abilities activate on switch-in")
MESSAGE("Kadabra's Sp. Atk was heightened!");
}
}
+
+SINGLE_BATTLE_TEST("Setting level doesn't overwrite set stats")
+{
+ u32 level = 0;
+
+ PARAMETRIZE{level = 1;}
+ PARAMETRIZE{level = 10;}
+ PARAMETRIZE{level = 50;}
+ PARAMETRIZE{level = 99;}
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) {HP(5); MaxHP(10); Attack(10); Defense(10); Speed(10); SpAttack(10); SpDefense(10); Level(level); };
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(1);}
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE);}
+ } THEN {
+ EXPECT_EQ(player->hp, 5);
+ EXPECT_EQ(player->maxHP, 10);
+ EXPECT_EQ(player->attack, 10);
+ EXPECT_EQ(player->defense, 10);
+ EXPECT_EQ(player->speed, 10);
+ EXPECT_EQ(player->spAttack, 10);
+ EXPECT_EQ(player->spDefense, 10);
+ }
+}
+
+SINGLE_BATTLE_TEST("Changing forms doesn't overwrite set stats (not HP)")
+{
+ GIVEN {
+ PLAYER(SPECIES_DIANCIE) {Attack(10); Defense(10); Speed(10); SpAttack(10); SpDefense(10); Item(ITEM_DIANCITE);}
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(1);}
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
+ } THEN {
+ EXPECT_EQ(player->attack, 10);
+ EXPECT_EQ(player->defense, 10);
+ EXPECT_EQ(player->speed, 10);
+ EXPECT_EQ(player->spAttack, 10);
+ EXPECT_EQ(player->spDefense, 10);
+ }
+}
+
+SINGLE_BATTLE_TEST("Changing forms doesn't overwrite set stats (HP)")
+{
+ GIVEN {
+ PLAYER(SPECIES_TERAPAGOS) {HP(5); MaxHP(10); TeraType(TYPE_STELLAR);}
+ OPPONENT(SPECIES_WOBBUFFET) {}
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CELEBRATE);}
+ } THEN {
+ EXPECT_EQ(player->hp, 5);
+ EXPECT_EQ(player->maxHP, 10);
+ }
+}
diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c
index 663a720d3d..1821cebcb5 100644
--- a/test/battle/trainer_control.c
+++ b/test/battle/trainer_control.c
@@ -188,6 +188,7 @@ TEST("Difficulty default to Normal is the trainer doesn't have a member for the
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO);
Free(testParty);
+ SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
}
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (EASY)")
@@ -199,6 +200,7 @@ TEST("Difficulty changes which party if used for NPCs if defined for the difficu
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD);
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1);
Free(testParty);
+ SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
}
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (HARD)")
@@ -210,6 +212,7 @@ TEST("Difficulty changes which party if used for NPCs if defined for the difficu
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS);
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99);
Free(testParty);
+ SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
}
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (NORMAL)")
diff --git a/test/pokemon.c b/test/pokemon.c
index 6079bd28bd..d6c0cb4d42 100644
--- a/test/pokemon.c
+++ b/test/pokemon.c
@@ -477,6 +477,24 @@ TEST("Optimised SetMonData")
EXPECT_FASTER(optimised, vanilla);
}
+//Sanity check for a CalculateMonStats refactor (could be deleted or improved)
+TEST("CalculateMonStats")
+{
+ ZeroPlayerPartyMons();
+
+ RUN_OVERWORLD_SCRIPT(
+ givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, shinyMode=SHINY_MODE_ALWAYS, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7;
+ );
+
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MAX_HP), 497);
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK), 71);
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF), 143);
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED), 82);
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK), 83);
+ EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF), 134);
+
+}
+
TEST("BoxPokemon encryption works")
{
u32 raw[20] =
diff --git a/test/save.c b/test/save.c
new file mode 100644
index 0000000000..4e3000a794
--- /dev/null
+++ b/test/save.c
@@ -0,0 +1,35 @@
+#include "global.h"
+#include "pokemon_storage_system.h"
+#include "test/test.h"
+
+// If you would like to ensure save compatibility, update the values below with those for your hack. You can find these through the debug menu.
+// Please note that this simple check is not 100% foolproof, but should be able to catch most unintended shifts.
+#define T_SAVEBLOCK1_SIZE 15568
+#define T_SAVEBLOCK2_SIZE 3884
+#define T_SAVEBLOCK3_SIZE 4
+#define T_POKEMONSTORAGE_SIZE 34144
+
+TEST("SaveBlock1 is backwards compatible")
+{
+ EXPECT_EQ(sizeof(struct SaveBlock1), T_SAVEBLOCK1_SIZE);
+}
+
+TEST("SaveBlock2 is backwards compatible")
+{
+ EXPECT_EQ(sizeof(struct SaveBlock2), T_SAVEBLOCK2_SIZE);
+}
+
+TEST("SaveBlock3 is backwards compatible")
+{
+ EXPECT_EQ(sizeof(struct SaveBlock3), T_SAVEBLOCK3_SIZE);
+}
+
+TEST("PokemonStorage is backwards compatible")
+{
+ EXPECT_EQ(sizeof(struct PokemonStorage), T_POKEMONSTORAGE_SIZE);
+}
+
+#undef T_SAVEBLOCK1_SIZE
+#undef T_SAVEBLOCK2_SIZE
+#undef T_SAVEBLOCK3_SIZE
+#undef T_POKEMONSTORAGE_SIZE
diff --git a/test/test_runner.c b/test/test_runner.c
index 450283ab58..ac4b20cd2c 100644
--- a/test/test_runner.c
+++ b/test/test_runner.c
@@ -1,4 +1,5 @@
#include
+#include "fake_rtc.h"
#include "global.h"
#include "gpu_regs.h"
#include "load_save.h"
@@ -184,6 +185,13 @@ void TestRunner_CheckMemory(void)
}
}
+static void ClearSaveBlocks(void)
+{
+ ClearSav1();
+ ClearSav2();
+ ClearSav3();
+}
+
void CB2_TestRunner(void)
{
top:
@@ -201,17 +209,13 @@ top:
gTestRunnerState.filterMode = DetectFilterMode(gTestRunnerArgv);
MoveSaveBlocks_ResetHeap();
- ClearSav1();
- ClearSav2();
- ClearSav3();
gIntrTable[7] = Intr_Timer2;
- gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SET;
-
// The current test restarted the ROM (e.g. by jumping to NULL).
if (gPersistentTestRunnerState.address != 0)
{
+ ClearSaveBlocks();
gTestRunnerState.test = __start_tests;
while ((uintptr_t)gTestRunnerState.test != gPersistentTestRunnerState.address)
{
@@ -255,6 +259,7 @@ top:
break;
case STATE_ASSIGN_TEST:
+ ClearSaveBlocks();
while (1)
{
if (gTestRunnerState.test == __stop_tests)
diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c
index 2f9dd037fb..395381d1d2 100644
--- a/test/test_runner_battle.c
+++ b/test/test_runner_battle.c
@@ -1344,6 +1344,7 @@ static void TearDownBattle(void)
// Zero out the parties, data in them could potentially carry over
ZeroPlayerPartyMons();
ZeroEnemyPartyMons();
+ SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
FreeMonSpritesGfx();
FreeBattleSpritesData();
@@ -1681,7 +1682,9 @@ void Level_(u32 sourceLine, u32 level)
INVALID_IF(level == 0 || level > MAX_LEVEL, "Illegal level: %d", level);
SetMonData(DATA.currentMon, MON_DATA_LEVEL, &level);
SetMonData(DATA.currentMon, MON_DATA_EXP, &gExperienceTables[gSpeciesInfo[species].growthRate][level]);
+ gMain.inBattle = TRUE;
CalculateMonStats(DATA.currentMon);
+ gMain.inBattle = FALSE;
}
void MaxHP_(u32 sourceLine, u32 maxHP)
@@ -1689,6 +1692,8 @@ void MaxHP_(u32 sourceLine, u32 maxHP)
INVALID_IF(!DATA.currentMon, "MaxHP outside of PLAYER/OPPONENT");
INVALID_IF(maxHP == 0, "Illegal max HP: %d", maxHP);
SetMonData(DATA.currentMon, MON_DATA_MAX_HP, &maxHP);
+ bool32 hyperTrainingFlag = TRUE;
+ SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_HP, &hyperTrainingFlag);
}
void HP_(u32 sourceLine, u32 hp)
@@ -1704,6 +1709,8 @@ void Attack_(u32 sourceLine, u32 attack)
INVALID_IF(!DATA.currentMon, "Attack outside of PLAYER/OPPONENT");
INVALID_IF(attack == 0, "Illegal attack: %d", attack);
SetMonData(DATA.currentMon, MON_DATA_ATK, &attack);
+ bool32 hyperTrainingFlag = TRUE;
+ SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_ATK, &hyperTrainingFlag);
}
void Defense_(u32 sourceLine, u32 defense)
@@ -1711,6 +1718,8 @@ void Defense_(u32 sourceLine, u32 defense)
INVALID_IF(!DATA.currentMon, "Defense outside of PLAYER/OPPONENT");
INVALID_IF(defense == 0, "Illegal defense: %d", defense);
SetMonData(DATA.currentMon, MON_DATA_DEF, &defense);
+ bool32 hyperTrainingFlag = TRUE;
+ SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_DEF, &hyperTrainingFlag);
}
void SpAttack_(u32 sourceLine, u32 spAttack)
@@ -1718,6 +1727,8 @@ void SpAttack_(u32 sourceLine, u32 spAttack)
INVALID_IF(!DATA.currentMon, "SpAttack outside of PLAYER/OPPONENT");
INVALID_IF(spAttack == 0, "Illegal special attack: %d", spAttack);
SetMonData(DATA.currentMon, MON_DATA_SPATK, &spAttack);
+ bool32 hyperTrainingFlag = TRUE;
+ SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPATK, &hyperTrainingFlag);
}
void SpDefense_(u32 sourceLine, u32 spDefense)
@@ -1725,6 +1736,8 @@ void SpDefense_(u32 sourceLine, u32 spDefense)
INVALID_IF(!DATA.currentMon, "SpDefense outside of PLAYER/OPPONENT");
INVALID_IF(spDefense == 0, "Illegal special defense: %d", spDefense);
SetMonData(DATA.currentMon, MON_DATA_SPDEF, &spDefense);
+ bool32 hyperTrainingFlag = TRUE;
+ SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPDEF, &hyperTrainingFlag);
}
void Speed_(u32 sourceLine, u32 speed)
@@ -1732,6 +1745,8 @@ void Speed_(u32 sourceLine, u32 speed)
INVALID_IF(!DATA.currentMon, "Speed outside of PLAYER/OPPONENT");
INVALID_IF(speed == 0, "Illegal speed: %d", speed);
SetMonData(DATA.currentMon, MON_DATA_SPEED, &speed);
+ bool32 hyperTrainingFlag = TRUE;
+ SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPEED, &hyperTrainingFlag);
DATA.hasExplicitSpeeds = TRUE;
DATA.explicitSpeeds[DATA.currentSide] |= 1 << DATA.currentPartyIndex;
}
diff --git a/tools/mapjson/mapjson.cpp b/tools/mapjson/mapjson.cpp
index 4dd036dc19..936c2a5286 100644
--- a/tools/mapjson/mapjson.cpp
+++ b/tools/mapjson/mapjson.cpp
@@ -530,12 +530,14 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) {
text << "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n//\n\n";
+ text << "enum\n{\n";
+
int group_num = 0;
vector map_count_vec; //DEBUG
for (auto &group : groups_data["group_order"].array_items()) {
string groupName = json_to_string(group);
- text << "// " << groupName << "\n";
+ text << " // " << groupName << "\n";
vector map_ids;
size_t max_length = 0;
@@ -556,8 +558,8 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) {
int map_id_num = 0;
for (string map_id : map_ids) {
- text << "#define " << map_id << string((max_length - map_id.length() + 1), ' ')
- << "(" << map_id_num++ << " | (" << group_num << " << 8))\n";
+ text << " " << map_id << string(max_length - map_id.length(), ' ')
+ << " = (" << map_id_num++ << " | (" << group_num << " << 8)),\n";
}
text << "\n";
@@ -565,6 +567,8 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) {
map_count_vec.push_back(map_count); //DEBUG
}
+ text << "};\n\n";
+
text << "#define MAP_GROUPS_COUNT " << group_num << "\n\n";
text << get_include_guard_end(guard_name);
diff --git a/tools/misc/make_scr_cmd_constants.py b/tools/misc/make_scr_cmd_constants.py
new file mode 100644
index 0000000000..d158dfcf8a
--- /dev/null
+++ b/tools/misc/make_scr_cmd_constants.py
@@ -0,0 +1,25 @@
+import re
+
+SCR_CMD_PAT = re.compile(r"\tscript_cmd_table_entry (\w+)\s+\w+,\s+[\w=]+\s+@( 0x[0-9a-f]+)")
+
+def main():
+ output = [
+ "//",
+ "// DO NOT MODIFY THIS FILE! It is auto-generated by tools/misc/make_scr_cmd_constants.py",
+ "//",
+ "#ifndef GUARD_SCR_CMD_CONSTANTS_H",
+ "#define GUARD_SCR_CMD_CONSTANTS_H\n",
+ ]
+
+ with open("data/script_cmd_table.inc", "r") as f:
+ for line in f.readlines():
+ if match := re.match(SCR_CMD_PAT, line):
+ new_line = "#define " + match.group(1) + match.group(2)
+ output.append(new_line)
+
+ output.append("\n#endif // GUARD_SCR_CMD_CONSTANTS_H\n")
+ with open("include/constants/script_commands.h", "w+") as f:
+ f.write('\n'.join(output))
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/tools/wild_encounters/wild_encounters_to_header.py b/tools/wild_encounters/wild_encounters_to_header.py
index 5de52fbea9..375dda3ecf 100644
--- a/tools/wild_encounters/wild_encounters_to_header.py
+++ b/tools/wild_encounters/wild_encounters_to_header.py
@@ -391,35 +391,33 @@ def PrintWildMonHeadersContent():
timeCounter += 1
PrintEncounterHeaders(TabStr(2) + "},")
PrintEncounterHeaders(tabStr + "},")
-
- if labelCount + 1 == headerStructTable[group][label]["encounterTotalCount"]:
- PrintEncounterHeaders(tabStr + "{")
- PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},")
- PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},")
-
- nullCount = 0
- while nullCount < TIMES_OF_DAY_COUNT:
- if nullCount == 0:
- PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =")
- PrintEncounterHeaders(TabStr(2)+ "{")
-
- PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ")
-
- nullIndex = 0
- while nullIndex <= len(fieldData) - 1:
- if nullIndex == 0:
- PrintEncounterHeaders(TabStr(3) + "{")
-
- PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,")
-
- if nullIndex == len(fieldData) - 1:
- PrintEncounterHeaders(TabStr(3) + "},")
-
- nullIndex += 1
- nullCount += 1
- PrintEncounterHeaders(TabStr(2) + "},")
- PrintEncounterHeaders(tabStr + "},")
labelCount += 1
+ PrintEncounterHeaders(tabStr + "{")
+ PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},")
+ PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},")
+
+ nullCount = 0
+ while nullCount < TIMES_OF_DAY_COUNT:
+ if nullCount == 0:
+ PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =")
+ PrintEncounterHeaders(TabStr(2)+ "{")
+
+ PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ")
+
+ nullIndex = 0
+ while nullIndex <= len(fieldData) - 1:
+ if nullIndex == 0:
+ PrintEncounterHeaders(TabStr(3) + "{")
+
+ PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,")
+
+ if nullIndex == len(fieldData) - 1:
+ PrintEncounterHeaders(TabStr(3) + "},")
+
+ nullIndex += 1
+ nullCount += 1
+ PrintEncounterHeaders(TabStr(2) + "},")
+ PrintEncounterHeaders(tabStr + "},")
groupCount += 1
PrintEncounterHeaders("};")