diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 32a1ce262695..f8f9b8f0afbe 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.8.4 (Latest release) + - 1.8.5 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.8.4 - 1.8.3 - 1.8.2 - 1.8.1 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 3e21436c1a98..c29ace1224f8 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,10 +23,12 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.8.4 (Latest release) + - 1.8.5 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.8.4 - 1.8.3 + - 1.8.2 - 1.8.1 - 1.8.0 - 1.7.4 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index fcbf4b8884fe..c3320e67de32 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,10 +23,12 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.8.4 (Latest release) + - 1.8.5 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.8.4 - 1.8.3 + - 1.8.2 - 1.8.1 - 1.8.0 - 1.7.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index f513818cbb6e..84331cb4e39b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,48 +1,49 @@ # Pokeemerald-Expansion Changelogs ## 1.8.x -- ### [Version 1.8.4](docs/changelogs/1.8.x/1.8.4.md) - Bugfix Release 🧹 -- ### [Version 1.8.3](docs/changelogs/1.8.x/1.8.3.md) - Bugfix Release 🧹 -- ### [Version 1.8.2](docs/changelogs/1.8.x/1.8.2.md) - Bugfix Release 🧹 -- ### [Version 1.8.1](docs/changelogs/1.8.x/1.8.1.md) - HOTFIX Release 🔥 -- ### [Version 1.8.0](docs/changelogs/1.8.x/1.8.0.md) - Feature Release ✨ +- **[Version 1.8.5](docs/changelogs/1.8.x/1.8.5.md) - 🧹 Bugfix Release** +- **[Version 1.8.4](docs/changelogs/1.8.x/1.8.4.md) - 🧹 Bugfix Release** +- **[Version 1.8.3](docs/changelogs/1.8.x/1.8.3.md) - 🧹 Bugfix Release** +- **[Version 1.8.2](docs/changelogs/1.8.x/1.8.2.md) - 🧹 Bugfix Release** +- **[Version 1.8.1](docs/changelogs/1.8.x/1.8.1.md) - 🔥 HOTFIX Release** +- **[Version 1.8.0](docs/changelogs/1.8.x/1.8.0.md) - ✨ Feature Release** ## 1.7.x -- ### [Version 1.7.4](docs/changelogs/1.7.x/1.7.4.md) - Bugfix Release 🧹 -- ### [Version 1.7.3](docs/changelogs/1.7.x/1.7.3.md) - Bugfix Release 🧹 -- ### [Version 1.7.2](docs/changelogs/1.7.x/1.7.2.md) - Bugfix Release 🧹 -- ### [Version 1.7.1](docs/changelogs/1.7.x/1.7.1.md) - Bugfix Release 🧹 -- ### [Version 1.7.0](docs/changelogs/1.7.x/1.7.0.md) - Feature Release ✨ +- **[Version 1.7.4](docs/changelogs/1.7.x/1.7.4.md) - 🧹 Bugfix Release** +- **[Version 1.7.3](docs/changelogs/1.7.x/1.7.3.md) - 🧹 Bugfix Release** +- **[Version 1.7.2](docs/changelogs/1.7.x/1.7.2.md) - 🧹 Bugfix Release** +- **[Version 1.7.1](docs/changelogs/1.7.x/1.7.1.md) - 🧹 Bugfix Release** +- **[Version 1.7.0](docs/changelogs/1.7.x/1.7.0.md) - ✨ Feature Release** ## 1.6.x -- ### [Version 1.6.2](docs/changelogs/1.6.x/1.6.2.md) - Bugfix Release 🧹 -- ### [Version 1.6.1](docs/changelogs/1.6.x/1.6.1.md) - HOTFIX Release 🔥 -- ### [Version 1.6.0](docs/changelogs/1.6.x/1.6.0.md) - Feature Release ✨ +- **[Version 1.6.2](docs/changelogs/1.6.x/1.6.2.md) - 🧹 Bugfix Release** +- **[Version 1.6.1](docs/changelogs/1.6.x/1.6.1.md) - 🔥 HOTFIX Release** +- **[Version 1.6.0](docs/changelogs/1.6.x/1.6.0.md) - ✨ Feature Release** ## 1.5.x -- ### [Version 1.5.3](docs/changelogs/1.5.x/1.5.3.md) - HOTFIX Release 🔥 -- ### [Version 1.5.2](docs/changelogs/1.5.x/1.5.2.md) - Bugfix Release 🧹 -- ### [Version 1.5.1](docs/changelogs/1.5.x/1.5.1.md) - Bugfix Release 🧹 -- ### [Version 1.5.0](docs/changelogs/1.5.x/1.5.0.md) - Feature Release ✨ +- **[Version 1.5.3](docs/changelogs/1.5.x/1.5.3.md) - 🔥 HOTFIX Release** +- **[Version 1.5.2](docs/changelogs/1.5.x/1.5.2.md) - 🧹 Bugfix Release** +- **[Version 1.5.1](docs/changelogs/1.5.x/1.5.1.md) - 🧹 Bugfix Release** +- **[Version 1.5.0](docs/changelogs/1.5.x/1.5.0.md) - ✨ Feature Release** ## 1.4.x -- ### [Version 1.4.3](docs/changelogs/1.4.x/1.4.3.md) - Bugfix Release 🧹 -- ### [Version 1.4.2](docs/changelogs/1.4.x/1.4.2.md) - Bugfix Release 🧹 -- ### [Version 1.4.1](docs/changelogs/1.4.x/1.4.1.md) - HOTFIX Release 🔥 -- ### [Version 1.4.0](docs/changelogs/1.4.x/1.4.0.md) - Feature Release ✨ +- **[Version 1.4.3](docs/changelogs/1.4.x/1.4.3.md) - 🧹 Bugfix Release** +- **[Version 1.4.2](docs/changelogs/1.4.x/1.4.2.md) - 🧹 Bugfix Release** +- **[Version 1.4.1](docs/changelogs/1.4.x/1.4.1.md) - 🔥 HOTFIX Release** +- **[Version 1.4.0](docs/changelogs/1.4.x/1.4.0.md) - ✨ Feature Release** ## 1.3.x -- ### [Version 1.3.0](docs/changelogs/1.3.x/1.3.0.md) - Feature Release ✨ +- **[Version 1.3.0](docs/changelogs/1.3.x/1.3.0.md) - ✨ Feature Release** ## 1.2.x -- ### [Version 1.2.0](docs/changelogs/1.2.x/1.2.0.md) - Feature Release ✨ +- **[Version 1.2.0](docs/changelogs/1.2.x/1.2.0.md) - ✨ Feature Release** ## 1.1.x -- ### [Version 1.1.1](docs/changelogs/1.1.x/1.1.1.md) - Bugfix Release 🧹 -- ### [Version 1.1.0](docs/changelogs/1.1.x/1.1.0.md) - Feature Release ✨ +- **[Version 1.1.1](docs/changelogs/1.1.x/1.1.1.md) - 🧹 Bugfix Release** +- **[Version 1.1.0](docs/changelogs/1.1.x/1.1.0.md) - ✨ Feature Release** ## 1.0.x -- ### [Version 1.0.0](docs/changelogs/1.0.x/1.0.0.md) - Feature Release ✨ +- **[Version 1.0.0](docs/changelogs/1.0.x/1.0.0.md) - ✨ Feature Release** ## Pre-1.0.x: -- ### [Version 0.9.0](docs/changelogs/0.9.x/0.9.0.md) - Retroactive Version 🦕 +- **[Version 0.9.0](docs/changelogs/0.9.x/0.9.0.md) - 🦕 Retroactive Version** diff --git a/INSTALL.md b/INSTALL.md index ed7d5bad0897..cc6eba97efb5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -482,6 +482,13 @@ nix-shell -p pkgsCross.arm-embedded.stdenv.cc git pkg-config libpng ``` Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux). +### NixOS +Run the following command to start an interactive shell with the necessary packages: +```bash +nix-shell -p pkgsCross.arm-embedded.stdenv.cc git pkg-config libpng +``` +Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux). + ### Other distributions _(Specific instructions for other distributions would be greatly appreciated!)_ diff --git a/Makefile b/Makefile index 0428c36970dd..3bf8bf456e78 100644 --- a/Makefile +++ b/Makefile @@ -377,7 +377,7 @@ ifeq ($(NODEP),1) $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.c ifeq (,$(KEEP_TEMPS)) @echo "$(CC1) -o $@ $<" - @$(CPP) $(CPPFLAGS) $< | $(PREPROC) $< charmap.txt -i | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - + @$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - else @$(CPP) $(CPPFLAGS) $< -o $(C_BUILDDIR)/$*.i @$(PREPROC) $(C_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(C_BUILDDIR)/$*.s @@ -389,7 +389,7 @@ define C_DEP $1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2) ifeq (,$$(KEEP_TEMPS)) @echo "$$(CC1) -o $$@ $$<" - @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - + @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - else @$$(CPP) $$(CPPFLAGS) $$< -o $$(C_BUILDDIR)/$3.i @$$(PREPROC) $$(C_BUILDDIR)/$3.i charmap.txt | $$(CC1) $$(CFLAGS) -o $$(C_BUILDDIR)/$3.s @@ -404,7 +404,7 @@ ifeq ($(NODEP),1) $(GFLIB_BUILDDIR)/%.o: $(GFLIB_SUBDIR)/%.c $$(c_dep) ifeq (,$(KEEP_TEMPS)) @echo "$(CC1) -o $@ $<" - @$(CPP) $(CPPFLAGS) $< | $(PREPROC) $< charmap.txt -i | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - + @$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - else @$(CPP) $(CPPFLAGS) $< -o $(GFLIB_BUILDDIR)/$*.i @$(PREPROC) $(GFLIB_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(GFLIB_BUILDDIR)/$*.s @@ -416,7 +416,7 @@ define GFLIB_DEP $1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2) ifeq (,$$(KEEP_TEMPS)) @echo "$$(CC1) -o $$@ $$<" - @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - + @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - else @$$(CPP) $$(CPPFLAGS) $$< -o $$(GFLIB_BUILDDIR)/$3.i @$$(PREPROC) $$(GFLIB_BUILDDIR)/$3.i charmap.txt | $$(CC1) $$(CFLAGS) -o $$(GFLIB_BUILDDIR)/$3.s @@ -429,11 +429,11 @@ endif ifeq ($(NODEP),1) $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s - $(PREPROC) $< charmap.txt | $(CPP) -I include - | $(AS) $(ASFLAGS) -o $@ + $(PREPROC) $< charmap.txt | $(CPP) -I include - | $(PREPROC) -i $$< charmap.txt | $(AS) $(ASFLAGS) -o $@ else define SRC_ASM_DATA_DEP $1: $2 $$(shell $(SCANINC) -I include -I "" $2) - $$(PREPROC) $$< charmap.txt | $$(CPP) -I include - | $$(AS) $$(ASFLAGS) -o $$@ + $$(PREPROC) $$< charmap.txt | $$(CPP) -I include - | $$(PREPROC) -ie $$< charmap.txt | $$(AS) $$(ASFLAGS) -o $$@ endef $(foreach src, $(C_ASM_SRCS), $(eval $(call SRC_ASM_DATA_DEP,$(patsubst $(C_SUBDIR)/%.s,$(C_BUILDDIR)/%.o, $(src)),$(src)))) endif @@ -451,7 +451,7 @@ endif ifeq ($(NODEP),1) $(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s - $(PREPROC) $< charmap.txt | $(CPP) -I include - | $(AS) $(ASFLAGS) -o $@ + $(PREPROC) $< charmap.txt | $(CPP) -I include - | $(PREPROC) -ie $$< charmap.txt | $(AS) $(ASFLAGS) -o $@ else $(foreach src, $(REGULAR_DATA_ASM_SRCS), $(eval $(call SRC_ASM_DATA_DEP,$(patsubst $(DATA_ASM_SUBDIR)/%.s,$(DATA_ASM_BUILDDIR)/%.o, $(src)),$(src)))) endif @@ -477,7 +477,7 @@ $(DATA_SRC_SUBDIR)/pokemon/teachable_learnsets.h: $(DATA_ASM_BUILDDIR)/event_scr define TEST_DEP $1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2) @echo "$$(CC1) -o $$@ $$<" - @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - + @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - endef $(foreach src, $(TEST_SRCS), $(eval $(call TEST_DEP,$(patsubst $(TEST_SUBDIR)/%.c,$(TEST_BUILDDIR)/%.o,$(src)),$(src),$(patsubst $(TEST_SUBDIR)/%.c,%,$(src))))) diff --git a/README.md b/README.md index f10b4978a586..2e8d311c9db2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ You are currently on the master branch which should always be a working (at leas ## What feature branches are included? -- **[RHH's pokeemerald-expansion](https://github.com/rh-hideout/pokeemerald-expansion?tab=readme-ov-file#pokeemerald-expansion)** +- **[RHH's pokeemerald-expansion v1.8.5](https://github.com/rh-hideout/pokeemerald-expansion?tab=readme-ov-file#pokeemerald-expansion)** - **[Prof. Leon Dias' Fully Decapitalized Pokeemerald](https://www.pokecommunity.com/showthread.php?t=451448)** - **[Content from Pokecommunity Simple Modifications Directory Content](https://www.pokecommunity.com/showthread.php?t=416647):** IV/EV Stat screens - **[Fixes and content from the pret tutorials](https://github.com/pret/pokeemerald/wiki/Tutorials):** Map dependent Trainer Battle Music diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index d20771619473..021dd12bcea9 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1507,18 +1507,10 @@ .4byte \jumpInstr .endm - .macro jumpifrodaffected battler:req, jumpInstr:req - callnative BS_JumpIfRod - .4byte \jumpInstr - .endm - - .macro jumpifabsorbaffected battler:req, jumpInstr:req - callnative BS_JumpIfAbsorb - .4byte \jumpInstr - .endm - - .macro jumpifmotoraffected battler:req, jumpInstr:req - callnative BS_JumpIfMotor + .macro jumpifelectricabilityaffected battler:req, ability:req, jumpInstr:req + callnative BS_JumpIfElectricAbilityAffected + .byte \battler + .2byte \ability .4byte \jumpInstr .endm @@ -1647,6 +1639,12 @@ .4byte \failInstr .endm + .macro copyfoesstatincrease battler:req, failInstr:req + callnative BS_CopyFoesStatIncrease + .byte \battler + .4byte \failInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8bceedfa47ad..b349dea98548 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -432,9 +432,9 @@ BattleScript_EffectTeatime:: waitanimation setbyte gBattlerTarget, 0 BattleScript_TeatimeLoop: - jumpifrodaffected BS_TARGET, BattleScript_Teatimerod - jumpifabsorbaffected BS_TARGET, BattleScript_Teatimesorb - jumpifmotoraffected BS_TARGET, BattleScript_Teatimemotor + jumpifelectricabilityaffected BS_TARGET, ABILITY_LIGHTNING_ROD, BattleScript_Teatimerod + jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_Teatimesorb + jumpifelectricabilityaffected BS_TARGET, ABILITY_MOTOR_DRIVE, BattleScript_Teatimemotor jumpifteainvulnerable BS_TARGET, BattleScript_Teatimevul @ in semi-invulnerable state OR held item is not a Berry orword gHitMarker, HITMARKER_DISABLE_ANIMATION | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE setbyte sBERRY_OVERRIDE, TRUE @ override the requirements for eating berries @@ -758,13 +758,11 @@ BattleScript_OctolockEndTurn:: playstatchangeanimation BS_ATTACKER, BIT_DEF | BIT_SPDEF, STAT_CHANGE_NEGATIVE setstatchanger STAT_DEF, 1, TRUE statbuffchange STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_NOT_PROTECT_AFFECTED, BattleScript_OctolockTryLowerSpDef -BattleScript_OctolockTryLowerDef: printfromtable gStatDownStringIds waitmessage B_WAIT_TIME_LONG BattleScript_OctolockTryLowerSpDef: setstatchanger STAT_SPDEF, 1, TRUE statbuffchange STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_NOT_PROTECT_AFFECTED, BattleScript_OctlockTurnDmgEnd -BattleScript_OctolockTurnDmgPrintMsg: printfromtable gStatDownStringIds waitmessage B_WAIT_TIME_LONG BattleScript_OctlockTurnDmgEnd: @@ -904,7 +902,6 @@ BattleScript_EffectStuffCheeks:: jumpifnotberry BS_ATTACKER, BattleScript_ButItFailed attackanimation waitanimation -BattleScript_StuffCheeksEatBerry: setbyte sBERRY_OVERRIDE, 1 orword gHitMarker, HITMARKER_DISABLE_ANIMATION consumeberry BS_ATTACKER, TRUE @@ -1116,10 +1113,6 @@ BattleScript_StrengthSapLower: waitmessage B_WAIT_TIME_LONG goto BattleScript_StrengthSapHp @ Drain HP without lowering a stat -BattleScript_StrengthSapTryHp: - jumpiffullhp BS_ATTACKER, BattleScript_ButItFailed - attackanimation - waitanimation BattleScript_StrengthSapHp: jumpifability BS_TARGET, ABILITY_LIQUID_OOZE, BattleScript_StrengthSapManipulateDmg jumpifstatus3 BS_ATTACKER, STATUS3_HEAL_BLOCK, BattleScript_MoveEnd @@ -1250,8 +1243,6 @@ BattleScript_EffectPartingShotTrySpAtk: BattleScript_EffectPartingShotSwitch: moveendall goto BattleScript_MoveSwitch -BattleScript_PartingShotEnd: - end BattleScript_EffectPowder:: attackcanceler @@ -1454,7 +1445,6 @@ BattleScript_RototillerLoop: jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MAX_STAT_STAGE, BattleScript_RototillerCantRaiseMultipleStats BattleScript_RototillerCheckAffected: jumpifnotrototilleraffected BS_TARGET, BattleScript_RototillerNoEffect -BattleScript_RototillerAffected: setbyte sSTAT_ANIM_PLAYED, FALSE playstatchangeanimation BS_TARGET, BIT_ATK | BIT_SPATK, 0 setstatchanger STAT_ATK, 1, FALSE @@ -1519,6 +1509,7 @@ BattleScript_MoveEffectFlameBurst:: waitmessage B_WAIT_TIME_LONG savetarget copybyte gBattlerTarget, sSAVED_BATTLER + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE healthbarupdate BS_TARGET datahpupdate BS_TARGET tryfaintmon BS_TARGET @@ -1684,7 +1675,6 @@ BattleScript_EffectAutotomize:: BattleScript_AutotomizeAttackAnim:: attackanimation waitanimation -BattleScript_AutotomizeDoAnim:: setgraphicalstatchangevalues playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 BattleScript_AutotomizePrintString:: @@ -2595,7 +2585,6 @@ BattleScript_TryTailwindAbilitiesLoop_Iter: BattleScript_TryTailwindAbilitiesLoop_Increment: addbyte gBattlerTarget, 0x1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_TryTailwindAbilitiesLoop_Iter -BattleScript_TryTailwindAbilitiesLoop_Ret: restoretarget return @@ -2696,7 +2685,6 @@ BattleScript_EffectPlaceholder:: goto BattleScript_MoveEnd BattleScript_EffectHit:: -BattleScript_HitFromAtkCanceler:: attackcanceler BattleScript_HitFromAccCheck:: accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE @@ -3186,14 +3174,6 @@ BattleScript_RoarBlockedByDynamax: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectMultiHit:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - ppreduce - setmultihitcounter 0 - initmultihitstring - sethword sMULTIHIT_EFFECT, 0 BattleScript_MultiHitLoop:: jumpifhasnohp BS_ATTACKER, BattleScript_MultiHitEnd jumpifhasnohp BS_TARGET, BattleScript_MultiHitPrintStrings @@ -3606,10 +3586,9 @@ BattleScript_EffectParalyze:: jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect jumpifsubstituteblocks BattleScript_ButItFailed typecalc -BattleScript_BattleScript_EffectParalyzeNoTypeCalc: jumpifmovehadnoeffect BattleScript_ButItFailed jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed - jumpifabsorbaffected BS_TARGET, BattleScript_VoltAbsorbHeal + jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_VoltAbsorbHeal tryparalyzetype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents @@ -4533,7 +4512,6 @@ BattleScript_EffectTeleport:: .else jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_FailedFromAtkCanceler .endif -BattleScript_EffectTeleportTryToRunAway: attackcanceler attackstring ppreduce @@ -5931,6 +5909,10 @@ BattleScript_MagicRoomEnds:: waitmessage B_WAIT_TIME_LONG end2 +BattleScript_GrassyTerrainEnds:: + call BattleScript_GrassyTerrainHeals_Ret + goto BattleScript_TerrainEnds + BattleScript_TerrainEnds_Ret:: printfromtable gTerrainStringIds waitmessage B_WAIT_TIME_LONG @@ -6389,7 +6371,6 @@ BattleScript_WindPowerActivates:: setcharge BS_TARGET printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER waitmessage B_WAIT_TIME_LONG -BattleScript_WindPowerActivates_Ret: return BattleScript_ToxicDebrisActivates:: @@ -7076,7 +7057,6 @@ BattleScript_CottonDownTargetSpeedCantGoLower: BattleScript_CottonDownLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_CottonDownLoop -BattleScript_CottonDownReturn: swapattackerwithtarget copybyte gBattlerAttacker, sSAVED_BATTLER return @@ -7695,7 +7675,6 @@ BattleScript_ActivateWeatherAbilities: BattleScript_ActivateWeatherAbilities_Loop: copybyte sBATTLER, gBattlerTarget activateweatherchangeabilities BS_TARGET -BattleScript_ActivateWeatherAbilities_Increment: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_ActivateWeatherAbilities_Loop restoretarget @@ -7754,7 +7733,6 @@ BattleScript_IntimidateEffect_WaitString: BattleScript_IntimidateLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_IntimidateLoop -BattleScript_IntimidateEnd: copybyte sBATTLER, gBattlerAttacker destroyabilitypopup copybyte gBattlerTarget, sSAVED_BATTLER @@ -7820,7 +7798,6 @@ BattleScript_SupersweetSyrupEffect_WaitString: BattleScript_SupersweetSyrupLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_SupersweetSyrupLoop -BattleScript_SupersweetSyrupEnd: copybyte sBATTLER, gBattlerAttacker destroyabilitypopup copybyte gBattlerTarget, sSAVED_BATTLER @@ -7979,7 +7956,6 @@ BattleScript_ActivateTerrainSeed: removeitem BS_TARGET BattleScript_ActivateTerrainAbility: activateterrainchangeabilities BS_TARGET -BattleScript_ActivateTerrainEffects_Increment: addbyte gBattlerTarget, 0x1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_ActivateTerrainSeed restoretarget @@ -8225,29 +8201,30 @@ BattleScript_MoveUsedPsychicTerrainPrevents:: goto BattleScript_MoveEnd BattleScript_GrassyTerrainHeals:: + call BattleScript_GrassyTerrainHeals_Ret + end2 + +BattleScript_GrassyTerrainHeals_Ret:: setbyte gBattleCommunication, 0 BattleScript_GrassyTerrainLoop: copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattleCommunication, 1 checkgrassyterrainheal BS_ATTACKER, BattleScript_GrassyTerrainLoopIncrement printstring STRINGID_GRASSYTERRAINHEALS waitmessage B_WAIT_TIME_LONG -BattleScript_GrassyTerrainHpChange: orword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE healthbarupdate BS_ATTACKER datahpupdate BS_ATTACKER BattleScript_GrassyTerrainLoopIncrement:: addbyte gBattleCommunication, 1 jumpifbytenotequal gBattleCommunication, gBattlersCount, BattleScript_GrassyTerrainLoop -BattleScript_GrassyTerrainLoopEnd:: bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE jumpifword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_PERMANENT, BattleScript_GrassyTerrainHealEnd BattleScript_GrassyTerrainHealEnd: - end2 + return BattleScript_AbilityNoSpecificStatLoss:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp -BattleScript_AbilityNoSpecificStatLossPrint: printstring STRINGID_PKMNSXPREVENTSYLOSS waitmessage B_WAIT_TIME_LONG setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_FELL_EMPTY @@ -9024,16 +9001,6 @@ BattleScript_ArenaTurnBeginning:: volumeup end2 -@ Unused -BattleScript_ArenaNothingDecided:: - playse SE_DING_DONG - arenadrawreftextbox - arenajudgmentstring B_MSG_REF_NOTHING_IS_DECIDED - arenawaitmessage B_MSG_REF_NOTHING_IS_DECIDED - pause B_WAIT_TIME_LONG - arenaerasereftextbox - end2 - BattleScript_ArenaDoJudgment:: makevisible BS_PLAYER1 waitstate @@ -9133,7 +9100,6 @@ BattleScript_TotemFlaredToLife:: call BattleScript_ApplyTotemVarBoost end2 -@ remove the mirror herb, do totem loop BattleScript_MirrorHerbCopyStatChangeEnd2:: call BattleScript_MirrorHerbCopyStatChange end2 @@ -9143,16 +9109,24 @@ BattleScript_MirrorHerbCopyStatChange:: printstring STRINGID_MIRRORHERBCOPIED waitmessage B_WAIT_TIME_LONG removeitem BS_SCRIPTING - call BattleScript_TotemVar_Ret - copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe + playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 +BattleScript_MirrorHerbStartCopyStats: + copyfoesstatincrease BS_SCRIPTING, BattleScript_MirrorHerbStartReturn + statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_MirrorHerbStartReturn + goto BattleScript_MirrorHerbStartCopyStats +BattleScript_MirrorHerbStartReturn: return BattleScript_OpportunistCopyStatChange:: - call BattleScript_AbilityPopUp - printstring STRINGID_OPPORTUNISTCOPIED + call BattleScript_AbilityPopUpScripting + playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 +BattleScript_OpportunistStartCopyStats: + copyfoesstatincrease BS_SCRIPTING, BattleScript_OpportunistCopyStatChangeEnd + statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_OpportunistCopyStatChangeEnd + printfromtable gStatUpStringIds waitmessage B_WAIT_TIME_LONG - call BattleScript_TotemVar_Ret - copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe + goto BattleScript_OpportunistStartCopyStats +BattleScript_OpportunistCopyStatChangeEnd: end3 BattleScript_TotemVar:: @@ -9307,7 +9281,6 @@ BattleScript_EffectExtremeEvoboost:: BattleScript_ExtremeEvoboostAnim: attackanimation waitanimation -BattleScript_ExtremeEvoboostAtk:: setbyte sSTAT_ANIM_PLAYED, FALSE playstatchangeanimation BS_ATTACKER, BIT_ATK | BIT_DEF | BIT_SPEED | BIT_SPATK | BIT_SPDEF, 0x0 setstatchanger STAT_ATK, 2, FALSE @@ -9881,7 +9854,6 @@ BattleScript_CouldntFullyProtect:: return BattleScript_BerserkGeneRet:: -BattleScript_BerserkGeneRet_Anim: savetarget copybyte gBattlerTarget, sBATTLER statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_BerserkGeneRet_TryConfuse diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 3cee2d32aeb3..6238afb36c27 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -159,7 +159,6 @@ BattleScript_SafariBallThrow:: BattleScript_SuccessBallThrow:: setbyte sMON_CAUGHT, TRUE incrementgamestat GAME_STAT_POKEMON_CAPTURES -BattleScript_PrintCaughtMonInfo:: printstring STRINGID_GOTCHAPKMNCAUGHTPLAYER jumpifbyte CMP_NOT_EQUAL, sEXP_CATCH, TRUE, BattleScript_TryPrintCaughtMonInfo setbyte sGIVEEXP_STATE, 0 diff --git a/data/scripts/cable_club.inc b/data/scripts/cable_club.inc index e810e354a5b8..488b55be7482 100644 --- a/data/scripts/cable_club.inc +++ b/data/scripts/cable_club.inc @@ -1257,7 +1257,6 @@ EventScript_CloseMossdeepGameCornerBarrier:: return CableClub_OnResume: - special InitUnionRoom end MossdeepCity_GameCorner_1F_EventScript_InfoMan2:: diff --git a/data/scripts/pkmn_center_nurse.inc b/data/scripts/pkmn_center_nurse.inc index 64ccdfa77e55..0619d5f0a380 100644 --- a/data/scripts/pkmn_center_nurse.inc +++ b/data/scripts/pkmn_center_nurse.inc @@ -4,9 +4,7 @@ Common_EventScript_PkmnCenterNurse:: setvar VAR_0x8004, 0 specialvar VAR_RESULT, CountPlayerTrainerStars goto_if_eq VAR_RESULT, 4, EventScript_PkmnCenterNurse_GoldCard - msgbox gText_WouldYouLikeToRestYourPkmn, MSGBOX_YESNO - goto_if_eq VAR_RESULT, YES, EventScript_PkmnCenterNurse_HealPkmn - goto_if_eq VAR_RESULT, NO, EventScript_PkmnCenterNurse_Goodbye + goto EventScript_PkmnCenterNurse_HealPkmn end EventScript_PkmnCenterNurse_Goodbye:: @@ -21,7 +19,7 @@ EventScript_PkmnCenterNurse_HealPkmn:: waitmessage call EventScript_PkmnCenterNurse_TakeAndHealPkmn goto_if_unset FLAG_POKERUS_EXPLAINED, EventScript_PkmnCenterNurse_CheckPokerus - goto EventScript_PkmnCenterNurse_CheckTrainerHillAndUnionRoom + goto EventScript_PkmnCenterNurse_ReturnPkmn end EventScript_PkmnCenterNurse_IllTakeYourPkmn:: @@ -59,6 +57,7 @@ EventScript_PkmnCenterNurse_ReturnPkmn:: applymovement VAR_0x800B, Movement_PkmnCenterNurse_Bow waitmovement 0 message gText_WeHopeToSeeYouAgain + goto EventScript_PkmnCenterNurse_PlayerTurn return EventScript_PkmnCenterNurse_ReturnPkmn2:: @@ -67,6 +66,7 @@ EventScript_PkmnCenterNurse_ReturnPkmn2:: applymovement VAR_0x800B, Movement_PkmnCenterNurse_Bow waitmovement 0 message gText_WeHopeToSeeYouAgain2 + goto EventScript_PkmnCenterNurse_PlayerTurn return EventScript_PkmnCenterNurse_PlayerWaitingInUnionRoom:: @@ -83,7 +83,7 @@ EventScript_PkmnCenterNurse_PlayerWaitingInUnionRoom:: EventScript_PkmnCenterNurse_CheckPokerus:: specialvar VAR_RESULT, IsPokerusInParty goto_if_eq VAR_RESULT, TRUE, EventScript_PkmnCenterNurse_ExplainPokerus - goto_if_eq VAR_RESULT, FALSE, EventScript_PkmnCenterNurse_CheckTrainerHillAndUnionRoom + goto EventScript_PkmnCenterNurse_ReturnPkmn end EventScript_PkmnCenterNurse_ExplainPokerus:: @@ -120,3 +120,10 @@ Movement_PkmnCenterNurse_Bow: nurse_joy_bow delay_4 step_end + +EventScript_PkmnCenterNurse_PlayerTurn:: + closemessage + applymovement OBJ_EVENT_ID_PLAYER, Common_Movement_FaceDown + waitmovement 0 + release + end \ No newline at end of file diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 8e19e46c60ca..d1a93ce0be92 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -5,6 +5,7 @@ - [Setting up WSL1 (Legacy Portion)](./legacy_WSL1_INSTALL.md) - [Changelog](./CHANGELOG.md) - [1.8.x]() + - [Version 1.8.5](changelogs/1.8.x/1.8.5.md) - [Version 1.8.4](changelogs/1.8.x/1.8.4.md) - [Version 1.8.3](changelogs/1.8.x/1.8.3.md) - [Version 1.8.2](changelogs/1.8.x/1.8.2.md) diff --git a/docs/changelogs/1.8.x/1.8.5.md b/docs/changelogs/1.8.x/1.8.5.md new file mode 100644 index 000000000000..ded502ccfe69 --- /dev/null +++ b/docs/changelogs/1.8.x/1.8.5.md @@ -0,0 +1,219 @@ +# Version 1.8.5 + +```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.8.5`. +``` + +## 🌋 *IMPORTANT CHANGES AND REFACTORS* 🌋 +* Small `ABILITYEFFECT_MOVES_BLOCK` refactor for readability by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4635 +* Removed redundant `partnerPicId` arguments in macros by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4736 +* Renamed some labels from "critical hit chance" to "critical hit odds" by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4751 + * `sCriticalHitChance` => `sCriticalHitOdds`. + * `GetCritHitChance` => `GetCritHitOdds`. +* Renamed `BS_FAINTED_LINK_MULTIPLE_1/2` to `BS_FAINTED_MULTIPLE_1/2` by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4851 +* Remove Duplicate ai code from battle_ai_util.c by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4883 + * `AI_CanBeBurned` is now handled by `CanBeBurned`. + * `CanSleep` renamed to `CanBeSlept`. + * `AI_CanSleep` is now handled by `CanBeSlept` and added `defAbility` argument. + * Added `defAbility` argument for `CanBePoisoned`. + * Added `defAbility` argument for `CanBeBurned`. + * Added `defAbility` argument for `CanBeParalyzed`. +* Got rid of hardcoded stat raise anim values by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4825 +* Added parentheses to `SPECIES_EGG` macro to avoid issues by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4899 +* `IsFinalStrikeEffect` uses effect instead of move by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4917 + +## 💥 *Softlock/Crash fixes* 💥 +* Fixed Baton Pass hardlocking on Memento by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4773 +* Fixed Revive in Double Battles causing a softlock by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4784 +* Fixed Tectonic Rage causing a freeze after leveling up by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4787 +* Fixed Smack Down's animation softlocking the game after around 13 uses by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4774 +* Fixed softlock caused by using Covet/Thief on a Pokémon with Sticky Hold by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4885 + +## 🧬 General 🧬 +### Added +* Added regional form breeding mechanics by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4695 +* Convert docs/ folder to mdBook by @leo60228 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4684 + * Go [here to see the new documentation page](https://rh-hideout.github.io/pokeemerald-expansion/). Feel free to contribute via PRs! + * Cleanup by @leo60228 in: + * https://github.com/rh-hideout/pokeemerald-expansion/pull/4711 + * https://github.com/rh-hideout/pokeemerald-expansion/pull/4715 + * https://github.com/rh-hideout/pokeemerald-expansion/pull/4716 + * https://github.com/rh-hideout/pokeemerald-expansion/pull/4809 +* Added forbidden status parameter to `FORM_CHANGE_ITEM_USE` by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4738 + * Used for Shaymin's form change via Gracidea being prevented if it's frozen or frostbitten. +### Fixed +* Fixed Poké Ball inheritance into actually working by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4695 +* Daycare fixes by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4479 + * Fixed Evolved mons not copying moves as they do not have their own egg move table. + * Fixed forms of the same species not copying egg moves from each other. +* Fixed Roamer Frostbite status not carrying over between battles by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4822 +* Fixed Partners getting the wrong nature defined by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4926 +* Fixed Shop menu lists not properly displaying 6-digit item prices by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4929 + +## 🐉 Pokémon 🐉 +### Changed +* PokeCommunity sprites batch (May) by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4714 + * Pawmi (Front by KingOfThe-X-Roads, back by Vent, QC by leParagon) + * Pawmo (By vent, qc by leParagon) + * Okidogi (Front by KingOfThe-X-Roads + by leParagon, back by Lykeron + QC by Alexandrev2. 0) + * Backported Hedara's Terapagos from `upcoming`. +* Updated wild held items to Gen 9 by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4769 + * Teddiursa (Common: None_> Honey) + * Bisharp (Rare: None -> Leader's Crest) + * All Oricorio: (Rare: Honey -> Corresponding Nectar) + * Toedscool, Toedscruel: (Common: Tiny Mushroom, Rare: Big Mushroom) + * All Paradox Mon: (Rare: Booster Energy) +### Fixed +* Fixed Tandemaus being able to evolve via Rare and Exp. Candies by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4759 +* Fixed Poltchageist not always producing Counterfeit form offspring by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4812 +* Fixed missing Minior Pokédex Entry by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4858 +* Multiple Pokemon sprite fixes by @cafei-uh in https://github.com/rh-hideout/pokeemerald-expansion/pull/4887 + - Fixed Charjabug shiny + cleaned up its front sprite + - Changed Lurantis back sprite to better match its ingame model + - Fixed Bounsweet shiny not changing the color of its lower body + - Brightened the white color of Bruxish shiny + tweaked its sprites to accommodate the change +* Fixed `P_UPDATED_ABILITIES` not reverting newly gained abilities in Gens 4/6/7 by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4922 + +## ⚔️ Battle General ⚔️ ## +### Fixed +* Fixed Dynamax HP not reverting upon fainting by @WillKolada in https://github.com/rh-hideout/pokeemerald-expansion/pull/4681 +* Fixed the partner's palette from shifting when throwing a Poké Ball by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4736 +* Fixed shiny value not being updated in battle, causing the palette to reset when reloading the sprites by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4788 +* Fixed `B_VAR_STARTING_STATUS` every following terrains infinite by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4795 +* Fixed Gen 7+ confusion self hurt chance from `1 in 3` to `33%` by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4831 +* Fixed caught wild pokemon not restoring their used held item by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4803 +* Fixed AI's right slot switching-in too early by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4851 +* Ported post-KO test improvements from closed PR by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4859 +* Fixed how switch-in effects are played out after multiple faints in the same turn by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4864 +* Fixed Megas gaining abilities after fainting by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4873 + +## 🤹 Moves 🤹 +### Fixed +* Added missing `ignoreSubstitute` flags for Defog (only in Gen 4), Torment and Powder by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4623 +* Fixed Grassy Glide granting priority if it's converted to Max Move by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4659 +* Fixed Rampage moves not confusing the user if it's canceled by Protect and Type immunity by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4722 +* Fixed Tar shot the target always be to Fire regardless of existing types by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4756 +* Fixed Shell Side Arm by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4753 + * Cleanup by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4806 +* Fixed Body press using physical Defense stat changes instead of Sp. Defense stat stages in Wonder Room (still uses physical defense stat) by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4792 +* Fixed Relic Song playing the transforming animation for species other than Meloetta by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4799 +* Fixed Pursuit not being locked by Choice Items when used on an opponent switching out by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4801 +* Fixed Encore's effect lasting only 2 turns if it was used after the opponent uses the move in the same turn by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4802 +* Fixed Spectral Thief playing stat raise animation when the user has Contrary by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4824 +* Fixed Fury Cutter scaling up to 640 power by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4846 +* Fixed moves with the `ignoresTargetAbility` flag ignoring the attacker's own ability by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4855 +* Fixed incorrect move names by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4854 + * Floral Healing + * Jungle Healing + * Oceanic Operetta +* Fixed Grassy Terrain not healing on the final turn of its duration by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4903 +* Fixed Shed Tail's HP reduction rounding down instead of up by @kleeenexfeu in https://github.com/rh-hideout/pokeemerald-expansion/pull/4913 +* Fixed Bitter Malice's description, which used to imply that it didn't always lower the opponent's Attack by @kleeenexfeu in https://github.com/rh-hideout/pokeemerald-expansion/pull/4933 +* Fixed Flame Burst's adjacent damage not bypassing Substitute by @hedara90 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4939 + +## 🎭 Abilities 🎭 +### Added +* Added missing Gen 3-4 config for Lightning Rod/Storm Drain (`B_REDIRECT_ABILITY_IMMUNITY`) by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4920 +### Fixed +* Fixed Dancer issues @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4638 + * Fixed Dancer triggering if the move user flinches when it was going to use a dance move + * Fixed Dancer not triggering if a previous Dancer flinches. +* Fixed Dazzling, Queenly Majesty and Armor Tail not protecting partners from priority moves by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4635 + * Cleanup by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4647 +* Fixed Burning Bulwark's incorrect message by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4659 +* Fixed Disguise's `breakable` flag (ignorable by Mold Breaker) by @AgustinGDLV in https://github.com/rh-hideout/pokeemerald-expansion/pull/4666 +* Fixed redundant call to `IsBattlerWeatherAffected` Dry Skin's checks by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4675 +* Fixed Guard Dog blocking U-Turn/Volt Switch/Flip Turn instead of Roar/Circle Throw/Dragon Tail by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4690 +* Fixed some abilities preventing stat dropping move effects when used on self by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4696 + * Hyper Cutter (Attack) + * Big Pecks (Defense) + * Keen Eye/Illuminate (Accuracy) +* Fixed Mummy and Lingering Aroma not working at all by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4731 +* Fixed Berserk, Anger Shell, Wimp Out, Emergency Exit HP threshold (more than half HP instead of half HP and more) by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4724 +* Fixed Mycelium Might affecting every battler on the field instead of just the ability holder by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4740 +* Fixed Skill Link ignoring Gastro Acid/Neutralizing Gas by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4746 +* Fixed Own Tempo ignoring Gastro Acid/Neutralizing Gas when checking for Berserk Gene by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4746 +* Fixed Soul-Heart changing stats of wrong battler, which caused Mirror Herb to behave incorrectly by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4785 +* Fixed Poison Touch trigger chance from `1 in 3` to `30%` by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4831 +* Fixed Cursed Body trigger chance from `1 in 3` to `30%` by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4831 +* Fixed Comatose not being supressed if it's obtained by a Pokémon Transforming by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4852 +* Fixed Supersweet Syrup0s description by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4902 + +## 🧶 Items 🧶 +### Changed +* Allowed rematches to occur if I_VS_SEEKER_CHARGING isn't enabled by @fdeblasio in https://github.com/rh-hideout/pokeemerald-expansion/pull/4866 +### Fixed +* Fixed Honey altering tiles when used by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4703 +* Fixed potential undefined behaviour when using escape rope by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4705 +* Fixed Hyper Potion's price not respecting `I_PRICE` by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4737 +* Fixed Ability Shield not protecting against multiple effects by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4740 + * Mold Breaker-type abilities. + * Mycelium Might. + * Moves that bypass abilities (eg. Sunsteel Stike). + +## 🤖 Battle AI 🤖 +### Changed +* Add checks for `EFFECT_POLTERGEIST` and `EFFECT_FIRST_TURN_ONLY` to `IsDamageMoveUnusable` by @iriv24 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4693 +### Fixed +* Fixed AI going for speed control even when it's faster than the opponet by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4630 +* Fixed AI not considering `EFFECT_RAGING_BULL` similarly to `EFFECT_BRICK_BREAK` by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4682 +* Fixed AI getting incorrect calculations due to using `gCurrentMove` in `CalcCritChanceStageArgs` by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4687 +* Fixed AI not recording Battle Armor or Shell Armor by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4751 +* Fixed AI not considering guaranteed crits by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4751 +* Fixed AI not properly avoiding contact against Rocky Helmet/Rough Skin by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4779 +* Fixed AI not recognising Volt Absorb/Flash Fire by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4781 +* Fixed AI thinking that Defiant/Competitive triggers off partner's moves in doubles by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4786 +* Fixed AI using `TRUE` (Stench) when calculating if it's worth using Swagger or Flatter on its partner by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4786 +* Fixed passing hold effect instead of ability in one call of ShouldSetSandstorm by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4789 + +## 🧪 Test Runner 🧪 +### Added +* Added `FORCE_MOVE_ANIM` macro to force an animation to run in headless mode by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4774 + * Usage: `FORCE_MOVE_ANIM(TRUE)` at the beginning of the test and `FORCE_MOVE_ANIM(FALSE)` at the end. +* Added test to prevent species from having different evolution tracker methods by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4771 +* Added test for missing Pokédex Entries by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4858 +* Added missing Move Effect TODO tests - Volume B by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4682 +* Added missing tests + * Berserk by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4724 + * Clear Body by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/4685 + * Cleanup by @Pawkkie in: + * https://github.com/rh-hideout/pokeemerald-expansion/pull/4797 + * https://github.com/rh-hideout/pokeemerald-expansion/pull/4837 + * Anger Point and Moxie by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4811 + * Cursed Body, Poison Touch and Confusion status by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4831 + * Corrosion, Acrobatics, Hone Claws and Hurricane by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/4708 +* Added AI tests for avoiding using status moves on Pokémon with abilities that prevent them by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4883 +* Added Own Tempo/Mold Breaker test by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4883 +### Changed +* Multiple changes by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4682 + * Renamed some Baton Pass-related tests so that their prefix becomes Baton Pass. + * Eg. `Aqua Ring can be Baton Passed` becomes `Baton Pass passes Aqua Ring's effect`. + * Expanded Belch's test to show that it doesn't need to eat a berry before each use of the move. + * Unconfirmed interactions added to the specific files. + * Split `EFFECT_RAGING_BULL` from `EFFECT_BRICK_BREAK`'s file. + * Moved Grassy Terrain/Earthquake test to Earthquake file. +* Write new/Fix old tests for post-ko switch scenarios by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4856 +### Fixed +* Fixed issues when building tests with agbcc by @u8-Salem in https://github.com/rh-hideout/pokeemerald-expansion/pull/4689 +* Fixed incorrect `ASSUME`s in 2 test rampage tests by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4722 +* Fixed AI ability guessing in tests by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4764 +* Fixed Pokemon in tests not being male by default by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4766 +* Fixed Plasma Fists/Pixilate interaction test by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4865 +* Fixed wrong palette in player backsprite in recorded battle by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4875 + +## 📦 Pret merges 📦 +* Merge in pret 189d542 by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4832 + * Fix Steven partner battle post-battle script by @Jaizu in https://github.com/pret/pokeemerald/pull/2001 +* Pret merge (20th of June) by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4845 + - [366f8b3](https://github.com/pret/pokeemerald/commit/366f8b33a38010f47886f70e52a4fa6dd306a4c4) NixOS install instructions by @leo60228 + - [c0d630e](https://github.com/pret/pokeemerald/commit/c0d630ee6304df61a5304942386308a872aeb504) Add support for LF line endings to gbagfx by @nashalexander +* Pret Merge (5th of July) by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4915 + - [pret#2006](https://github.com/pret/pokeemerald/pull/2006/) (manually reverted) + - [pret#2009](https://github.com/pret/pokeemerald/pull/2009/) Decode unused JP text in `berry_blender.c` + - [pret#2012](https://github.com/pret/pokeemerald/pull/2012/) Berry blender's `Task_HandleOpponent` small fixes + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.4...expansion/1.8.5 + + diff --git a/docs/changelogs/template.md b/docs/changelogs/template.md index b70a44ce5466..63e9e352aad1 100644 --- a/docs/changelogs/template.md +++ b/docs/changelogs/template.md @@ -6,7 +6,7 @@ - Once you have your remote set up, run the command `git pull RHH expansion/1.Y.Z`. ``` -## 🌋 *IMPORTANT CHANGES* 🌋 +## 🌋 *IMPORTANT CHANGES AND REFACTORS* 🌋 * N/A ## 💥 *Softlock/Crash fixes* 💥 diff --git a/gflib/malloc.c b/gflib/malloc.c index 3c15dd944781..96f6aacda8a5 100644 --- a/gflib/malloc.c +++ b/gflib/malloc.c @@ -99,7 +99,7 @@ void *AllocInternal(void *heapStart, u32 size, const char *location) block = block->next; } while (block != head); - Test_ExitWithResult(TEST_RESULT_ERROR, "%s: OOM allocating %d bytes", location, size); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":L%s:%d, %s: OOM allocating %d bytes", gTestRunnerState.test->filename, SourceLine(0), location, size); #endif return NULL; } diff --git a/graphics/pokemon/ogerpon/back.png b/graphics/pokemon/ogerpon/back.png index 8ec6f0fc0d52..29ad3c09b2a7 100644 Binary files a/graphics/pokemon/ogerpon/back.png and b/graphics/pokemon/ogerpon/back.png differ diff --git a/graphics/pokemon/ogerpon/cornerstone/back.png b/graphics/pokemon/ogerpon/cornerstone/back.png index 1f37c9c1dffb..39170fd75708 100644 Binary files a/graphics/pokemon/ogerpon/cornerstone/back.png and b/graphics/pokemon/ogerpon/cornerstone/back.png differ diff --git a/graphics/pokemon/ogerpon/cornerstone/tera/back.png b/graphics/pokemon/ogerpon/cornerstone/tera/back.png new file mode 100644 index 000000000000..63c105ba0d94 Binary files /dev/null and b/graphics/pokemon/ogerpon/cornerstone/tera/back.png differ diff --git a/graphics/pokemon/ogerpon/cornerstone/tera/front.png b/graphics/pokemon/ogerpon/cornerstone/tera/front.png new file mode 100644 index 000000000000..7b93d3210a94 Binary files /dev/null and b/graphics/pokemon/ogerpon/cornerstone/tera/front.png differ diff --git a/graphics/pokemon/ogerpon/cornerstone/tera/normal.pal b/graphics/pokemon/ogerpon/cornerstone/tera/normal.pal new file mode 100644 index 000000000000..eb034d36e9fd --- /dev/null +++ b/graphics/pokemon/ogerpon/cornerstone/tera/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +224 54 230 +14 17 13 +27 29 27 +42 44 41 +46 48 45 +74 76 73 +90 96 103 +61 113 53 +139 99 57 +0 150 177 +90 171 46 +149 151 148 +0 212 248 +196 198 195 +253 218 63 +251 253 250 diff --git a/graphics/pokemon/ogerpon/cornerstone/tera/shiny.pal b/graphics/pokemon/ogerpon/cornerstone/tera/shiny.pal new file mode 100644 index 000000000000..eb034d36e9fd --- /dev/null +++ b/graphics/pokemon/ogerpon/cornerstone/tera/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +224 54 230 +14 17 13 +27 29 27 +42 44 41 +46 48 45 +74 76 73 +90 96 103 +61 113 53 +139 99 57 +0 150 177 +90 171 46 +149 151 148 +0 212 248 +196 198 195 +253 218 63 +251 253 250 diff --git a/graphics/pokemon/ogerpon/hearthflame/back.png b/graphics/pokemon/ogerpon/hearthflame/back.png index d092ec3297cf..19d665687009 100644 Binary files a/graphics/pokemon/ogerpon/hearthflame/back.png and b/graphics/pokemon/ogerpon/hearthflame/back.png differ diff --git a/graphics/pokemon/ogerpon/hearthflame/tera/back.png b/graphics/pokemon/ogerpon/hearthflame/tera/back.png new file mode 100644 index 000000000000..d98631e11e86 Binary files /dev/null and b/graphics/pokemon/ogerpon/hearthflame/tera/back.png differ diff --git a/graphics/pokemon/ogerpon/hearthflame/tera/front.png b/graphics/pokemon/ogerpon/hearthflame/tera/front.png new file mode 100644 index 000000000000..8a9e9b6a0a8a Binary files /dev/null and b/graphics/pokemon/ogerpon/hearthflame/tera/front.png differ diff --git a/graphics/pokemon/ogerpon/hearthflame/tera/normal.pal b/graphics/pokemon/ogerpon/hearthflame/tera/normal.pal new file mode 100644 index 000000000000..fbb3c82e191a --- /dev/null +++ b/graphics/pokemon/ogerpon/hearthflame/tera/normal.pal @@ -0,0 +1,18 @@ +JASC-PAL +0100 +15 +222 57 229 +16 16 16 +87 15 15 +44 43 41 +153 23 23 +75 76 76 +64 112 54 +237 59 47 +141 98 59 +91 171 49 +209 154 24 +232 157 2 +0 211 249 +255 218 70 +253 253 253 diff --git a/graphics/pokemon/ogerpon/hearthflame/tera/shiny.pal b/graphics/pokemon/ogerpon/hearthflame/tera/shiny.pal new file mode 100644 index 000000000000..fbb3c82e191a --- /dev/null +++ b/graphics/pokemon/ogerpon/hearthflame/tera/shiny.pal @@ -0,0 +1,18 @@ +JASC-PAL +0100 +15 +222 57 229 +16 16 16 +87 15 15 +44 43 41 +153 23 23 +75 76 76 +64 112 54 +237 59 47 +141 98 59 +91 171 49 +209 154 24 +232 157 2 +0 211 249 +255 218 70 +253 253 253 diff --git a/graphics/pokemon/ogerpon/tera/back.png b/graphics/pokemon/ogerpon/tera/back.png new file mode 100644 index 000000000000..47e211e5568a Binary files /dev/null and b/graphics/pokemon/ogerpon/tera/back.png differ diff --git a/graphics/pokemon/ogerpon/tera/front.png b/graphics/pokemon/ogerpon/tera/front.png new file mode 100644 index 000000000000..7131db78d5fb Binary files /dev/null and b/graphics/pokemon/ogerpon/tera/front.png differ diff --git a/graphics/pokemon/ogerpon/tera/normal.pal b/graphics/pokemon/ogerpon/tera/normal.pal new file mode 100644 index 000000000000..2409682978f7 --- /dev/null +++ b/graphics/pokemon/ogerpon/tera/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +222 57 229 +16 16 16 +44 43 41 +75 76 76 +15 106 67 +64 112 54 +141 98 59 +9 164 156 +97 167 27 +91 171 49 +84 182 101 +232 157 2 +219 156 94 +151 216 120 +255 218 70 +253 253 253 diff --git a/graphics/pokemon/ogerpon/tera/shiny.pal b/graphics/pokemon/ogerpon/tera/shiny.pal new file mode 100644 index 000000000000..2409682978f7 --- /dev/null +++ b/graphics/pokemon/ogerpon/tera/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +222 57 229 +16 16 16 +44 43 41 +75 76 76 +15 106 67 +64 112 54 +141 98 59 +9 164 156 +97 167 27 +91 171 49 +84 182 101 +232 157 2 +219 156 94 +151 216 120 +255 218 70 +253 253 253 diff --git a/graphics/pokemon/ogerpon/wellspring/back.png b/graphics/pokemon/ogerpon/wellspring/back.png index ed74528e6579..f1d5276baa7e 100644 Binary files a/graphics/pokemon/ogerpon/wellspring/back.png and b/graphics/pokemon/ogerpon/wellspring/back.png differ diff --git a/graphics/pokemon/ogerpon/wellspring/tera/back.png b/graphics/pokemon/ogerpon/wellspring/tera/back.png new file mode 100644 index 000000000000..a0a9c9834ae7 Binary files /dev/null and b/graphics/pokemon/ogerpon/wellspring/tera/back.png differ diff --git a/graphics/pokemon/ogerpon/wellspring/tera/front.png b/graphics/pokemon/ogerpon/wellspring/tera/front.png new file mode 100644 index 000000000000..714d271fb2ad Binary files /dev/null and b/graphics/pokemon/ogerpon/wellspring/tera/front.png differ diff --git a/graphics/pokemon/ogerpon/wellspring/tera/normal.pal b/graphics/pokemon/ogerpon/wellspring/tera/normal.pal new file mode 100644 index 000000000000..c4f92593b64d --- /dev/null +++ b/graphics/pokemon/ogerpon/wellspring/tera/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +224 54 230 +14 17 13 +42 44 41 +22 65 128 +74 76 73 +19 94 181 +61 113 53 +139 99 57 +0 130 229 +90 171 46 +42 214 251 +139 196 213 +204 222 254 +251 253 250 +253 218 63 +232 157 0 diff --git a/graphics/pokemon/ogerpon/wellspring/tera/shiny.pal b/graphics/pokemon/ogerpon/wellspring/tera/shiny.pal new file mode 100644 index 000000000000..c4f92593b64d --- /dev/null +++ b/graphics/pokemon/ogerpon/wellspring/tera/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +224 54 230 +14 17 13 +42 44 41 +22 65 128 +74 76 73 +19 94 181 +61 113 53 +139 99 57 +0 130 229 +90 171 46 +42 214 251 +139 196 213 +204 222 254 +251 253 250 +253 218 63 +232 157 0 diff --git a/graphics/pokemon/terapagos/stellar/back.png b/graphics/pokemon/terapagos/stellar/back.png new file mode 100644 index 000000000000..bad6bc3916d6 Binary files /dev/null and b/graphics/pokemon/terapagos/stellar/back.png differ diff --git a/graphics/pokemon/terapagos/stellar/front.png b/graphics/pokemon/terapagos/stellar/front.png new file mode 100644 index 000000000000..62bac5c46806 Binary files /dev/null and b/graphics/pokemon/terapagos/stellar/front.png differ diff --git a/graphics/pokemon/terapagos/stellar/normal.pal b/graphics/pokemon/terapagos/stellar/normal.pal new file mode 100644 index 000000000000..4cdc143ad657 --- /dev/null +++ b/graphics/pokemon/terapagos/stellar/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +224 54 230 +26 26 26 +50 56 135 +56 56 177 +62 85 198 +169 58 211 +147 78 192 +247 69 26 +65 132 243 +43 149 206 +34 177 76 +44 211 177 +245 160 163 +165 230 153 +255 213 45 +212 245 255 diff --git a/graphics/pokemon/terapagos/stellar/shiny.pal b/graphics/pokemon/terapagos/stellar/shiny.pal new file mode 100644 index 000000000000..4cdc143ad657 --- /dev/null +++ b/graphics/pokemon/terapagos/stellar/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +224 54 230 +26 26 26 +50 56 135 +56 56 177 +62 85 198 +169 58 211 +147 78 192 +247 69 26 +65 132 243 +43 149 206 +34 177 76 +44 211 177 +245 160 163 +165 230 153 +255 213 45 +212 245 255 diff --git a/include/battle.h b/include/battle.h index addf654d913c..73ae60831f53 100644 --- a/include/battle.h +++ b/include/battle.h @@ -196,10 +196,10 @@ struct SpecialStatus u8 statLowered:1; u8 lightningRodRedirected:1; u8 restoredBattlerSprite: 1; - u8 traced:1; u8 faintedHasReplacement:1; u8 focusBanded:1; u8 focusSashed:1; + u8 unused:1; // End of byte u8 sturdied:1; u8 stormDrainRedirected:1; @@ -628,7 +628,7 @@ struct BattleStruct u8 moneyMultiplierItem:1; u8 moneyMultiplierMove:1; u8 savedTurnActionNumber; - u8 switchInAbilitiesCounter; + u8 eventsBeforeFirstTurnState; u8 faintedActionsState; u8 faintedActionsBattlerId; u8 scriptPartyIdx; // for printing the nickname @@ -672,7 +672,7 @@ struct BattleStruct u16 chosenItem[MAX_BATTLERS_COUNT]; u16 choicedMove[MAX_BATTLERS_COUNT]; u16 changedItems[MAX_BATTLERS_COUNT]; - u8 switchInItemsCounter; + u8 switchInBattlerCounter; u8 arenaTurnCounter; u8 turnSideTracker; u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker] diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index bd2b8fb92036..8ea2141aee12 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -134,10 +134,8 @@ bool32 HasMagicCoatAffectedMove(u32 battler); bool32 HasSnatchAffectedMove(u32 battler); // status checks -bool32 AI_CanBeBurned(u32 battler, u32 ability); bool32 AI_CanGetFrostbite(u32 battler, u32 ability); bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability); -bool32 AI_CanSleep(u32 battler, u32 ability); bool32 IsBattlerIncapacitated(u32 battler, u32 ability); bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); bool32 ShouldPoisonSelf(u32 battler, u32 ability); diff --git a/include/battle_main.h b/include/battle_main.h index 9469e63efe6d..e313ec662955 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -23,6 +23,20 @@ struct MultiPartnerMenuPokemon #define BOUNCE_MON 0x0 #define BOUNCE_HEALTHBOX 0x1 +enum { + FIRST_TURN_EVENTS_START, + FIRST_TURN_EVENTS_OVERWORLD_WEATHER, + FIRST_TURN_EVENTS_TERRAIN, + FIRST_TURN_EVENTS_STARTING_STATUS, + FIRST_TURN_EVENTS_TOTEM_BOOST, + FIRST_TURN_EVENTS_NEUTRALIZING_GAS, + FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES, + FIRST_TURN_EVENTS_OPPORTUNIST_1, + FIRST_TURN_EVENTS_ITEM_EFFECTS, + FIRST_TURN_EVENTS_OPPORTUNIST_2, + FIRST_TURN_EVENTS_END, +}; + void CB2_InitBattle(void); void BattleMainCB2(void); void CB2_QuitRecordedBattle(void); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 1fd77449f65d..148a91783b50 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -265,6 +265,7 @@ extern const u8 BattleScript_WonderRoomEnds[]; extern const u8 BattleScript_MagicRoomEnds[]; extern const u8 BattleScript_TerrainEnds[]; extern const u8 BattleScript_TerrainEnds_Ret[]; +extern const u8 BattleScript_GrassyTerrainEnds[]; extern const u8 BattleScript_MudSportEnds[]; extern const u8 BattleScript_WaterSportEnds[]; extern const u8 BattleScript_SturdiedMsg[]; diff --git a/include/battle_util.h b/include/battle_util.h index 2866d9072f04..644a112d7dab 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -29,17 +29,15 @@ #define ABILITYEFFECT_IMMUNITY 6 #define ABILITYEFFECT_SYNCHRONIZE 7 #define ABILITYEFFECT_ATK_SYNCHRONIZE 8 -#define ABILITYEFFECT_TRACE1 9 -#define ABILITYEFFECT_TRACE2 10 -#define ABILITYEFFECT_MOVE_END_OTHER 11 -#define ABILITYEFFECT_NEUTRALIZINGGAS 12 -#define ABILITYEFFECT_FIELD_SPORT 13 // Only used if B_SPORT_TURNS >= GEN_6 -#define ABILITYEFFECT_ON_WEATHER 14 -#define ABILITYEFFECT_ON_TERRAIN 15 -#define ABILITYEFFECT_SWITCH_IN_TERRAIN 16 -#define ABILITYEFFECT_SWITCH_IN_WEATHER 17 -#define ABILITYEFFECT_OPPORTUNIST 18 -#define ABILITYEFFECT_SWITCH_IN_STATUSES 19 +#define ABILITYEFFECT_MOVE_END_OTHER 9 +#define ABILITYEFFECT_NEUTRALIZINGGAS 10 +#define ABILITYEFFECT_FIELD_SPORT 11 // Only used if B_SPORT_TURNS >= GEN_6 +#define ABILITYEFFECT_ON_WEATHER 12 +#define ABILITYEFFECT_ON_TERRAIN 13 +#define ABILITYEFFECT_SWITCH_IN_TERRAIN 14 +#define ABILITYEFFECT_SWITCH_IN_WEATHER 15 +#define ABILITYEFFECT_OPPORTUNIST 16 +#define ABILITYEFFECT_SWITCH_IN_STATUSES 17 // Special cases #define ABILITYEFFECT_MUD_SPORT 252 // Only used if B_SPORT_TURNS >= GEN_6 #define ABILITYEFFECT_WATER_SPORT 253 // Only used if B_SPORT_TURNS >= GEN_6 @@ -235,10 +233,10 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect); bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument); bool32 MoveHasChargeTurnAdditionalEffect(u32 move); -bool32 CanSleep(u32 battler); -bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget); -bool32 CanBeBurned(u32 battler); -bool32 CanBeParalyzed(u32 battler); +bool32 CanBeSlept(u32 battler, u32 ability); +bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 CanBeBurned(u32 battler, u32 ability); +bool32 CanBeParalyzed(u32 battler, u32 ability); bool32 CanBeFrozen(u32 battler); bool32 CanGetFrostbite(u32 battler); bool32 CanBeConfused(u32 battler); diff --git a/include/config/battle.h b/include/config/battle.h index 293377032098..f80cf3d2a1d7 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -135,6 +135,7 @@ #define B_WEATHER_FORMS GEN_LATEST // In Gen5+, Castform and Cherrim revert to their base form upon losing their respective ability. Cherrim needs Flower Gift to swap forms. #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. +#define B_REDIRECT_ABILITY_IMMUNITY GEN_LATEST // In Gen5+, Pokémon with Lightning Rod/Storm Drain become immune to Electric/Water-type moves and increase their Sp. Attack by 1 stage on top of the redirecting effect. #define B_LEAF_GUARD_PREVENTS_REST GEN_LATEST // In Gen5+, Leaf Guard prevents the use of Rest in harsh sunlight. #define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail. #define B_TRANSISTOR_BOOST GEN_LATEST // In Gen9+, Transistor will only boost Electric-type moves by 1.3x as opposed to 1.5x. diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 6f5b0db9efe1..8d5cde7c2e57 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -670,45 +670,44 @@ #define STRINGID_CURRENTMOVECANTSELECT 668 #define STRINGID_TARGETISBEINGSALTCURED 669 #define STRINGID_TARGETISHURTBYSALTCURE 670 -#define STRINGID_OPPORTUNISTCOPIED 671 -#define STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP 672 -#define STRINGID_SHARPSTEELFLOATS 673 -#define STRINGID_SHARPSTEELDMG 674 -#define STRINGID_PKMNBLEWAWAYSHARPSTEEL 675 -#define STRINGID_SHARPSTEELDISAPPEAREDFROMTEAM 676 -#define STRINGID_TEAMTRAPPEDWITHVINES 677 -#define STRINGID_PKMNHURTBYVINES 678 -#define STRINGID_TEAMCAUGHTINVORTEX 679 -#define STRINGID_PKMNHURTBYVORTEX 680 -#define STRINGID_TEAMSURROUNDEDBYFIRE 681 -#define STRINGID_PKMNBURNINGUP 682 -#define STRINGID_TEAMSURROUNDEDBYROCKS 683 -#define STRINGID_PKMNHURTBYROCKSTHROWN 684 -#define STRINGID_MOVEBLOCKEDBYDYNAMAX 685 -#define STRINGID_ZEROTOHEROTRANSFORMATION 686 -#define STRINGID_THETWOMOVESBECOMEONE 687 -#define STRINGID_ARAINBOWAPPEAREDONSIDE 688 -#define STRINGID_THERAINBOWDISAPPEARED 689 -#define STRINGID_WAITINGFORPARTNERSMOVE 690 -#define STRINGID_SEAOFFIREENVELOPEDSIDE 691 -#define STRINGID_HURTBYTHESEAOFFIRE 692 -#define STRINGID_THESEAOFFIREDISAPPEARED 693 -#define STRINGID_SWAMPENVELOPEDSIDE 694 -#define STRINGID_THESWAMPDISAPPEARED 695 -#define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 696 -#define STRINGID_HOSPITALITYRESTORATION 697 -#define STRINGID_ELECTROSHOTCHARGING 698 -#define STRINGID_ITEMWASUSEDUP 699 -#define STRINGID_ATTACKERLOSTITSTYPE 700 -#define STRINGID_SHEDITSTAIL 701 -#define STRINGID_CLOAKEDINAHARSHLIGHT 702 -#define STRINGID_SUPERSWEETAROMAWAFTS 703 -#define STRINGID_DIMENSIONSWERETWISTED 704 -#define STRINGID_BIZARREARENACREATED 705 -#define STRINGID_BIZARREAREACREATED 706 -#define STRINGID_TIDYINGUPCOMPLETE 707 +#define STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP 671 +#define STRINGID_SHARPSTEELFLOATS 672 +#define STRINGID_SHARPSTEELDMG 673 +#define STRINGID_PKMNBLEWAWAYSHARPSTEEL 674 +#define STRINGID_SHARPSTEELDISAPPEAREDFROMTEAM 675 +#define STRINGID_TEAMTRAPPEDWITHVINES 676 +#define STRINGID_PKMNHURTBYVINES 677 +#define STRINGID_TEAMCAUGHTINVORTEX 678 +#define STRINGID_PKMNHURTBYVORTEX 679 +#define STRINGID_TEAMSURROUNDEDBYFIRE 680 +#define STRINGID_PKMNBURNINGUP 681 +#define STRINGID_TEAMSURROUNDEDBYROCKS 682 +#define STRINGID_PKMNHURTBYROCKSTHROWN 683 +#define STRINGID_MOVEBLOCKEDBYDYNAMAX 684 +#define STRINGID_ZEROTOHEROTRANSFORMATION 685 +#define STRINGID_THETWOMOVESBECOMEONE 686 +#define STRINGID_ARAINBOWAPPEAREDONSIDE 687 +#define STRINGID_THERAINBOWDISAPPEARED 688 +#define STRINGID_WAITINGFORPARTNERSMOVE 689 +#define STRINGID_SEAOFFIREENVELOPEDSIDE 690 +#define STRINGID_HURTBYTHESEAOFFIRE 691 +#define STRINGID_THESEAOFFIREDISAPPEARED 692 +#define STRINGID_SWAMPENVELOPEDSIDE 693 +#define STRINGID_THESWAMPDISAPPEARED 694 +#define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 695 +#define STRINGID_HOSPITALITYRESTORATION 696 +#define STRINGID_ELECTROSHOTCHARGING 697 +#define STRINGID_ITEMWASUSEDUP 698 +#define STRINGID_ATTACKERLOSTITSTYPE 699 +#define STRINGID_SHEDITSTAIL 700 +#define STRINGID_CLOAKEDINAHARSHLIGHT 701 +#define STRINGID_SUPERSWEETAROMAWAFTS 702 +#define STRINGID_DIMENSIONSWERETWISTED 703 +#define STRINGID_BIZARREARENACREATED 704 +#define STRINGID_BIZARREAREACREATED 705 +#define STRINGID_TIDYINGUPCOMPLETE 706 -#define BATTLESTRINGS_COUNT 708 +#define BATTLESTRINGS_COUNT 707 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 43afdbe2122c..bf54ade9fcdf 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 -// 1.8.4 +// 1.8.5 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 8 -#define EXPANSION_VERSION_PATCH 4 +#define EXPANSION_VERSION_PATCH 5 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. diff --git a/include/constants/flags.h b/include/constants/flags.h index 8d98e3aa0491..75a00378e890 100644 --- a/include/constants/flags.h +++ b/include/constants/flags.h @@ -89,7 +89,7 @@ #define FLAG_UNUSED_0x04C 0x4C // Unused Flag #define FLAG_UNUSED_0x04D 0x4D // Unused Flag #define FLAG_UNUSED_0x04E 0x4E // Unused Flag -#define FLAG_UNUSED_0x04F 0x4F // Unused Flag +#define FLAG_SYS_SET_BATTLE_BGM 0x4F // Unused Flag // Scripts #define FLAG_HIDE_SKY_PILLAR_TOP_RAYQUAZA_STILL 0x50 diff --git a/include/constants/party_menu.h b/include/constants/party_menu.h index 1f37f3416c96..549b0d90ee82 100644 --- a/include/constants/party_menu.h +++ b/include/constants/party_menu.h @@ -81,6 +81,7 @@ #define PARTY_MSG_ALREADY_HOLDING_ONE 26 #define PARTY_MSG_WHICH_APPLIANCE 27 #define PARTY_MSG_CHOOSE_SECOND_FUSION 28 +#define PARTY_MSG_MOVE_ITEM_WHERE 29 #define PARTY_MSG_NONE 127 // IDs for DisplayPartyPokemonDescriptionText, to display a message in the party pokemon's box diff --git a/include/constants/species.h b/include/constants/species.h index d4dcc8211557..73455131247f 100644 --- a/include/constants/species.h +++ b/include/constants/species.h @@ -1629,7 +1629,7 @@ #define SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX 1522 #define SPECIES_MIMIKYU_TOTEM_BUSTED 1523 -#define SPECIES_EGG SPECIES_MIMIKYU_TOTEM_BUSTED + 1 +#define SPECIES_EGG (SPECIES_MIMIKYU_TOTEM_BUSTED + 1) #define NUM_SPECIES SPECIES_EGG diff --git a/include/field_control_avatar.h b/include/field_control_avatar.h index b259eb53c1b6..0e058eccd4d2 100644 --- a/include/field_control_avatar.h +++ b/include/field_control_avatar.h @@ -20,6 +20,7 @@ struct FieldInput bool8 input_field_1_6:1; bool8 input_field_1_7:1; u8 dpadDirection; + bool8 pressedRButton:1; }; void FieldClearPlayerInput(struct FieldInput *pStruct); diff --git a/include/strings.h b/include/strings.h index af0ee0bbba14..00b7dbf1c951 100644 --- a/include/strings.h +++ b/include/strings.h @@ -3061,4 +3061,10 @@ extern const u8 gText_BasePointsResetToZero[]; extern const u8 gText_Fertilize[]; extern const u8 gText_PlantBerry[]; +// Move Item +extern const u8 gMenuText_Move[]; +extern const u8 gText_MoveItemWhere[]; +extern const u8 gText_XsYAnd[]; +extern const u8 gText_XsYWereSwapped[]; + #endif // GUARD_STRINGS_H diff --git a/include/test/battle.h b/include/test/battle.h index 514e3bca495c..24998b8fa506 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -518,7 +518,6 @@ typedef void (*DoubleBattleTestFunction)(void *, const u32, struct BattlePokemon struct BattleTest { u8 type; - u16 sourceLine; union { SingleBattleTestFunction singles; @@ -762,10 +761,10 @@ extern struct BattleTestRunnerState *const gBattleTestRunnerState; .name = _name, \ .filename = __FILE__, \ .runner = &gBattleTestRunner, \ + .sourceLine = __LINE__, \ .data = (void *)&(const struct BattleTest) \ { \ .type = _type, \ - .sourceLine = __LINE__, \ .function = { .singles = (SingleBattleTestFunction)CAT(Test, __LINE__) }, \ .resultsSize = sizeof(struct CAT(Result, __LINE__)), \ }, \ @@ -780,10 +779,10 @@ extern struct BattleTestRunnerState *const gBattleTestRunnerState; .name = _name, \ .filename = __FILE__, \ .runner = &gBattleTestRunner, \ + .sourceLine = __LINE__, \ .data = (void *)&(const struct BattleTest) \ { \ .type = _type, \ - .sourceLine = __LINE__, \ .function = { .doubles = (DoubleBattleTestFunction)CAT(Test, __LINE__) }, \ .resultsSize = sizeof(struct CAT(Result, __LINE__)), \ }, \ @@ -1006,6 +1005,8 @@ void SendOut(u32 sourceLine, struct BattlePokemon *, u32 partyIndex); // Static const is needed to make the modern compiler put the pattern variable in the .rodata section, instead of putting it on stack(which can break the game). #define MESSAGE(pattern) do {static const u8 msg[] = _(pattern); QueueMessage(__LINE__, msg);} while (0) #define STATUS_ICON(battler, status) QueueStatus(__LINE__, battler, (struct StatusEventContext) { status }) +#define FREEZE_OR_FROSTBURN_STATUS(battler, isFrostbite) \ + (B_USE_FROSTBITE ? STATUS_ICON(battler, frostbite: isFrostbite) : STATUS_ICON(battler, freeze: isFrostbite)) enum QueueGroupType { @@ -1089,7 +1090,7 @@ void ValidateFinally(u32 sourceLine); s32 _am = Q_4_12_TO_INT(_a * _m); \ s32 _t = max(Q_4_12_TO_INT(abs(_m) + Q_4_12_ROUND), 1); \ if (abs(_am-_b) > _t) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_MUL_EQ(%d, %q, %d) failed: %d not in [%d..%d]", gTestRunnerState.test->filename, __LINE__, _a, _m, _b, _am, _b-_t, _b+_t); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_MUL_EQ(%d, %q, %d) failed: %d not in [%d..%d]", gTestRunnerState.test->filename, __LINE__, _a, _m, _b, _am, _b-_t, _b+_t); \ } while (0) #endif diff --git a/include/test/test.h b/include/test/test.h index 790563e77227..437b0b6b40db 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -33,6 +33,7 @@ struct Test const char *filename; const struct TestRunner *runner; void *data; + u16 sourceLine; }; struct TestRunnerState @@ -40,6 +41,7 @@ struct TestRunnerState u8 state; u8 exitCode; const char *skipFilename; + u32 failedAssumptionsBlockLine; const struct Test *test; u32 processCosts[MAX_PROCESSES]; @@ -73,7 +75,9 @@ void CB2_TestRunner(void); void Test_ExpectedResult(enum TestResult); void Test_ExpectLeaks(bool32); -void Test_ExitWithResult(enum TestResult, const char *fmt, ...); +void Test_ExitWithResult(enum TestResult, u32 stopLine, const char *fmt, ...); +u32 SourceLine(u32 sourceLineOffset); +u32 SourceLineOffset(u32 sourceLine); s32 MgbaPrintf_(const char *fmt, ...); @@ -84,6 +88,7 @@ s32 MgbaPrintf_(const char *fmt, ...); .name = _name, \ .filename = __FILE__, \ .runner = &gFunctionTestRunner, \ + .sourceLine = __LINE__, \ .data = (void *)CAT(Test, __LINE__), \ }; \ static void CAT(Test, __LINE__)(void) @@ -95,6 +100,7 @@ s32 MgbaPrintf_(const char *fmt, ...); .name = "ASSUMPTIONS: " __FILE__, \ .filename = __FILE__, \ .runner = &gAssumptionsRunner, \ + .sourceLine = __LINE__, \ .data = Assumptions, \ }; \ static void Assumptions(void) @@ -103,14 +109,14 @@ s32 MgbaPrintf_(const char *fmt, ...); do \ { \ if (!(c)) \ - Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, "%s:%d: ASSUME failed", gTestRunnerState.test->filename, __LINE__); \ + Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, __LINE__, ":L%s:%d: ASSUME failed", gTestRunnerState.test->filename, __LINE__); \ } while (0) #define EXPECT(c) \ do \ { \ if (!(c)) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT failed", gTestRunnerState.test->filename, __LINE__); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT failed", gTestRunnerState.test->filename, __LINE__); \ } while (0) #define EXPECT_EQ(a, b) \ @@ -118,7 +124,7 @@ s32 MgbaPrintf_(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a != _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%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) \ @@ -126,7 +132,7 @@ s32 MgbaPrintf_(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a == _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%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) \ @@ -134,7 +140,7 @@ s32 MgbaPrintf_(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a >= _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%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) \ @@ -142,7 +148,7 @@ s32 MgbaPrintf_(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a > _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%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) \ @@ -150,7 +156,7 @@ s32 MgbaPrintf_(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a <= _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%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) \ @@ -158,7 +164,7 @@ s32 MgbaPrintf_(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a < _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%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; }; @@ -195,7 +201,7 @@ static inline struct Benchmark BenchmarkStop(void) u32 a_ = (a).ticks; u32 b_ = (b).ticks; \ MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \ if (((a_ - BENCHMARK_ABS) * BENCHMARK_REL) >= (b_ * 100)) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \ } while (0) #define EXPECT_SLOWER(a, b) \ @@ -204,7 +210,7 @@ static inline struct Benchmark BenchmarkStop(void) u32 a_ = (a).ticks; u32 b_ = (b).ticks; \ MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \ if ((a_ * 100) <= ((b_ - BENCHMARK_ABS) * BENCHMARK_REL)) \ - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \ } while (0) #define KNOWN_FAILING \ @@ -218,7 +224,7 @@ static inline struct Benchmark BenchmarkStop(void) #define TO_DO \ do { \ Test_ExpectedResult(TEST_RESULT_TODO); \ - Test_ExitWithResult(TEST_RESULT_TODO, "%s:%d: EXPECT_TO_DO", gTestRunnerState.test->filename, __LINE__); \ + Test_ExitWithResult(TEST_RESULT_TODO, __LINE__, ":L%s:%d: EXPECT_TO_DO", gTestRunnerState.test->filename, __LINE__); \ } while (0) #endif diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 28823822cec7..789237264e83 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -461,12 +461,20 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) aiData->weatherHasEffect = WEATHER_HAS_EFFECT; // get/assume all battler data and simulate AI damage battlersCount = gBattlersCount; + for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { if (!IsBattlerAlive(battlerAtk)) continue; SetBattlerAiData(battlerAtk, aiData); + } + + for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) + { + if (!IsBattlerAlive(battlerAtk)) + continue; + SetBattlerAiMovesData(aiData, battlerAtk, battlersCount); } } @@ -943,12 +951,21 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) && IsNonVolatileStatusMoveEffect(moveEffect)) RETURN_SCORE_MINUS(10); break; - case ABILITY_VOLT_ABSORB: case ABILITY_LIGHTNING_ROD: + if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) + break; + // Fallthrough + case ABILITY_MOTOR_DRIVE: + case ABILITY_VOLT_ABSORB: if (moveType == TYPE_ELECTRIC) RETURN_SCORE_MINUS(20); break; case ABILITY_STORM_DRAIN: + if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) + break; + // Fallthrough + case ABILITY_WATER_ABSORB: + case ABILITY_DRY_SKIN: if (moveType == TYPE_WATER) RETURN_SCORE_MINUS(20); break; @@ -956,6 +973,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (moveType == TYPE_FIRE) RETURN_SCORE_MINUS(20); break; + case ABILITY_EARTH_EATER: + if (moveType == TYPE_GROUND) + RETURN_SCORE_MINUS(20); + break; } // def ability checks // target partner ability checks & not attacking partner @@ -1866,7 +1887,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_REST: - if (!AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk])) + if (!CanBeSlept(battlerAtk, aiData->abilities[battlerAtk])) ADJUST_SCORE(-10); //fallthrough case EFFECT_RESTORE_HP: @@ -2631,7 +2652,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_UPPER_HAND: - if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move + if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER || GetMovePriority(battlerDef, predictedMove) < 1 || GetMovePriority(battlerDef, predictedMove) > 3) // Opponent going first or not using priority move ADJUST_SCORE(-10); break; case EFFECT_PLACEHOLDER: @@ -2820,7 +2841,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } break; case ABILITY_LIGHTNING_ROD: - if (moveType == TYPE_ELECTRIC + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 + && moveType == TYPE_ELECTRIC && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_SPECIAL) && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK)) { @@ -2836,7 +2858,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } break; // handled in AI_HPAware case ABILITY_STORM_DRAIN: - if (moveType == TYPE_WATER + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 + && moveType == TYPE_WATER && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_SPECIAL) && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK)) { @@ -3456,7 +3479,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case EFFECT_REST: - if (!(AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk]))) + if (!(CanBeSlept(battlerAtk, aiData->abilities[battlerAtk]))) { break; } @@ -3936,7 +3959,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); break; case HOLD_EFFECT_FLAME_ORB: - if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) + if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) ADJUST_SCORE(DECENT_EFFECT); break; case HOLD_EFFECT_BLACK_SLUDGE: @@ -4266,7 +4289,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_ION_DELUGE: if ((aiData->abilities[battlerAtk] == ABILITY_VOLT_ABSORB || aiData->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE - || aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD) + || (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)) && gMovesInfo[predictedMove].type == TYPE_NORMAL) ADJUST_SCORE(DECENT_EFFECT); break; @@ -4325,7 +4348,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) if (predictedMove != MOVE_NONE && (aiData->abilities[battlerAtk] == ABILITY_VOLT_ABSORB || aiData->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE - || aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)) + || (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD))) { ADJUST_SCORE(DECENT_EFFECT); } diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 093db185d9c4..50aef125e07c 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -334,32 +334,29 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler, bool32 emitResult) // Create an array of possible absorb abilities so the AI considers all of them if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_FIRE) { - absorbingTypeAbilities[0] = ABILITY_FLASH_FIRE; - numAbsorbingAbilities = 1; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE; } else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_WATER) { - absorbingTypeAbilities[0] = ABILITY_WATER_ABSORB; - absorbingTypeAbilities[1] = ABILITY_STORM_DRAIN; - absorbingTypeAbilities[2] = ABILITY_DRY_SKIN; - numAbsorbingAbilities = 3; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN; + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN; } else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_ELECTRIC) { - absorbingTypeAbilities[0] = ABILITY_VOLT_ABSORB; - absorbingTypeAbilities[1] = ABILITY_MOTOR_DRIVE; - absorbingTypeAbilities[2] = ABILITY_LIGHTNING_ROD; - numAbsorbingAbilities = 3; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE; + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD; } else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_GRASS) { - absorbingTypeAbilities[0] = ABILITY_SAP_SIPPER; - numAbsorbingAbilities = 1; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER; } else if (gMovesInfo[gLastLandedMoves[battler]].type == TYPE_GROUND) { - absorbingTypeAbilities[0] = ABILITY_EARTH_EATER; - numAbsorbingAbilities = 1; + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER; } else { @@ -442,7 +439,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) { //Yawn if (gStatuses3[battler] & STATUS3_YAWN - && AI_CanSleep(battler, monAbility) + && CanBeSlept(battler, monAbility) && gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3) { switchMon = TRUE; @@ -1291,8 +1288,6 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon hazardDamage += spikesDamage; } - // Toxic Spikes - // TODO: CanBePoisoned compatibility to avoid duplicate code if ((hazardFlags & SIDE_STATUS_TOXIC_SPIKES) && (defType1 != TYPE_POISON && defType2 != TYPE_POISON && defType1 != TYPE_STEEL && defType2 != TYPE_STEEL && ability != ABILITY_IMMUNITY && ability != ABILITY_POISON_HEAL && ability != ABILITY_COMATOSE @@ -1906,12 +1901,11 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, else if (batonPassId != PARTY_SIZE) return batonPassId; - - // If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon. - else if (aceMonId != PARTY_SIZE - && (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT)) - return aceMonId; } + // If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon. + if (aceMonId != PARTY_SIZE && (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT)) + return aceMonId; + return PARTY_SIZE; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index ea50b636a695..b9770ba95091 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -378,22 +378,26 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) else battlerDefAbility = aiData->abilities[battlerDef]; - // Battler doesn't see partners Ability for some reason. - // This is a small hack to avoid the issue but should be investigated if (battlerDef == BATTLE_PARTNER(battlerAtk)) - battlerDefAbility = GetBattlerAbility(battlerDef); + battlerDefAbility = aiData->abilities[battlerDef]; switch (battlerDefAbility) { + case ABILITY_LIGHTNING_ROD: + if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) + break; + // Fallthrough case ABILITY_VOLT_ABSORB: case ABILITY_MOTOR_DRIVE: - case ABILITY_LIGHTNING_ROD: if (moveType == TYPE_ELECTRIC) return TRUE; break; + case ABILITY_STORM_DRAIN: + if (B_REDIRECT_ABILITY_IMMUNITY < GEN_5) + break; + // Fallthrough case ABILITY_WATER_ABSORB: case ABILITY_DRY_SKIN: - case ABILITY_STORM_DRAIN: if (moveType == TYPE_WATER) return TRUE; break; @@ -2624,48 +2628,18 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability) return FALSE; } -bool32 AI_CanSleep(u32 battler, u32 ability) -{ - if (ability == ABILITY_INSOMNIA - || ability == ABILITY_VITAL_SPIRIT - || ability == ABILITY_COMATOSE - || gBattleMons[battler].status1 & STATUS1_ANY - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || (gFieldStatuses & (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN)) - || IsAbilityStatusProtected(battler)) - return FALSE; - return TRUE; -} - bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanSleep(battlerDef, defAbility) + if (!CanBeSlept(battlerDef, defAbility) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep return FALSE; return TRUE; } -static bool32 AI_CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 move) -{ - u32 ability = AI_DATA->abilities[battlerDef]; - - if (!(CanPoisonType(battlerAtk, battlerDef)) - || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battlerDef].status1 & STATUS1_ANY - || ability == ABILITY_IMMUNITY - || ability == ABILITY_COMATOSE - || AI_IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL) - || gBattleMons[battlerDef].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battlerDef) - || AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - bool32 ShouldPoisonSelf(u32 battler, u32 ability) { - if (AI_CanBePoisoned(battler, battler, 0) && ( + if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)) && ( ability == ABILITY_MARVEL_SCALE || ability == ABILITY_POISON_HEAL || ability == ABILITY_QUICK_FEET @@ -2680,7 +2654,7 @@ bool32 ShouldPoisonSelf(u32 battler, u32 ability) bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBePoisoned(battlerAtk, battlerDef, move) + if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef)) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) @@ -2693,20 +2667,9 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3 return TRUE; } -static bool32 AI_CanBeParalyzed(u32 battler, u32 ability) -{ - if (ability == ABILITY_LIMBER - || ability == ABILITY_COMATOSE - || IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC) - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler)) - return FALSE; - return TRUE; -} - bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBeParalyzed(battlerDef, defAbility) + if (!CanBeParalyzed(battlerDef, defAbility) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) @@ -2740,19 +2703,6 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battler return TRUE; } -bool32 AI_CanBeBurned(u32 battler, u32 ability) -{ - if (ability == ABILITY_WATER_VEIL - || ability == ABILITY_WATER_BUBBLE - || ability == ABILITY_COMATOSE - || IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) - return FALSE; - return TRUE; -} - bool32 AI_CanGetFrostbite(u32 battler, u32 ability) { if (ability == ABILITY_MAGMA_ARMOR @@ -2767,7 +2717,7 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability) bool32 ShouldBurnSelf(u32 battler, u32 ability) { - if (AI_CanBeBurned(battler, ability) && ( + if (CanBeBurned(battler, ability) && ( ability == ABILITY_QUICK_FEET || ability == ABILITY_HEATPROOF || ability == ABILITY_MAGIC_GUARD @@ -2781,7 +2731,7 @@ bool32 ShouldBurnSelf(u32 battler, u32 ability) bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { - if (!AI_CanBeBurned(battlerDef, defAbility) + if (!CanBeBurned(battlerDef, defAbility) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove)) diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 557ed604feef..8dbde6d82133 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -791,7 +791,7 @@ void BS_SetMaxMoveEffect(void) { static const u8 sSnoozeEffects[] = {TRUE, FALSE}; if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN) - && CanSleep(gBattlerTarget) + && CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && RandomElement(RNG_G_MAX_SNOOZE, sSnoozeEffects)) // 50% chance of success { gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2); @@ -897,7 +897,7 @@ void BS_TrySetStatus1(void) switch (status1) { case STATUS1_POISON: - if (CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON; gBattleCommunication[MULTISTRING_CHOOSER] = 0; @@ -905,7 +905,7 @@ void BS_TrySetStatus1(void) } break; case STATUS1_PARALYSIS: - if (CanBeParalyzed(gBattlerTarget)) + if (CanBeParalyzed(gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS; gBattleCommunication[MULTISTRING_CHOOSER] = 3; @@ -913,7 +913,7 @@ void BS_TrySetStatus1(void) } break; case STATUS1_SLEEP: - if (CanSleep(gBattlerTarget)) + if (CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { if (B_SLEEP_TURNS >= GEN_5) gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2); diff --git a/src/battle_main.c b/src/battle_main.c index f5d6487740ab..f5714d0ce24b 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3995,8 +3995,8 @@ static void DoBattleIntro(void) } } - gBattleStruct->switchInAbilitiesCounter = 0; - gBattleStruct->switchInItemsCounter = 0; + gBattleStruct->eventsBeforeFirstTurnState = 0; + gBattleStruct->switchInBattlerCounter = 0; gBattleStruct->overworldWeatherDone = FALSE; SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. @@ -4031,34 +4031,35 @@ static void TryDoEventsBeforeFirstTurn(void) if (gBattleControllerExecFlags) return; - // Set invalid mons as absent(for example when starting a double battle with only one pokemon). - if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) + switch (gBattleStruct->eventsBeforeFirstTurnState) { - for (i = 0; i < gBattlersCount; i++) + case FIRST_TURN_EVENTS_START: + // Set invalid mons as absent(for example when starting a double battle with only one pokemon). + if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) { - struct Pokemon *party = GetBattlerParty(i); - struct Pokemon *mon = &party[gBattlerPartyIndexes[i]]; - if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG)) - gAbsentBattlerFlags |= gBitTable[i]; + for (i = 0; i < gBattlersCount; i++) + { + struct Pokemon *party = GetBattlerParty(i); + struct Pokemon *mon = &party[gBattlerPartyIndexes[i]]; + if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG)) + gAbsentBattlerFlags |= gBitTable[i]; + } } - } - // Allow for illegal abilities within tests. - #if TESTING - if (gTestRunnerEnabled && gBattleStruct->switchInAbilitiesCounter == 0) - { - for (i = 0; i < gBattlersCount; ++i) + // Allow for illegal abilities within tests. + #if TESTING + if (gTestRunnerEnabled) { - u32 side = GetBattlerSide(i); - u32 partyIndex = gBattlerPartyIndexes[i]; - if (TestRunner_Battle_GetForcedAbility(side, partyIndex)) - gBattleMons[i].ability = gBattleStruct->overwrittenAbilities[i] = TestRunner_Battle_GetForcedAbility(side, partyIndex); + for (i = 0; i < gBattlersCount; ++i) + { + u32 side = GetBattlerSide(i); + u32 partyIndex = gBattlerPartyIndexes[i]; + if (TestRunner_Battle_GetForcedAbility(side, partyIndex)) + gBattleMons[i].ability = gBattleStruct->overwrittenAbilities[i] = TestRunner_Battle_GetForcedAbility(side, partyIndex); + } } - } - #endif // TESTING + #endif // TESTING - if (gBattleStruct->switchInAbilitiesCounter == 0) - { for (i = 0; i < gBattlersCount; i++) gBattlerByTurnOrder[i] = i; for (i = 0; i < gBattlersCount - 1; i++) @@ -4069,111 +4070,135 @@ static void TryDoEventsBeforeFirstTurn(void) SwapTurnOrder(i, j); } } - } - if (!gBattleStruct->overworldWeatherDone - && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_WEATHER, 0, 0, ABILITYEFFECT_SWITCH_IN_WEATHER, 0) != 0) - { - gBattleStruct->overworldWeatherDone = TRUE; - return; - } - - if (!gBattleStruct->terrainDone && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_TERRAIN, 0, 0, ABILITYEFFECT_SWITCH_IN_TERRAIN, 0) != 0) - { - gBattleStruct->terrainDone = TRUE; - return; - } - - if (!gBattleStruct->startingStatusDone - && gBattleStruct->startingStatus - && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_STATUSES, 0, 0, ABILITYEFFECT_SWITCH_IN_STATUSES, 0) != 0) - { - gBattleStruct->startingStatusDone = TRUE; - return; - } - - // Totem boosts - for (i = 0; i < gBattlersCount; i++) - { - if (gQueuedStatBoosts[i].stats != 0 && !gProtectStructs[i].eatMirrorHerb && gProtectStructs[i].activateOpportunist == 0) + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_OVERWORLD_WEATHER: + if (!gBattleStruct->overworldWeatherDone + && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_WEATHER, 0, 0, ABILITYEFFECT_SWITCH_IN_WEATHER, 0) != 0) { - gBattlerAttacker = i; - BattleScriptExecute(BattleScript_TotemVar); + gBattleStruct->overworldWeatherDone = TRUE; return; } - } - - // Check neutralizing gas - if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0) - return; - - // Check all switch in abilities happening from the fastest mon to slowest. - while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount) - { - gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInAbilitiesCounter++]; - - if (TryPrimalReversion(gBattlerAttacker)) + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_TERRAIN: + if (!gBattleStruct->terrainDone + && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_TERRAIN, 0, 0, ABILITYEFFECT_SWITCH_IN_TERRAIN, 0) != 0) + { + gBattleStruct->terrainDone = TRUE; return; - if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0) + } + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_STARTING_STATUS: + if (!gBattleStruct->startingStatusDone + && gBattleStruct->startingStatus + && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_STATUSES, 0, 0, ABILITYEFFECT_SWITCH_IN_STATUSES, 0) != 0) + { + gBattleStruct->startingStatusDone = TRUE; return; - } - if (AbilityBattleEffects(ABILITYEFFECT_TRACE1, 0, 0, 0, 0) != 0) - return; - // Check all switch in items having effect from the fastest mon to slowest. - while (gBattleStruct->switchInItemsCounter < gBattlersCount) - { - if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter++], FALSE)) + } + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_TOTEM_BOOST: + for (i = 0; i < gBattlersCount; i++) + { + if (gQueuedStatBoosts[i].stats != 0 && !gProtectStructs[i].eatMirrorHerb && gProtectStructs[i].activateOpportunist == 0) + { + gBattlerAttacker = i; + BattleScriptExecute(BattleScript_TotemVar); + return; + } + } + memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts for Mirror Herb and Opportunist + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_NEUTRALIZING_GAS: + if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0) return; - } - - if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) - return; + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest + { + gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - { - *(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE; - gChosenActionByBattler[i] = B_ACTION_NONE; - gChosenMoveByBattler[i] = MOVE_NONE; - } - TurnValuesCleanUp(FALSE); - SpecialStatusesClear(); - *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; - BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); - gBattleMainFunc = HandleTurnActionSelectionState; - ResetSentPokesToOpponentValue(); + if (TryPrimalReversion(gBattlerAttacker)) + return; + if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0) + return; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_OPPORTUNIST_1: + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) + return; + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_ITEM_EFFECTS: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest + { + if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++], FALSE)) + return; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_OPPORTUNIST_2: + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) + return; + gBattleStruct->eventsBeforeFirstTurnState++; + break; + case FIRST_TURN_EVENTS_END: + for (i = 0; i < MAX_BATTLERS_COUNT; i++) + { + *(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE; + gChosenActionByBattler[i] = B_ACTION_NONE; + gChosenMoveByBattler[i] = MOVE_NONE; + } + TurnValuesCleanUp(FALSE); + SpecialStatusesClear(); + *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; + BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); + gBattleMainFunc = HandleTurnActionSelectionState; + ResetSentPokesToOpponentValue(); - for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++) - gBattleCommunication[i] = 0; + for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++) + gBattleCommunication[i] = 0; - for (i = 0; i < gBattlersCount; i++) - { - gBattleMons[i].status2 &= ~STATUS2_FLINCHED; - // Record party slots of player's mons that appeared in battle - if (!BattlerHasAi(i)) - gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]]; - } + for (i = 0; i < gBattlersCount; i++) + { + gBattleMons[i].status2 &= ~STATUS2_FLINCHED; + // Record party slots of player's mons that appeared in battle + if (!BattlerHasAi(i)) + gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]]; + } - *(&gBattleStruct->turnEffectsTracker) = 0; - *(&gBattleStruct->turnEffectsBattlerId) = 0; - *(&gBattleStruct->wishPerishSongState) = 0; - *(&gBattleStruct->wishPerishSongBattlerId) = 0; - gBattleScripting.moveendState = 0; - gBattleStruct->faintedActionsState = 0; - gBattleStruct->turnCountersTracker = 0; - gMoveResultFlags = 0; + *(&gBattleStruct->turnEffectsTracker) = 0; + *(&gBattleStruct->turnEffectsBattlerId) = 0; + *(&gBattleStruct->wishPerishSongState) = 0; + *(&gBattleStruct->wishPerishSongBattlerId) = 0; + gBattleScripting.moveendState = 0; + gBattleStruct->faintedActionsState = 0; + gBattleStruct->turnCountersTracker = 0; + gMoveResultFlags = 0; - memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts just to be safe + memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); + SetShellSideArmCategory(); + SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers - SetShellSideArmCategory(); - SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers + if (gBattleTypeFlags & BATTLE_TYPE_ARENA) + { + StopCryAndClearCrySongs(); + BattleScriptExecute(BattleScript_ArenaTurnBeginning); + } - if (gBattleTypeFlags & BATTLE_TYPE_ARENA) - { - StopCryAndClearCrySongs(); - BattleScriptExecute(BattleScript_ArenaTurnBeginning); + if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_BEFORE_FIRST_TURN))) + BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2); + gBattleStruct->eventsBeforeFirstTurnState = 0; + break; } - - if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_BEFORE_FIRST_TURN))) - BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2); } static void HandleEndTurn_ContinueBattle(void) diff --git a/src/battle_message.c b/src/battle_message.c index eecec7d2a4e2..f57d29f9135b 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -824,7 +824,6 @@ static const u8 sText_TeamGainedEXP[] = _("The rest of your team gained EXP.\nPo static const u8 sText_CurrentMoveCantSelect[] = _("{B_BUFF1} cannot be used!\p"); static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is being salt cured!"); static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!"); -static const u8 sText_OpportunistCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} copied its\nopponent's stat changes!"); static const u8 sText_TargetCoveredInStickyCandySyrup[] = _("{B_DEF_NAME_WITH_PREFIX} got covered\nin sticky syrup!"); static const u8 sText_PkmnTellChillingReceptionJoke[] = _("{B_ATK_NAME_WITH_PREFIX} is preparing to tell a\nchillingly bad joke!"); static const u8 sText_ZeroToHeroTransformation[] = _("{B_ATK_NAME_WITH_PREFIX} underwent a heroic\ntransformation!"); @@ -864,7 +863,6 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_ZEROTOHEROTRANSFORMATION - BATTLESTRINGS_TABLE_START] = sText_ZeroToHeroTransformation, [STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE - BATTLESTRINGS_TABLE_START] = sText_PkmnTellChillingReceptionJoke, [STRINGID_MOVEBLOCKEDBYDYNAMAX - BATTLESTRINGS_TABLE_START] = sText_MoveBlockedByDynamax, - [STRINGID_OPPORTUNISTCOPIED - BATTLESTRINGS_TABLE_START] = sText_OpportunistCopied, [STRINGID_TARGETISHURTBYSALTCURE - BATTLESTRINGS_TABLE_START] = sText_TargetIsHurtBySaltCure, [STRINGID_TARGETISBEINGSALTCURED - BATTLESTRINGS_TABLE_START] = sText_TargetIsBeingSaltCured, [STRINGID_CURRENTMOVECANTSELECT - BATTLESTRINGS_TABLE_START] = sText_CurrentMoveCantSelect, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index eaa69de10db4..22497262d88f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -357,7 +357,7 @@ static void DrawLevelUpBannerText(void); static void SpriteCB_MonIconOnLvlUpBanner(struct Sprite *sprite); static bool32 CriticalCapture(u32 odds); static void BestowItem(u32 battlerAtk, u32 battlerDef); -static bool8 IsFinalStrikeEffect(u16 move); +static bool8 IsFinalStrikeEffect(u32 moveEffect); static void TryUpdateRoundTurnOrder(void); static bool32 ChangeOrderTargetAfterAttacker(void); void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler); @@ -2907,7 +2907,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) if (i != gBattlersCount) break; - if (!CanSleep(gEffectBattler)) + if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler); @@ -2946,7 +2946,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } - if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler)) + if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -2990,7 +2990,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) break; } - if (!CanBeBurned(gEffectBattler)) + if (!CanBeBurned(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -3055,7 +3055,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)) break; - if (!CanBeParalyzed(gEffectBattler)) + if (!CanBeParalyzed(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -3093,7 +3093,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } if (gBattleMons[gEffectBattler].status1) break; - if (CanBePoisoned(gBattleScripting.battler, gEffectBattler)) + if (CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler))) { // It's redundant, because at this point we know the status1 value is 0. gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON; @@ -5514,7 +5514,7 @@ static void Cmd_moveend(void) } // Not strictly a protect effect, but works the same way else if (gProtectStructs[gBattlerTarget].beakBlastCharge - && CanBeBurned(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; @@ -6412,6 +6412,7 @@ static void Cmd_moveend(void) gBattleStruct->bouncedMoveIsUsed = FALSE; gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; + memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); gBattleScripting.moveendState++; break; case MOVEEND_COUNT: @@ -7140,8 +7141,7 @@ bool32 DoSwitchInAbilities(u32 battler) return (TryPrimalReversion(battler) || AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0) || (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0)) - || (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0)) - || AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0)); + || (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0))); } static void UpdateSentMonFlags(u32 battler) @@ -7289,6 +7289,14 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) gDisableStructs[battler].truantSwitchInHack = 0; + for (i = 0; i < gBattlersCount; i++) + { + if (i != battler + && GetBattlerAbility(i) == ABILITY_TRACE + && AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0)) + return TRUE; + } + if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE)) return TRUE; else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0)) @@ -8699,7 +8707,7 @@ static bool32 IsRototillerAffected(u32 battler) return TRUE; } -static bool32 IsElectricAbilityAffected(u32 ability) +static bool32 IsElectricAbilityAffected(u32 battler, u32 ability) { u32 moveType; @@ -8710,7 +8718,9 @@ static bool32 IsElectricAbilityAffected(u32 ability) else moveType = gMovesInfo[gCurrentMove].type; - if (moveType == TYPE_ELECTRIC && GetBattlerAbility(gBattlerTarget) == ability) + if (moveType == TYPE_ELECTRIC + && (ability != ABILITY_LIGHTNING_ROD || B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) + && GetBattlerAbility(battler) == ability) return TRUE; else return FALSE; @@ -9224,7 +9234,6 @@ static void Cmd_various(void) case VARIOUS_RESET_SWITCH_IN_ABILITY_BITS: { VARIOUS_ARGS(); - gSpecialStatuses[battler].traced = FALSE; gSpecialStatuses[battler].switchInAbilityDone = FALSE; break; } @@ -9457,7 +9466,6 @@ static void Cmd_various(void) gBattlescriptCurrInstr = cmd->nextInstr; AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0); - AbilityBattleEffects(ABILITYEFFECT_TRACE2, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0); return; } @@ -9940,18 +9948,19 @@ static void Cmd_various(void) case VARIOUS_PSYCHO_SHIFT: { VARIOUS_ARGS(const u8 *failInstr); + u32 targetAbility = GetBattlerAbility(gBattlerTarget); // Psycho shift works - if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 0; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 1; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 2; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 3; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanSleep(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 4; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanGetFrostbite(gBattlerTarget)) gBattleCommunication[MULTISTRING_CHOOSER] = 5; else { @@ -11778,27 +11787,35 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr } else { + u32 statIncrease; + if ((statValue + gBattleMons[battler].statStages[statId]) > MAX_STAT_STAGE) + statIncrease = MAX_STAT_STAGE - gBattleMons[battler].statStages[statId]; + else + statIncrease = statValue; + gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); gProtectStructs[battler].statRaised = TRUE; - // check mirror herb + // Check Mirror Herb / Opportunist for (index = 0; index < gBattlersCount; index++) { if (GetBattlerSide(index) == GetBattlerSide(battler)) continue; // Only triggers on opposing side + if (GetBattlerAbility(index) == ABILITY_OPPORTUNIST - && gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises + && gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises { gProtectStructs[index].activateOpportunist = 2; // set stats to copy - gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk - gQueuedStatBoosts[index].statChanges[statId - 1] += statValue; // cumulative in case of multiple opponent boosts } - else if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB - && gBattleMons[index].statStages[statId] < MAX_STAT_STAGE) + if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB) { gProtectStructs[index].eatMirrorHerb = 1; + } + + if (gProtectStructs[index].activateOpportunist == 2 || gProtectStructs[index].eatMirrorHerb == 1) + { gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk - gQueuedStatBoosts[index].statChanges[statId - 1] = statValue; + gQueuedStatBoosts[index].statChanges[statId - 1] += statIncrease; } } } @@ -12607,9 +12624,14 @@ static void Cmd_setsubstitute(void) CMD_ARGS(); u32 factor = gMovesInfo[gCurrentMove].effect == EFFECT_SHED_TAIL ? 2 : 4; - u32 hp = GetNonDynamaxMaxHP(gBattlerAttacker) / factor; + u32 hp; + + if (factor == 2) + hp = (GetNonDynamaxMaxHP(gBattlerAttacker)+1) / factor; // shed tail rounds up + else + hp = GetNonDynamaxMaxHP(gBattlerAttacker) / factor; // one bit value will only work for Pokémon which max hp can go to 1020(which is more than possible in games) - if (GetNonDynamaxMaxHP(gBattlerAttacker) / factor == 0) + if (hp == 0) hp = 1; if (gBattleMons[gBattlerAttacker].hp <= hp) @@ -12619,7 +12641,7 @@ static void Cmd_setsubstitute(void) } else { - gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / factor; // one bit value will only work for Pokémon which max hp can go to 1020(which is more than possible in games) + gBattleMoveDamage = hp; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; @@ -13019,7 +13041,7 @@ static void Cmd_trychoosesleeptalkmove(void) } } - unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~MOVE_LIMITATION_PP); + unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM)); if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen { gBattlescriptCurrInstr = cmd->nextInstr; @@ -15870,10 +15892,9 @@ bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler) return FALSE; } -static bool8 IsFinalStrikeEffect(u16 move) +static bool8 IsFinalStrikeEffect(u32 moveEffect) { u32 i; - u16 moveEffect = gMovesInfo[move].effect; for (i = 0; i < ARRAY_COUNT(sFinalStrikeOnlyEffects); i++) { @@ -16341,28 +16362,12 @@ void BS_JumpIfEmergencyExited(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_JumpIfRod(void) +void BS_JumpIfElectricAbilityAffected(void) { - NATIVE_ARGS(const u8 *jumpInstr); - if (IsElectricAbilityAffected(ABILITY_LIGHTNING_ROD)) - gBattlescriptCurrInstr = cmd->jumpInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; -} - -void BS_JumpIfAbsorb(void) -{ - NATIVE_ARGS(const u8 *jumpInstr); - if (IsElectricAbilityAffected(ABILITY_VOLT_ABSORB)) - gBattlescriptCurrInstr = cmd->jumpInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; -} + NATIVE_ARGS(u8 battler, u16 ability, const u8 *jumpInstr); + u32 battler = GetBattlerForBattleScript(cmd->battler); -void BS_JumpIfMotor(void) -{ - NATIVE_ARGS(const u8 *jumpInstr); - if (IsElectricAbilityAffected(ABILITY_MOTOR_DRIVE)) + if (IsElectricAbilityAffected(battler, cmd->ability)) gBattlescriptCurrInstr = cmd->jumpInstr; else gBattlescriptCurrInstr = cmd->nextInstr; @@ -17009,3 +17014,38 @@ void BS_TryQuash(void) } gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_CopyFoesStatIncrease(void) +{ + NATIVE_ARGS(u8 battler, const u8 *jumpInstr); + u32 stat = 0; + u32 battler = GetBattlerForBattleScript(cmd->battler); + + if (gQueuedStatBoosts[battler].stats == 0) + { + for (stat = 0; stat < (NUM_BATTLE_STATS - 1); stat++) + { + if (gQueuedStatBoosts[battler].statChanges[stat] != 0) + gQueuedStatBoosts[battler].stats |= (1 << stat); + } + gBattlescriptCurrInstr = cmd->jumpInstr; + return; + } + + for (stat = 0; stat < (NUM_BATTLE_STATS - 1); stat++) + { + if (gQueuedStatBoosts[battler].stats & (1 << stat)) + { + if (gQueuedStatBoosts[battler].statChanges[stat] <= -1) + SET_STATCHANGER(stat + 1, abs(gQueuedStatBoosts[battler].statChanges[stat]), TRUE); + else + SET_STATCHANGER(stat + 1, gQueuedStatBoosts[battler].statChanges[stat], FALSE); + + gQueuedStatBoosts[battler].stats &= ~(1 << stat); + gBattlerTarget = battler; + gBattlescriptCurrInstr = cmd->nextInstr; + return; + } + } + gBattlescriptCurrInstr = cmd->jumpInstr; +} diff --git a/src/battle_tower.c b/src/battle_tower.c index 266d7258b349..79c9de5c0458 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -2060,7 +2060,7 @@ void DoSpecialTrainerBattle(void) BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_SECRET_BASE)); break; case SPECIAL_BATTLE_EREADER: - #if FREE_BATTLE_TOWER_E_READER == FALSE + #if FREE_BATTLE_TOWER_E_READER == FALSE ZeroEnemyPartyMons(); for (i = 0; i < (int)ARRAY_COUNT(gSaveBlock2Ptr->frontier.ereaderTrainer.party); i++) CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i]); @@ -3061,9 +3061,7 @@ static void FillPartnerParty(u16 trainerId) personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); else if (partyData[i].gender == TRAINER_MON_FEMALE) personality = (personality & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); - if (partyData[i].nature != 0) - ModifyPersonalityForNature(&personality, partyData[i].nature - 1); - + ModifyPersonalityForNature(&personality, partyData[i].nature); CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, personality, OT_ID_PRESET, otID); j = partyData[i].isShiny; SetMonData(&gPlayerParty[i + 3], MON_DATA_IS_SHINY, &j); diff --git a/src/battle_util.c b/src/battle_util.c index ffa81eb2a75f..8cabba6e0222 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1716,7 +1716,10 @@ static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) gFieldStatuses &= ~terrainFlag; TryToRevertMimicryAndFlags(); gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; - BattleScriptExecute(BattleScript_TerrainEnds); + if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) + BattleScriptExecute(BattleScript_GrassyTerrainEnds); + else + BattleScriptExecute(BattleScript_TerrainEnds); return TRUE; } } @@ -4244,6 +4247,50 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gBattleScripting.battler = battler; switch (gLastUsedAbility) { + case ABILITY_TRACE: + { + u32 chosenTarget; + u32 target1; + u32 target2; + + if (gSpecialStatuses[battler].switchInAbilityDone) + break; + if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_TRACED) + break; + + side = (BATTLE_OPPOSITE(GetBattlerPosition(battler))) & BIT_SIDE; + target1 = GetBattlerAtPosition(side); + target2 = GetBattlerAtPosition(side + BIT_FLANK); + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0 + && !gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) + chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++; + else if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) + chosenTarget = target1, effect++; + else if (!gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) + chosenTarget = target2, effect++; + } + else + { + if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) + chosenTarget = target1, effect++; + } + + if (effect != 0) + { + BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); + gBattleResources->flags->flags[battler] &= ~RESOURCE_FLAG_TRACED; + gBattleStruct->tracedAbility[battler] = gLastUsedAbility = gBattleMons[chosenTarget].ability; + RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability + battler = gBattlerAbility = gBattleScripting.battler = battler; + + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget]) + PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) + } + } + break; case ABILITY_IMPOSTER: if (IsBattlerAlive(BATTLE_OPPOSITE(battler)) && !(gBattleMons[BATTLE_OPPOSITE(battler)].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE)) @@ -4581,13 +4628,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - case ABILITY_TRACE: - if (!(gSpecialStatuses[battler].traced)) - { - gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_TRACED; - gSpecialStatuses[battler].traced = TRUE; - } - break; case ABILITY_CLOUD_NINE: case ABILITY_AIR_LOCK: if (!gSpecialStatuses[battler].switchInAbilityDone) @@ -5077,11 +5117,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect = 2, statId = STAT_SPEED; break; case ABILITY_LIGHTNING_ROD: - if (moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_ELECTRIC && gMovesInfo[move].target != MOVE_TARGET_ALL_BATTLERS) effect = 2, statId = STAT_SPATK; break; case ABILITY_STORM_DRAIN: - if (moveType == TYPE_WATER) + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5 && moveType == TYPE_WATER) effect = 2, statId = STAT_SPATK; break; case ABILITY_SAP_SIPPER: @@ -5455,8 +5495,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_EFFECT_SPORE: + { + u32 ability = GetBattlerAbility(gBattlerAttacker); if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_OVERCOAT + && ability != ABILITY_OVERCOAT && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) { i = Random() % 3; @@ -5469,7 +5511,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanSleep(gBattlerAttacker) + && CanBeSlept(gBattlerAttacker, ability) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) @@ -5482,6 +5524,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } } + } break; POISON_POINT: case ABILITY_POISON_POINT: @@ -5489,7 +5532,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanBePoisoned(gBattlerTarget, gBattlerAttacker) + && CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && RandomWeighted(RNG_POISON_POINT, 2, 1)) @@ -5508,7 +5551,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanBeParalyzed(gBattlerAttacker) + && CanBeParalyzed(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && RandomWeighted(RNG_STATIC, 2, 1)) @@ -5528,7 +5571,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED - && CanBeBurned(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && RandomWeighted(RNG_FLAME_BODY, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; @@ -5728,7 +5771,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IS_MOVE_PHYSICAL(gCurrentMove) && TARGET_TURN_DAMAGED - && (gSideTimers[gBattlerAttacker].toxicSpikesAmount != 2)) + && (gSideTimers[GetBattlerSide(gBattlerAttacker)].toxicSpikesAmount != 2)) { SWAP(gBattlerAttacker, gBattlerTarget, i); BattleScriptPushCursor(); @@ -5745,7 +5788,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg - && CanBePoisoned(gBattlerAttacker, gBattlerTarget) + && CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && TARGET_TURN_DAMAGED // Need to actually hit the target @@ -5831,9 +5874,40 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_OPPORTUNIST: if (gProtectStructs[battler].activateOpportunist == 2) { - gBattleScripting.savedBattler = gBattlerAttacker; - gBattleScripting.battler = gBattlerAttacker = gBattlerAbility = battler; + bool32 statBuffMoreThan1 = FALSE; + bool32 handleSpeedAnimLater = FALSE; + gBattleScripting.animArg1 = 0; + gBattleScripting.battler = battler; gProtectStructs[battler].activateOpportunist--; + + for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) + { + if ((gQueuedStatBoosts[battler].stats & (1 << i)) == 0) + continue; + + if (i == STAT_SPEED) + { + handleSpeedAnimLater = TRUE; + continue; + } + + if (!statBuffMoreThan1) + statBuffMoreThan1 = ((gQueuedStatBoosts[battler].stats & (1 << i)) > 1); + + if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats + gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2); + else + gBattleScripting.animArg1 = GET_STAT_BUFF_ID((i + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2); + + } + if (handleSpeedAnimLater) + { + if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats + gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2); + else + gBattleScripting.animArg1 = GET_STAT_BUFF_ID((STAT_SPEED + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2); + } + BattleScriptPushCursorAndCallback(BattleScript_OpportunistCopyStatChange); effect = 1; } @@ -5975,48 +6049,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } } break; - case ABILITYEFFECT_TRACE1: - case ABILITYEFFECT_TRACE2: - for (i = 0; i < gBattlersCount; i++) - { - if (gBattleMons[i].ability == ABILITY_TRACE && (gBattleResources->flags->flags[i] & RESOURCE_FLAG_TRACED)) - { - u32 chosenTarget; - u32 side = (BATTLE_OPPOSITE(GetBattlerPosition(i))) & BIT_SIDE; // side of the opposing Pokémon - u32 target1 = GetBattlerAtPosition(side); - u32 target2 = GetBattlerAtPosition(side + BIT_FLANK); - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - { - if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0 - && !gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) - chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++; - else if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) - chosenTarget = target1, effect++; - else if (!gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) - chosenTarget = target2, effect++; - } - else - { - if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) - chosenTarget = target1, effect++; - } - - if (effect != 0) - { - BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); - gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_TRACED; - gBattleStruct->tracedAbility[i] = gLastUsedAbility = gBattleMons[chosenTarget].ability; - RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability - battler = gBattlerAbility = gBattleScripting.battler = i; - - PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget]) - PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) - break; - } - } - } - break; case ABILITYEFFECT_NEUTRALIZINGGAS: // Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards for (i = 0; i < gBattlersCount; i++) @@ -6327,83 +6360,78 @@ bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag) return IsBattlerGrounded(battler); } -bool32 CanSleep(u32 battler) +bool32 CanBeSlept(u32 battler, u32 ability) { - u16 ability = GetBattlerAbility(battler); if (ability == ABILITY_INSOMNIA - || ability == ABILITY_VITAL_SPIRIT - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityOnSide(battler, ABILITY_SWEET_VEIL) - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) + || ability == ABILITY_VITAL_SPIRIT + || ability == ABILITY_COMATOSE + || ability == ABILITY_PURIFYING_SALT + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battler].status1 & STATUS1_ANY + || IsAbilityOnSide(battler, ABILITY_SWEET_VEIL) + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget) +bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - u16 ability = GetBattlerAbility(battlerTarget); + if (!(CanPoisonType(battlerAtk, battlerDef)) + || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battlerDef].status1 & STATUS1_ANY + || defAbility == ABILITY_IMMUNITY + || defAbility == ABILITY_COMATOSE + || defAbility == ABILITY_PURIFYING_SALT + || IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL) + || IsAbilityStatusProtected(battlerDef) + || IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} - if (!(CanPoisonType(battlerAttacker, battlerTarget)) - || gSideStatuses[GetBattlerSide(battlerTarget)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battlerTarget].status1 & STATUS1_ANY - || ability == ABILITY_IMMUNITY +bool32 CanBeBurned(u32 battler, u32 ability) +{ + if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battler].status1 & STATUS1_ANY + || ability == ABILITY_WATER_VEIL + || ability == ABILITY_WATER_BUBBLE || ability == ABILITY_COMATOSE + || ability == ABILITY_THERMAL_EXCHANGE || ability == ABILITY_PURIFYING_SALT - || IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL) - || IsAbilityStatusProtected(battlerTarget) - || IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN)) + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBeBurned(u32 battler) +bool32 CanBeParalyzed(u32 battler, u32 ability) { - u16 ability = GetBattlerAbility(battler); - if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) + if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)) || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battler].status1 & STATUS1_ANY - || ability == ABILITY_WATER_VEIL - || ability == ABILITY_WATER_BUBBLE + || ability == ABILITY_LIMBER || ability == ABILITY_COMATOSE - || ability == ABILITY_THERMAL_EXCHANGE || ability == ABILITY_PURIFYING_SALT + || gBattleMons[battler].status1 & STATUS1_ANY || IsAbilityStatusProtected(battler) || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBeParalyzed(u32 battler) -{ - u16 ability = GetBattlerAbility(battler); - if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || ability == ABILITY_LIMBER - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - bool32 CanBeFrozen(u32 battler) { u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE) - || IsBattlerWeatherAffected(battler, B_WEATHER_SUN) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || ability == ABILITY_MAGMA_ARMOR - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + || IsBattlerWeatherAffected(battler, B_WEATHER_SUN) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || ability == ABILITY_MAGMA_ARMOR + || ability == ABILITY_COMATOSE + || ability == ABILITY_PURIFYING_SALT + || gBattleMons[battler].status1 & STATUS1_ANY + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } @@ -6426,8 +6454,8 @@ bool32 CanGetFrostbite(u32 battler) bool32 CanBeConfused(u32 battler) { if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO - || gBattleMons[battler].status2 & STATUS2_CONFUSION - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + || gBattleMons[battler].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } @@ -6818,10 +6846,43 @@ static u8 TryConsumeMirrorHerb(u32 battler, bool32 execute) if (gProtectStructs[battler].eatMirrorHerb) { + u32 i; + bool32 statBuffMoreThan1 = FALSE; + bool32 handleSpeedAnimLater = FALSE; + gBattleScripting.animArg1 = 0; + gLastUsedItem = gBattleMons[battler].item; - gBattleScripting.savedBattler = gBattlerAttacker; - gBattleScripting.battler = gBattlerAttacker = battler; + gBattleScripting.battler = battler; gProtectStructs[battler].eatMirrorHerb = 0; + + for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) + { + if ((gQueuedStatBoosts[battler].stats & (1 << i)) == 0) + continue; + + if (i == STAT_SPEED) + { + handleSpeedAnimLater = TRUE; + continue; + } + + if (!statBuffMoreThan1) + statBuffMoreThan1 = ((gQueuedStatBoosts[battler].stats & (1 << i)) > 1); + + if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats + gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2); + else + gBattleScripting.animArg1 = GET_STAT_BUFF_ID((i + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2); + + } + if (handleSpeedAnimLater) + { + if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats + gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2); + else + gBattleScripting.animArg1 = GET_STAT_BUFF_ID((STAT_SPEED + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2); + } + if (execute) { BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2); @@ -7348,6 +7409,9 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet); effect = ITEM_STATS_CHANGE; break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(battler, TRUE); + break; } if (effect != 0) { @@ -7851,7 +7915,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_JabocaRowapBerryActivates; PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - RecordItemEffectBattle(battler, HOLD_EFFECT_ROCKY_HELMET); + RecordItemEffectBattle(battler, HOLD_EFFECT_JABOCA_BERRY); } break; case HOLD_EFFECT_ROWAP_BERRY: // consume and damage attacker if used special move @@ -7871,7 +7935,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_JabocaRowapBerryActivates; PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - RecordItemEffectBattle(battler, HOLD_EFFECT_ROCKY_HELMET); + RecordItemEffectBattle(battler, HOLD_EFFECT_ROWAP_BERRY); } break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move @@ -7907,7 +7971,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) switch (battlerHoldEffect) { case HOLD_EFFECT_TOXIC_ORB: - if (CanBePoisoned(battler, battler)) + if (CanBePoisoned(battler, battler, GetBattlerAbility(battler))) { effect = ITEM_STATUS_CHANGE; gBattleMons[battler].status1 = STATUS1_TOXIC_POISON; @@ -7916,7 +7980,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } break; case HOLD_EFFECT_FLAME_ORB: - if (CanBeBurned(battler)) + if (CanBeBurned(battler, battlerAbility)) { effect = ITEM_STATUS_CHANGE; gBattleMons[battler].status1 = STATUS1_BURN; @@ -8185,7 +8249,7 @@ u8 IsMonDisobedient(void) obedienceLevel = levelReferenced - obedienceLevel; calc = (Random() & 255); - if (calc < obedienceLevel && CanSleep(gBattlerAttacker)) + if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) { // try putting asleep int i; diff --git a/src/berry_blender.c b/src/berry_blender.c index 3524ad50d214..0af3fd3ba646 100644 --- a/src/berry_blender.c +++ b/src/berry_blender.c @@ -916,14 +916,14 @@ static const u8 sBlackPokeblockFlavorFlags[] = { (1 << FLAVOR_SOUR) | (1 << FLAVOR_SWEET) | (1 << FLAVOR_SPICY), }; -static const u8 sUnused[] = -{ - 0xfe, 0x02, 0x02, 0xce, 0xd0, 0x37, 0x44, 0x07, 0x1f, 0x0c, 0x10, - 0x00, 0xff, 0xfe, 0x91, 0x72, 0xce, 0xd0, 0x37, 0x44, 0x07, 0x1f, - 0x0c, 0x10, 0x00, 0xff, 0x06, 0x27, 0x02, 0xff, 0x00, 0x0c, 0x48, - 0x02, 0xff, 0x00, 0x01, 0x1f, 0x02, 0xff, 0x00, 0x16, 0x37, 0x02, - 0xff, 0x00, 0x0d, 0x50, 0x4b, 0x02, 0xff, 0x06, 0x06, 0x06, 0x06, - 0x05, 0x03, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x02 +static const u8 sJPText_GoodTvReady[] = _("\nいいTVができました "); // Unused +static const u8 sJPText_BadTvReady[] = _("\nダメTVができました "); // Unused +static const u8 sJPText_Flavors[][5] = {_("からい"), _("しぶい"), _("あまい"), _("にがい"), _("すっぱい")}; // Unused + +static const u8 sUnused[] = { + 6, 6, 6, 6, 5, + 3, 3, 3, 2, 2, + 3, 3, 3, 3, 2 }; static const struct WindowTemplate sBlenderRecordWindowTemplate = @@ -1908,7 +1908,7 @@ static void Task_HandleOpponent1(u8 taskId) static void Task_HandleOpponent2(u8 taskId) { u32 var1 = (sBerryBlender->arrowPos + 0x1800) & 0xFFFF; - u32 arrowId = sBerryBlender->playerIdToArrowId[2] & 0xFF; + u8 arrowId = sBerryBlender->playerIdToArrowId[2]; if ((var1 >> 8) > sArrowHitRangeStart[arrowId] + 20 && (var1 >> 8) < sArrowHitRangeStart[arrowId] + 40) { if (!gTasks[taskId].tDidInput) @@ -1925,11 +1925,9 @@ static void Task_HandleOpponent2(u8 taskId) } else { - u8 value; if (rand > 65) gRecvCmds[2][BLENDER_COMM_SCORE] = LINKCMD_BLENDER_SCORE_BEST; - value = rand - 41; - if (value < 25) + if (rand > 40 && rand <= 65) gRecvCmds[2][BLENDER_COMM_SCORE] = LINKCMD_BLENDER_SCORE_GOOD; if (rand < 10) CreateOpponentMissTask(2, 5); @@ -1953,7 +1951,7 @@ static void Task_HandleOpponent2(u8 taskId) static void Task_HandleOpponent3(u8 taskId) { u32 var1 = (sBerryBlender->arrowPos + 0x1800) & 0xFFFF; - u32 arrowId = sBerryBlender->playerIdToArrowId[3] & 0xFF; + u8 arrowId = sBerryBlender->playerIdToArrowId[3]; if ((var1 >> 8) > sArrowHitRangeStart[arrowId] + 20 && (var1 >> 8) < sArrowHitRangeStart[arrowId] + 40) { if (gTasks[taskId].data[0] == 0) @@ -1971,16 +1969,9 @@ static void Task_HandleOpponent3(u8 taskId) else { if (rand > 60) - { gRecvCmds[3][BLENDER_COMM_SCORE] = LINKCMD_BLENDER_SCORE_BEST; - } - else - { - s8 value = rand - 56; // makes me wonder what the original code was - u8 value2 = value; - if (value2 < 5) - gRecvCmds[3][BLENDER_COMM_SCORE] = LINKCMD_BLENDER_SCORE_GOOD; - } + else if (rand > 55 && rand <= 60) + gRecvCmds[3][BLENDER_COMM_SCORE] = LINKCMD_BLENDER_SCORE_GOOD; if (rand < 5) CreateOpponentMissTask(3, 5); } diff --git a/src/data/abilities.h b/src/data/abilities.h index 8b81057b4ff4..ccb27ffe35ff 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -881,7 +881,11 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = [ABILITY_SNOW_WARNING] = { .name = _("Snow Warning"), - .description = COMPOUND_STRING("Summons a Hailstorm."), + #if B_SNOW_WARNING >= GEN_9 + .description = COMPOUND_STRING("Summons snow in battle."), + #else + .description = COMPOUND_STRING("Summons hail in battle."), + #endif .aiRating = 8, }, @@ -2548,7 +2552,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = #else .name = _("SuprswtSyrup"), #endif - .description = COMPOUND_STRING("Lowers the foe's Speed."), + .description = COMPOUND_STRING("Lowers the foe's Evasion."), .aiRating = 5, }, diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index aacb27d06884..fe7078f8e19c 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -12960,25 +12960,25 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar const u32 gMonShinyPalette_OgerponCornerstoneMask[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/shiny.gbapal.lz"); const u8 gMonIcon_OgerponCornerstoneMask[] = INCBIN_U8("graphics/pokemon/ogerpon/cornerstone/icon.4bpp"); - // const u32 gMonFrontPic_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/front.4bpp.lz"); - // const u32 gMonPalette_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/normal.gbapal.lz"); - // const u32 gMonBackPic_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/back.4bpp.lz"); - // const u32 gMonShinyPalette_OgerponTealMaskTera[]] = INCBIN_U32("graphics/pokemon/ogerpon/tera/shiny.gbapal.lz"); - - // const u32 gMonFrontPic_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/front.4bpp.lz"); - // const u32 gMonPalette_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/normal.gbapal.lz"); - // const u32 gMonBackPic_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/back.4bpp.lz"); - // const u32 gMonShinyPalette_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/shiny.gbapal.lz"); - - // const u32 gMonFrontPic_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/front.4bpp.lz"); - // const u32 gMonPalette_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/normal.gbapal.lz"); - // const u32 gMonBackPic_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/back.4bpp.lz"); - // const u32 gMonShinyPalette_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/shiny.gbapal.lz"); - - // const u32 gMonFrontPic_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/front.4bpp.lz"); - // const u32 gMonPalette_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/normal.gbapal.lz"); - // const u32 gMonBackPic_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/back.4bpp.lz"); - // const u32 gMonShinyPalette_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/shiny.gbapal.lz"); + const u32 gMonFrontPic_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/front.4bpp.lz"); + const u32 gMonPalette_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/normal.gbapal.lz"); + const u32 gMonBackPic_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/back.4bpp.lz"); + const u32 gMonShinyPalette_OgerponTealMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/tera/shiny.gbapal.lz"); + + const u32 gMonFrontPic_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/front.4bpp.lz"); + const u32 gMonPalette_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/normal.gbapal.lz"); + const u32 gMonBackPic_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/back.4bpp.lz"); + const u32 gMonShinyPalette_OgerponWellspringMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/wellspring/tera/shiny.gbapal.lz"); + + const u32 gMonFrontPic_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/front.4bpp.lz"); + const u32 gMonPalette_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/normal.gbapal.lz"); + const u32 gMonBackPic_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/back.4bpp.lz"); + const u32 gMonShinyPalette_OgerponHearthflameMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/hearthflame/tera/shiny.gbapal.lz"); + + const u32 gMonFrontPic_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/front.4bpp.lz"); + const u32 gMonPalette_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/normal.gbapal.lz"); + const u32 gMonBackPic_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/back.4bpp.lz"); + const u32 gMonShinyPalette_OgerponCornerstoneMaskTera[] = INCBIN_U32("graphics/pokemon/ogerpon/cornerstone/tera/shiny.gbapal.lz"); #endif //P_FAMILY_OGERPON #if P_FAMILY_GOUGING_FIRE @@ -13044,10 +13044,10 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar const u8 gMonFootprint_TerapagosTerastal[] = INCBIN_U8("graphics/pokemon/terapagos/terastal/footprint.1bpp"); #endif //P_FOOTPRINTS - // const u32 gMonFrontPic_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/front.4bpp.lz"); - // const u32 gMonPalette_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/normal.gbapal.lz"); - // const u32 gMonBackPic_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/back.4bpp.lz"); - // const u32 gMonShinyPalette_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/shiny.gbapal.lz"); + const u32 gMonFrontPic_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/front.4bpp.lz"); + const u32 gMonPalette_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/normal.gbapal.lz"); + const u32 gMonBackPic_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/back.4bpp.lz"); + const u32 gMonShinyPalette_TerapagosStellar[] = INCBIN_U32("graphics/pokemon/terapagos/stellar/shiny.gbapal.lz"); const u8 gMonIcon_TerapagosStellar[] = INCBIN_U8("graphics/pokemon/terapagos/stellar/icon.4bpp"); #if P_FOOTPRINTS const u8 gMonFootprint_TerapagosStellar[] = INCBIN_U8("graphics/pokemon/terapagos/stellar/footprint.1bpp"); diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 4d042773489a..3865f0b0c6a4 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -14126,6 +14126,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .metronomeBanned = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = B_UPDATED_MOVE_DATA >= GEN_7 ? MOVE_EFFECT_DEF_PLUS_2: MOVE_EFFECT_DEF_PLUS_1, + .self = TRUE, .chance = 50, }), .contestEffect = CONTEST_EFFECT_USER_MORE_EASILY_STARTLED, @@ -18233,7 +18234,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .name = HANDLE_EXPANDED_MOVE_NAME("BitterMalice", "Bitter Malice"), .description = COMPOUND_STRING( "A spine-chilling resentment.\n" - "May lower the foe's Attack."), + "Lowers the foe's Attack."), .effect = EFFECT_HIT, .power = B_UPDATED_MOVE_DATA >= GEN_9 ? 75 : 60, .type = TYPE_GHOST, diff --git a/src/data/party_menu.h b/src/data/party_menu.h index 99488d4ebe9d..b54b532d90bc 100644 --- a/src/data/party_menu.h +++ b/src/data/party_menu.h @@ -497,9 +497,9 @@ static const struct WindowTemplate sItemGiveTakeWindowTemplate = { .bg = 2, .tilemapLeft = 23, - .tilemapTop = 13, + .tilemapTop = 11, .width = 6, - .height = 6, + .height = 8, .paletteNum = 14, .baseBlock = 0x39D, }; @@ -659,6 +659,7 @@ static const u8 *const sActionStringTable[] = [PARTY_MSG_ALREADY_HOLDING_ONE] = gText_AlreadyHoldingOne, [PARTY_MSG_WHICH_APPLIANCE] = gText_WhichAppliance, [PARTY_MSG_CHOOSE_SECOND_FUSION] = gText_NextFusionMon, + [PARTY_MSG_MOVE_ITEM_WHERE] = gText_MoveItemWhere, }; static const u8 *const sDescriptionStringTable[] = @@ -698,6 +699,7 @@ struct [MENU_ITEM] = {gText_Item, CursorCb_Item}, [MENU_GIVE] = {gMenuText_Give, CursorCb_Give}, [MENU_TAKE_ITEM] = {gText_Take, CursorCb_TakeItem}, + [MENU_MOVE_ITEM] = {gMenuText_Move, CursorCb_MoveItem}, [MENU_MAIL] = {gText_Mail, CursorCb_Mail}, [MENU_TAKE_MAIL] = {gText_Take2, CursorCb_TakeMail}, [MENU_READ] = {gText_Read2, CursorCb_Read}, @@ -728,7 +730,7 @@ static const u8 sPartyMenuAction_SummaryCancel[] = {MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_EnterSummaryCancel[] = {MENU_ENTER, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_NoEntrySummaryCancel[] = {MENU_NO_ENTRY, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_StoreSummaryCancel[] = {MENU_STORE, MENU_SUMMARY, MENU_CANCEL1}; -static const u8 sPartyMenuAction_GiveTakeItemCancel[] = {MENU_GIVE, MENU_TAKE_ITEM, MENU_CANCEL2}; +static const u8 sPartyMenuAction_GiveTakeItemCancel[] = {MENU_GIVE, MENU_TAKE_ITEM, MENU_MOVE_ITEM, MENU_CANCEL2}; static const u8 sPartyMenuAction_ReadTakeMailCancel[] = {MENU_READ, MENU_TAKE_MAIL, MENU_CANCEL2}; static const u8 sPartyMenuAction_RegisterSummaryCancel[] = {MENU_REGISTER, MENU_SUMMARY, MENU_CANCEL1}; static const u8 sPartyMenuAction_TradeSummaryCancel1[] = {MENU_TRADE1, MENU_SUMMARY, MENU_CANCEL1}; diff --git a/src/data/pokemon/level_up_learnsets/gen_9.h b/src/data/pokemon/level_up_learnsets/gen_9.h index 21b0a96112c5..0db97107a22a 100644 --- a/src/data/pokemon/level_up_learnsets/gen_9.h +++ b/src/data/pokemon/level_up_learnsets/gen_9.h @@ -12801,7 +12801,6 @@ static const struct LevelUpMove sBasculinWhiteStripedLevelUpLearnset[] = { }; static const struct LevelUpMove sBasculegionLevelUpLearnset[] = { - LEVEL_UP_MOVE( 0, MOVE_DIRE_CLAW), LEVEL_UP_MOVE( 1, MOVE_SHADOW_BALL), LEVEL_UP_MOVE( 1, MOVE_TAIL_WHIP), LEVEL_UP_MOVE( 1, MOVE_WATER_GUN), diff --git a/src/data/pokemon/species_info/gen_1_families.h b/src/data/pokemon/species_info/gen_1_families.h index a2f63f443d05..717ee9b2b7d4 100644 --- a/src/data/pokemon/species_info/gen_1_families.h +++ b/src/data/pokemon/species_info/gen_1_families.h @@ -1411,7 +1411,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_KEEN_EYE, ABILITY_TANGLED_FEET, ABILITY_BIG_PECKS }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_BIG_PECKS }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Pidgey"), .cryId = CRY_PIDGEY, @@ -1464,7 +1468,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_KEEN_EYE, ABILITY_TANGLED_FEET, ABILITY_BIG_PECKS }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_BIG_PECKS }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Pidgeotto"), .cryId = CRY_PIDGEOTTO, @@ -1528,7 +1536,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_KEEN_EYE, ABILITY_TANGLED_FEET, ABILITY_BIG_PECKS }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_BIG_PECKS }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Pidgeot"), .cryId = CRY_PIDGEOT, @@ -3530,7 +3542,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_RIVALRY, ABILITY_HUSTLE }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_HUSTLE }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Nidoran♀"), .cryId = CRY_NIDORAN_F, @@ -3584,7 +3600,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_RIVALRY, ABILITY_HUSTLE }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_HUSTLE }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Nidorina"), .cryId = CRY_NIDORINA, @@ -3645,7 +3665,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_RIVALRY, ABILITY_SHEER_FORCE }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_SHEER_FORCE }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Nidoqueen"), .cryId = CRY_NIDOQUEEN, @@ -3697,7 +3721,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_RIVALRY, ABILITY_HUSTLE }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_HUSTLE }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Nidoran♂"), .cryId = CRY_NIDORAN_M, @@ -3750,7 +3778,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_RIVALRY, ABILITY_HUSTLE }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_HUSTLE }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Nidorino"), .cryId = CRY_NIDORINO, @@ -3811,7 +3843,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_RIVALRY, ABILITY_SHEER_FORCE }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_SHEER_FORCE }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Nidoking"), .cryId = CRY_NIDOKING, @@ -3874,7 +3910,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = 140, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CUTE_CHARM, ABILITY_MAGIC_GUARD, ABILITY_FRIEND_GUARD }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_FRIEND_GUARD }, + #endif .bodyColor = BODY_COLOR_PINK, .noFlip = TRUE, .speciesName = _("Cleffa"), @@ -3930,7 +3970,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = 140, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CUTE_CHARM, ABILITY_MAGIC_GUARD, ABILITY_FRIEND_GUARD }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_FRIEND_GUARD }, + #endif .bodyColor = BODY_COLOR_PINK, .noFlip = TRUE, .speciesName = _("Clefairy"), @@ -3993,7 +4037,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = 140, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CUTE_CHARM, ABILITY_MAGIC_GUARD, ABILITY_UNAWARE }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_UNAWARE }, + #endif .bodyColor = BODY_COLOR_PINK, .noFlip = TRUE, .speciesName = _("Clefable"), @@ -4281,7 +4329,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_CUTE_CHARM, ABILITY_COMPETITIVE, ABILITY_FRIEND_GUARD }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_FRIEND_GUARD }, + #endif .bodyColor = BODY_COLOR_PINK, .noFlip = TRUE, .speciesName = _("Igglybuff"), @@ -4337,7 +4389,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_CUTE_CHARM, ABILITY_COMPETITIVE, ABILITY_FRIEND_GUARD }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_FRIEND_GUARD }, + #endif .bodyColor = BODY_COLOR_PINK, .noFlip = TRUE, .speciesName = _("Jigglypuff"), @@ -4400,7 +4456,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_CUTE_CHARM, ABILITY_COMPETITIVE, ABILITY_FRISK }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_FRISK }, + #endif .bodyColor = BODY_COLOR_PINK, .noFlip = TRUE, .speciesName = _("Wigglytuff"), @@ -4879,7 +4939,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_EFFECT_SPORE, ABILITY_DRY_SKIN, ABILITY_DAMP }, + #else + .abilities = { ABILITY_EFFECT_SPORE, ABILITY_NONE, ABILITY_DAMP }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Paras"), .cryId = CRY_PARAS, @@ -4936,7 +5000,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_EFFECT_SPORE, ABILITY_DRY_SKIN, ABILITY_DAMP }, + #else + .abilities = { ABILITY_EFFECT_SPORE, ABILITY_NONE, ABILITY_DAMP }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Parasect"), .cryId = CRY_PARASECT, @@ -4991,7 +5059,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_COMPOUND_EYES, ABILITY_TINTED_LENS, ABILITY_RUN_AWAY }, + #else + .abilities = { ABILITY_COMPOUND_EYES, ABILITY_NONE, ABILITY_RUN_AWAY }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Venonat"), .cryId = CRY_VENONAT, @@ -5047,7 +5119,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SHIELD_DUST, ABILITY_TINTED_LENS, ABILITY_WONDER_SKIN }, + #else + .abilities = { ABILITY_SHIELD_DUST, ABILITY_NONE, ABILITY_WONDER_SKIN }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Venomoth"), .cryId = CRY_VENOMOTH, @@ -5341,7 +5417,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_PICKUP, ABILITY_TECHNICIAN, ABILITY_UNNERVE }, + #else + .abilities = { ABILITY_PICKUP, ABILITY_NONE, ABILITY_UNNERVE }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Meowth"), .cryId = CRY_MEOWTH, @@ -5398,7 +5478,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_LIMBER, ABILITY_TECHNICIAN, ABILITY_UNNERVE }, + #else + .abilities = { ABILITY_LIMBER, ABILITY_NONE, ABILITY_UNNERVE }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Persian"), .cryId = CRY_PERSIAN, @@ -5840,7 +5924,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_VITAL_SPIRIT, ABILITY_ANGER_POINT, ABILITY_DEFIANT }, + #else + .abilities = { ABILITY_VITAL_SPIRIT, ABILITY_NONE, ABILITY_DEFIANT }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Mankey"), .cryId = CRY_MANKEY, @@ -5894,7 +5982,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_VITAL_SPIRIT, ABILITY_ANGER_POINT, ABILITY_DEFIANT }, + #else + .abilities = { ABILITY_VITAL_SPIRIT, ABILITY_NONE, ABILITY_DEFIANT }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Primeape"), .cryId = CRY_PRIMEAPE, @@ -6724,7 +6816,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_GUTS, ABILITY_NO_GUARD, ABILITY_STEADFAST }, + #else + .abilities = { ABILITY_GUTS, ABILITY_NONE, ABILITY_STEADFAST }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Machop"), .cryId = CRY_MACHOP, @@ -6778,7 +6874,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_GUTS, ABILITY_NO_GUARD, ABILITY_STEADFAST }, + #else + .abilities = { ABILITY_GUTS, ABILITY_NONE, ABILITY_STEADFAST }, + #endif .bodyColor = BODY_COLOR_GRAY, .noFlip = TRUE, .speciesName = _("Machoke"), @@ -6842,7 +6942,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_GUTS, ABILITY_NO_GUARD, ABILITY_STEADFAST }, + #else + .abilities = { ABILITY_GUTS, ABILITY_NONE, ABILITY_STEADFAST }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Machamp"), .cryId = CRY_MACHAMP, @@ -6898,7 +7002,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_GUTS, ABILITY_NO_GUARD, ABILITY_STEADFAST }, + #else + .abilities = { ABILITY_GUTS, ABILITY_NONE, ABILITY_STEADFAST }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Machamp"), .cryId = CRY_MACHAMP, @@ -8687,7 +8795,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_THICK_FAT, ABILITY_HYDRATION, ABILITY_ICE_BODY }, + #else + .abilities = { ABILITY_THICK_FAT, ABILITY_NONE, ABILITY_ICE_BODY }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Seel"), .cryId = CRY_SEEL, @@ -8740,7 +8852,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_THICK_FAT, ABILITY_HYDRATION, ABILITY_ICE_BODY }, + #else + .abilities = { ABILITY_THICK_FAT, ABILITY_NONE, ABILITY_ICE_BODY }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Dewgong"), .cryId = CRY_DEWGONG, @@ -9024,7 +9140,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_3), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SHELL_ARMOR, ABILITY_SKILL_LINK, ABILITY_OVERCOAT }, + #else + .abilities = { ABILITY_SHELL_ARMOR, ABILITY_NONE, ABILITY_OVERCOAT }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Shellder"), .cryId = CRY_SHELLDER, @@ -9080,7 +9200,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_3), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SHELL_ARMOR, ABILITY_SKILL_LINK, ABILITY_OVERCOAT }, + #else + .abilities = { ABILITY_SHELL_ARMOR, ABILITY_NONE, ABILITY_OVERCOAT }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Cloyster"), .cryId = CRY_CLOYSTER, @@ -9602,7 +9726,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INSOMNIA, ABILITY_FOREWARN, ABILITY_INNER_FOCUS }, + #else + .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_INNER_FOCUS }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Drowzee"), .cryId = CRY_DROWZEE, @@ -9656,7 +9784,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INSOMNIA, ABILITY_FOREWARN, ABILITY_INNER_FOCUS }, + #else + .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_INNER_FOCUS }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Hypno"), .cryId = CRY_HYPNO, @@ -10507,7 +10639,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_GUTS, ABILITY_STEADFAST, ABILITY_VITAL_SPIRIT }, + #else + .abilities = { ABILITY_GUTS, ABILITY_NONE, ABILITY_VITAL_SPIRIT }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Tyrogue"), .cryId = CRY_TYROGUE, @@ -10563,7 +10699,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_LIMBER, ABILITY_RECKLESS, ABILITY_UNBURDEN }, + #else + .abilities = { ABILITY_LIMBER, ABILITY_NONE, ABILITY_UNBURDEN }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Hitmonlee"), .cryId = CRY_HITMONLEE, @@ -10615,7 +10755,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_KEEN_EYE, ABILITY_IRON_FIST, ABILITY_INNER_FOCUS }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_INNER_FOCUS }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Hitmonchan"), .cryId = CRY_HITMONCHAN, @@ -10668,7 +10812,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INTIMIDATE, ABILITY_TECHNICIAN, ABILITY_STEADFAST }, + #else + .abilities = { ABILITY_INTIMIDATE, ABILITY_NONE, ABILITY_STEADFAST }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Hitmontop"), .cryId = CRY_HITMONTOP, @@ -11364,7 +11512,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_LEAF_GUARD, ABILITY_REGENERATOR }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_REGENERATOR }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Tangela"), .cryId = CRY_TANGELA, @@ -11589,7 +11741,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_DRAGON), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SWIFT_SWIM, ABILITY_SNIPER, ABILITY_DAMP }, + #else + .abilities = { ABILITY_SWIFT_SWIM, ABILITY_NONE, ABILITY_DAMP }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Horsea"), .cryId = CRY_HORSEA, @@ -11644,7 +11800,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_DRAGON), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_POISON_POINT, ABILITY_SNIPER, ABILITY_DAMP }, + #else + .abilities = { ABILITY_POISON_POINT, ABILITY_NONE, ABILITY_DAMP }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Seadra"), .cryId = CRY_SEADRA, @@ -11708,7 +11868,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_DRAGON), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SWIFT_SWIM, ABILITY_SNIPER, ABILITY_DAMP }, + #else + .abilities = { ABILITY_SWIFT_SWIM, ABILITY_NONE, ABILITY_DAMP }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Kingdra"), .cryId = CRY_KINGDRA, @@ -11997,7 +12161,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SOUNDPROOF, ABILITY_FILTER, ABILITY_TECHNICIAN }, + #else + .abilities = { ABILITY_SOUNDPROOF, ABILITY_NONE, ABILITY_TECHNICIAN }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Mime Jr."), .cryId = CRY_MIME_JR, @@ -12219,7 +12387,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SWARM, ABILITY_TECHNICIAN, ABILITY_STEADFAST }, + #else + .abilities = { ABILITY_SWARM, ABILITY_NONE, ABILITY_STEADFAST }, + #endif .bodyColor = BODY_COLOR_GREEN, .speciesName = _("Scyther"), .cryId = CRY_SCYTHER, @@ -12278,7 +12450,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SWARM, ABILITY_TECHNICIAN, ABILITY_LIGHT_METAL }, + #else + .abilities = { ABILITY_SWARM, ABILITY_NONE, ABILITY_LIGHT_METAL }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Scizor"), .cryId = CRY_SCIZOR, @@ -12450,7 +12626,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_FOREWARN, ABILITY_HYDRATION }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_HYDRATION }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Smoochum"), .cryId = CRY_SMOOCHUM, @@ -12505,7 +12685,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_FOREWARN, ABILITY_DRY_SKIN }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_DRY_SKIN }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Jynx"), .cryId = CRY_JYNX, @@ -12911,7 +13095,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_HYPER_CUTTER, ABILITY_MOLD_BREAKER, ABILITY_MOXIE }, + #else + .abilities = { ABILITY_HYPER_CUTTER, ABILITY_NONE, ABILITY_MOXIE }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Pinsir"), .cryId = CRY_PINSIR, @@ -13026,7 +13214,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INTIMIDATE, ABILITY_ANGER_POINT, ABILITY_SHEER_FORCE }, + #else + .abilities = { ABILITY_INTIMIDATE, ABILITY_NONE, ABILITY_SHEER_FORCE }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Tauros"), .cryId = CRY_TAUROS, @@ -13596,7 +13788,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_RUN_AWAY, ABILITY_ADAPTABILITY, ABILITY_ANTICIPATION }, + #else + .abilities = { ABILITY_RUN_AWAY, ABILITY_NONE, ABILITY_ANTICIPATION }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Eevee"), .cryId = CRY_EEVEE, @@ -14201,7 +14397,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_TRACE, ABILITY_DOWNLOAD, ABILITY_ANALYTIC }, + #else + .abilities = { ABILITY_TRACE, ABILITY_NONE, ABILITY_ANALYTIC }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Porygon"), .cryId = CRY_PORYGON, @@ -14256,7 +14456,11 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_TRACE, ABILITY_DOWNLOAD, ABILITY_ANALYTIC }, + #else + .abilities = { ABILITY_TRACE, ABILITY_NONE, ABILITY_ANALYTIC }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Porygon2"), .cryId = CRY_PORYGON2, diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h index 5c42d99ef935..ebba260d04ff 100644 --- a/src/data/pokemon/species_info/gen_2_families.h +++ b/src/data/pokemon/species_info/gen_2_families.h @@ -1959,7 +1959,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_LEAF_GUARD, ABILITY_INFILTRATOR }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_INFILTRATOR }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Hoppip"), .cryId = CRY_HOPPIP, @@ -2013,7 +2017,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_LEAF_GUARD, ABILITY_INFILTRATOR }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_INFILTRATOR }, + #endif .bodyColor = BODY_COLOR_GREEN, .speciesName = _("Skiploom"), .cryId = CRY_SKIPLOOM, @@ -2075,7 +2083,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_LEAF_GUARD, ABILITY_INFILTRATOR }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_INFILTRATOR }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Jumpluff"), .cryId = CRY_JUMPLUFF, @@ -2247,7 +2259,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_SOLAR_POWER, ABILITY_EARLY_BIRD }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_EARLY_BIRD }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Sunkern"), .cryId = CRY_SUNKERN, @@ -2300,7 +2316,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_SOLAR_POWER, ABILITY_EARLY_BIRD }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_EARLY_BIRD }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Sunflora"), .cryId = CRY_SUNFLORA, @@ -2692,7 +2712,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = 35, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INSOMNIA, ABILITY_SUPER_LUCK, ABILITY_PRANKSTER }, + #else + .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_PRANKSTER }, + #endif .bodyColor = BODY_COLOR_BLACK, .speciesName = _("Murkrow"), .cryId = CRY_MURKROW, @@ -3691,7 +3715,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD, EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INTIMIDATE, ABILITY_QUICK_FEET, ABILITY_RATTLED }, + #else + .abilities = { ABILITY_INTIMIDATE, ABILITY_NONE, ABILITY_RATTLED }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Granbull"), .cryId = CRY_GRANBULL, @@ -3925,7 +3953,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_STURDY, ABILITY_GLUTTONY, ABILITY_CONTRARY }, + #else + .abilities = { ABILITY_STURDY, ABILITY_NONE, ABILITY_CONTRARY }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Shuckle"), .cryId = CRY_SHUCKLE, @@ -4336,7 +4368,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_PICKUP, ABILITY_QUICK_FEET, ABILITY_HONEY_GATHER }, + #else + .abilities = { ABILITY_PICKUP, ABILITY_NONE, ABILITY_HONEY_GATHER }, + #endif .bodyColor = BODY_COLOR_BROWN, .noFlip = TRUE, .speciesName = _("Teddiursa"), @@ -4390,7 +4426,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_GUTS, ABILITY_QUICK_FEET, ABILITY_UNNERVE }, + #else + .abilities = { ABILITY_GUTS, ABILITY_NONE, ABILITY_UNNERVE }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Ursaring"), .cryId = CRY_URSARING, @@ -4671,7 +4711,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_SNOW_CLOAK, ABILITY_THICK_FAT }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_THICK_FAT }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Swinub"), .cryId = CRY_SWINUB, @@ -4725,7 +4769,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_SNOW_CLOAK, ABILITY_THICK_FAT }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_THICK_FAT }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Piloswine"), .cryId = CRY_PILOSWINE, @@ -5022,7 +5070,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_WATER_2), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_HUSTLE, ABILITY_SNIPER, ABILITY_MOODY }, + #else + .abilities = { ABILITY_HUSTLE, ABILITY_NONE, ABILITY_MOODY }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Remoraid"), .cryId = CRY_REMORAID, @@ -5076,7 +5128,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_WATER_2), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SUCTION_CUPS, ABILITY_SNIPER, ABILITY_MOODY }, + #else + .abilities = { ABILITY_SUCTION_CUPS, ABILITY_NONE, ABILITY_MOODY }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Octillery"), .cryId = CRY_OCTILLERY, @@ -5641,7 +5697,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INTIMIDATE, ABILITY_FRISK, ABILITY_SAP_SIPPER }, + #else + .abilities = { ABILITY_INTIMIDATE, ABILITY_NONE, ABILITY_SAP_SIPPER }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Stantler"), .cryId = CRY_STANTLER, @@ -5751,7 +5811,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OWN_TEMPO, ABILITY_TECHNICIAN, ABILITY_MOODY }, + #else + .abilities = { ABILITY_OWN_TEMPO, ABILITY_NONE, ABILITY_MOODY }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Smeargle"), .cryId = CRY_SMEARGLE, @@ -5808,7 +5872,11 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_THICK_FAT, ABILITY_SCRAPPY, ABILITY_SAP_SIPPER }, + #else + .abilities = { ABILITY_THICK_FAT, ABILITY_NONE, ABILITY_SAP_SIPPER }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Miltank"), .cryId = CRY_MILTANK, diff --git a/src/data/pokemon/species_info/gen_3_families.h b/src/data/pokemon/species_info/gen_3_families.h index 2147acb00224..1e3ea5539f32 100644 --- a/src/data/pokemon/species_info/gen_3_families.h +++ b/src/data/pokemon/species_info/gen_3_families.h @@ -713,7 +713,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_RUN_AWAY, ABILITY_QUICK_FEET, ABILITY_RATTLED }, + #else + .abilities = { ABILITY_RUN_AWAY, ABILITY_NONE, ABILITY_RATTLED }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Poochyena"), .cryId = CRY_POOCHYENA, @@ -766,7 +770,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INTIMIDATE, ABILITY_QUICK_FEET, ABILITY_MOXIE }, + #else + .abilities = { ABILITY_INTIMIDATE, ABILITY_NONE, ABILITY_MOXIE }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Mightyena"), .cryId = CRY_MIGHTYENA, @@ -830,7 +838,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_PICKUP, ABILITY_GLUTTONY, ABILITY_QUICK_FEET }, + #else + .abilities = { ABILITY_PICKUP, ABILITY_NONE, ABILITY_QUICK_FEET }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Zigzagoon"), .cryId = CRY_ZIGZAGOON, @@ -886,7 +898,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_PICKUP, ABILITY_GLUTTONY, ABILITY_QUICK_FEET }, + #else + .abilities = { ABILITY_PICKUP, ABILITY_NONE, ABILITY_QUICK_FEET }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Linoone"), .cryId = CRY_LINOONE, @@ -1886,7 +1902,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_KEEN_EYE, ABILITY_HYDRATION, ABILITY_RAIN_DISH }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_RAIN_DISH }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Wingull"), .cryId = CRY_WINGULL, @@ -1947,7 +1967,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_KEEN_EYE, ABILITY_DRIZZLE, ABILITY_RAIN_DISH }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_RAIN_DISH }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Pelipper"), .cryId = CRY_PELIPPER, @@ -2478,7 +2502,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FLUCTUATING, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_EFFECT_SPORE, ABILITY_POISON_HEAL, ABILITY_QUICK_FEET }, + #else + .abilities = { ABILITY_EFFECT_SPORE, ABILITY_NONE, ABILITY_QUICK_FEET }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Shroomish"), .cryId = CRY_SHROOMISH, @@ -2533,7 +2561,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FLUCTUATING, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_EFFECT_SPORE, ABILITY_POISON_HEAL, ABILITY_TECHNICIAN }, + #else + .abilities = { ABILITY_EFFECT_SPORE, ABILITY_NONE, ABILITY_TECHNICIAN }, + #endif .bodyColor = BODY_COLOR_GREEN, .speciesName = _("Breloom"), .cryId = CRY_BRELOOM, @@ -3307,7 +3339,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD, EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CUTE_CHARM, ABILITY_NORMALIZE, ABILITY_WONDER_SKIN }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_WONDER_SKIN }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Skitty"), .cryId = CRY_SKITTY, @@ -3367,7 +3403,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD, EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CUTE_CHARM, ABILITY_NORMALIZE, ABILITY_WONDER_SKIN }, + #else + .abilities = { ABILITY_CUTE_CHARM, ABILITY_NONE, ABILITY_WONDER_SKIN }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Delcatty"), .cryId = CRY_DELCATTY, @@ -3423,7 +3463,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_KEEN_EYE, ABILITY_STALL, ABILITY_PRANKSTER }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_PRANKSTER }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Sableye"), .cryId = CRY_SABLEYE, @@ -4227,7 +4271,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_PLUS, ABILITY_NONE, ABILITY_LIGHTNING_ROD }, + #else + .abilities = { ABILITY_PLUS, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Plusle"), .cryId = CRY_PLUSLE, @@ -4282,7 +4330,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_MINUS, ABILITY_NONE, ABILITY_VOLT_ABSORB }, + #else + .abilities = { ABILITY_MINUS, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Minun"), .cryId = CRY_MINUN, @@ -4402,7 +4454,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FLUCTUATING, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG, EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_TINTED_LENS, ABILITY_PRANKSTER }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_PRANKSTER }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Illumise"), .cryId = CRY_ILLUMISE, @@ -5039,7 +5095,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_SIMPLE, ABILITY_OWN_TEMPO }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_OWN_TEMPO }, + #endif .bodyColor = BODY_COLOR_YELLOW, .speciesName = _("Numel"), .cryId = CRY_NUMEL, @@ -5097,7 +5157,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_MAGMA_ARMOR, ABILITY_SOLID_ROCK, ABILITY_ANGER_POINT }, + #else + .abilities = { ABILITY_MAGMA_ARMOR, ABILITY_NONE, ABILITY_ANGER_POINT }, + #endif .bodyColor = BODY_COLOR_RED, .speciesName = _("Camerupt"), .cryId = CRY_CAMERUPT, @@ -5216,7 +5280,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_WHITE_SMOKE, ABILITY_DROUGHT, ABILITY_SHELL_ARMOR }, + #else + .abilities = { ABILITY_WHITE_SMOKE, ABILITY_NONE, ABILITY_SHELL_ARMOR }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Torkoal"), .cryId = CRY_TORKOAL, @@ -5378,7 +5446,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD, EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OWN_TEMPO, ABILITY_TANGLED_FEET, ABILITY_CONTRARY }, + #else + .abilities = { ABILITY_OWN_TEMPO, ABILITY_NONE, ABILITY_CONTRARY }, + #endif .bodyColor = BODY_COLOR_BROWN, .noFlip = TRUE, .speciesName = _("Spinda"), @@ -6133,7 +6205,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_2), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_ANTICIPATION, ABILITY_HYDRATION }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_HYDRATION }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Barboach"), .cryId = CRY_BARBOACH, @@ -6187,7 +6263,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_2), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_OBLIVIOUS, ABILITY_ANTICIPATION, ABILITY_HYDRATION }, + #else + .abilities = { ABILITY_OBLIVIOUS, ABILITY_NONE, ABILITY_HYDRATION }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Whiscash"), .cryId = CRY_WHISCASH, @@ -6699,7 +6779,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_ERRATIC, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_DRAGON), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_SWIFT_SWIM, ABILITY_OBLIVIOUS, ABILITY_ADAPTABILITY }, + #else + .abilities = { ABILITY_SWIFT_SWIM, ABILITY_NONE, ABILITY_ADAPTABILITY }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Feebas"), .cryId = CRY_FEEBAS, @@ -6754,7 +6838,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_ERRATIC, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_DRAGON), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_MARVEL_SCALE, ABILITY_COMPETITIVE, ABILITY_CUTE_CHARM }, + #else + .abilities = { ABILITY_MARVEL_SCALE, ABILITY_NONE, ABILITY_CUTE_CHARM }, + #endif .bodyColor = BODY_COLOR_PINK, .speciesName = _("Milotic"), .cryId = CRY_MILOTIC, @@ -7043,7 +7131,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_COLOR_CHANGE, ABILITY_NONE, ABILITY_PROTEAN }, + #else + .abilities = { ABILITY_COLOR_CHANGE, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_GREEN, .speciesName = _("Kecleon"), .cryId = CRY_KECLEON, @@ -7099,7 +7191,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_AMORPHOUS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INSOMNIA, ABILITY_FRISK, ABILITY_CURSED_BODY }, + #else + .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_CURSED_BODY }, + #endif .bodyColor = BODY_COLOR_BLACK, .speciesName = _("Shuppet"), .cryId = CRY_SHUPPET, @@ -7154,7 +7250,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_AMORPHOUS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INSOMNIA, ABILITY_FRISK, ABILITY_CURSED_BODY }, + #else + .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_CURSED_BODY }, + #endif .bodyColor = BODY_COLOR_BLACK, .speciesName = _("Banette"), .cryId = CRY_BANETTE, @@ -7270,7 +7370,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_AMORPHOUS), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_LEVITATE, ABILITY_NONE, ABILITY_FRISK }, + #else + .abilities = { ABILITY_LEVITATE, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_BLACK, .speciesName = _("Duskull"), .cryId = CRY_DUSKULL, @@ -7326,7 +7430,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_AMORPHOUS), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_PRESSURE, ABILITY_NONE, ABILITY_FRISK }, + #else + .abilities = { ABILITY_PRESSURE, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_BLACK, .speciesName = _("Dusclops"), .cryId = CRY_DUSCLOPS, @@ -7390,7 +7498,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_AMORPHOUS), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_PRESSURE, ABILITY_NONE, ABILITY_FRISK }, + #else + .abilities = { ABILITY_PRESSURE, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_BLACK, .speciesName = _("Dusknoir"), .cryId = CRY_DUSKNOIR, @@ -7446,7 +7558,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_GRASS), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_CHLOROPHYLL, ABILITY_SOLAR_POWER, ABILITY_HARVEST }, + #else + .abilities = { ABILITY_CHLOROPHYLL, ABILITY_NONE, ABILITY_HARVEST }, + #endif .bodyColor = BODY_COLOR_GREEN, .speciesName = _("Tropius"), .cryId = CRY_TROPIUS, @@ -7619,7 +7735,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = 35, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_PRESSURE, ABILITY_SUPER_LUCK, ABILITY_JUSTIFIED }, + #else + .abilities = { ABILITY_PRESSURE, ABILITY_NONE, ABILITY_JUSTIFIED }, + #endif .bodyColor = BODY_COLOR_WHITE, .noFlip = TRUE, .speciesName = _("Absol"), @@ -7736,7 +7856,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INNER_FOCUS, ABILITY_ICE_BODY, ABILITY_MOODY }, + #else + .abilities = { ABILITY_INNER_FOCUS, ABILITY_NONE, ABILITY_MOODY }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Snorunt"), .cryId = CRY_SNORUNT, @@ -7791,7 +7915,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FAIRY, EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_INNER_FOCUS, ABILITY_ICE_BODY, ABILITY_MOODY }, + #else + .abilities = { ABILITY_INNER_FOCUS, ABILITY_NONE, ABILITY_MOODY }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Glalie"), .cryId = CRY_GLALIE, @@ -7960,7 +8088,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_THICK_FAT, ABILITY_ICE_BODY, ABILITY_OBLIVIOUS }, + #else + .abilities = { ABILITY_THICK_FAT, ABILITY_NONE, ABILITY_OBLIVIOUS }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Spheal"), .cryId = CRY_SPHEAL, @@ -8014,7 +8146,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_THICK_FAT, ABILITY_ICE_BODY, ABILITY_OBLIVIOUS }, + #else + .abilities = { ABILITY_THICK_FAT, ABILITY_NONE, ABILITY_OBLIVIOUS }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Sealeo"), .cryId = CRY_SEALEO, @@ -8073,7 +8209,11 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_4 .abilities = { ABILITY_THICK_FAT, ABILITY_ICE_BODY, ABILITY_OBLIVIOUS }, + #else + .abilities = { ABILITY_THICK_FAT, ABILITY_NONE, ABILITY_OBLIVIOUS }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Walrein"), .cryId = CRY_WALREIN, diff --git a/src/data/pokemon/species_info/gen_4_families.h b/src/data/pokemon/species_info/gen_4_families.h index 57860ffeee3c..a8795d698e5d 100644 --- a/src/data/pokemon/species_info/gen_4_families.h +++ b/src/data/pokemon/species_info/gen_4_families.h @@ -539,7 +539,11 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_RECKLESS }, + #else + .abilities = { ABILITY_KEEN_EYE, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_BROWN, .speciesName = _("Starly"), .cryId = CRY_STARLY, diff --git a/src/data/pokemon/species_info/gen_5_families.h b/src/data/pokemon/species_info/gen_5_families.h index b25aae654627..2063ae91f3c1 100644 --- a/src/data/pokemon/species_info/gen_5_families.h +++ b/src/data/pokemon/species_info/gen_5_families.h @@ -1710,7 +1710,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_STURDY, ABILITY_WEAK_ARMOR, ABILITY_SAND_FORCE }, + #else + .abilities = { ABILITY_STURDY, ABILITY_NONE, ABILITY_SAND_FORCE }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Roggenrola"), .cryId = CRY_ROGGENROLA, @@ -1766,7 +1770,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_STURDY, ABILITY_WEAK_ARMOR, ABILITY_SAND_FORCE }, + #else + .abilities = { ABILITY_STURDY, ABILITY_NONE, ABILITY_SAND_FORCE }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Boldore"), .cryId = CRY_BOLDORE, @@ -1828,7 +1836,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_STURDY, ABILITY_SAND_STREAM, ABILITY_SAND_FORCE }, + #else + .abilities = { ABILITY_STURDY, ABILITY_NONE, ABILITY_SAND_FORCE }, + #endif .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Gigalith"), .cryId = CRY_GIGALITH, @@ -5335,7 +5347,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_FRISK, ABILITY_COMPETITIVE, ABILITY_SHADOW_TAG }, + #else + .abilities = { ABILITY_FRISK, ABILITY_NONE, ABILITY_SHADOW_TAG }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Gothita"), .cryId = CRY_GOTHITA, @@ -5388,7 +5404,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_FRISK, ABILITY_COMPETITIVE, ABILITY_SHADOW_TAG }, + #else + .abilities = { ABILITY_FRISK, ABILITY_NONE, ABILITY_SHADOW_TAG }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Gothorita"), .cryId = CRY_GOTHORITA, @@ -5441,7 +5461,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_HUMAN_LIKE), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_FRISK, ABILITY_COMPETITIVE, ABILITY_SHADOW_TAG }, + #else + .abilities = { ABILITY_FRISK, ABILITY_NONE, ABILITY_SHADOW_TAG }, + #endif .bodyColor = BODY_COLOR_PURPLE, .speciesName = _("Gothitelle"), .cryId = CRY_GOTHITELLE, @@ -5767,7 +5791,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_ICE_BODY, ABILITY_SNOW_CLOAK, ABILITY_WEAK_ARMOR }, + #else + .abilities = { ABILITY_ICE_BODY, ABILITY_NONE, ABILITY_WEAK_ARMOR }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Vanillite"), .cryId = CRY_VANILLITE, @@ -5821,7 +5849,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_ICE_BODY, ABILITY_SNOW_CLOAK, ABILITY_WEAK_ARMOR }, + #else + .abilities = { ABILITY_ICE_BODY, ABILITY_NONE, ABILITY_WEAK_ARMOR }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Vanillish"), .cryId = CRY_VANILLISH, @@ -5875,7 +5907,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_ICE_BODY, ABILITY_SNOW_WARNING, ABILITY_WEAK_ARMOR }, + #else + .abilities = { ABILITY_ICE_BODY, ABILITY_NONE, ABILITY_WEAK_ARMOR }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Vanilluxe"), .cryId = CRY_VANILLUXE, @@ -6973,7 +7009,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_GRASS, EGG_GROUP_MINERAL), + #if P_UPDATED_ABILITIES >= GEN_6 .abilities = { ABILITY_IRON_BARBS, ABILITY_NONE, ABILITY_ANTICIPATION }, + #else + .abilities = { ABILITY_IRON_BARBS, ABILITY_NONE, ABILITY_NONE }, + #endif .bodyColor = BODY_COLOR_GRAY, .speciesName = _("Ferrothorn"), .cryId = CRY_FERROTHORN, @@ -7800,7 +7840,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_SNOW_CLOAK, ABILITY_SLUSH_RUSH, ABILITY_RATTLED }, + #else + .abilities = { ABILITY_SNOW_CLOAK, ABILITY_NONE, ABILITY_RATTLED }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Cubchoo"), .cryId = CRY_CUBCHOO, @@ -7853,7 +7897,11 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_FAST, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), + #if P_UPDATED_ABILITIES >= GEN_7 .abilities = { ABILITY_SNOW_CLOAK, ABILITY_SLUSH_RUSH, ABILITY_SWIFT_SWIM }, + #else + .abilities = { ABILITY_SNOW_CLOAK, ABILITY_NONE, ABILITY_SWIFT_SWIM }, + #endif .bodyColor = BODY_COLOR_WHITE, .speciesName = _("Beartic"), .cryId = CRY_BEARTIC, diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index 8110770ffd43..b94de7eb02ec 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -6278,7 +6278,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = #endif //P_FAMILY_FEZANDIPITI #if P_FAMILY_OGERPON -#define OGERPON_SPECIES_INFO(Form, type, ability, color, iconpalette) \ +#define OGERPON_SPECIES_INFO(Form1, Form2, type, ability, color, iconpalette) \ { \ .baseHP = 80, \ .baseAttack = 120, \ @@ -6303,23 +6303,23 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .categoryName = _("Mask"), \ .height = 12, \ .weight = 398, \ - .description = gOgerpon##Form##PokedexText, \ + .description = gOgerpon##Form1##MaskPokedexText, \ .pokemonScale = 356, \ .pokemonOffset = 17, \ .trainerScale = 256, \ .trainerOffset = 0, \ - .frontPic = gMonFrontPic_Ogerpon##Form, \ + .frontPic = gMonFrontPic_Ogerpon##Form1##Form2, \ .frontPicSize = MON_COORDS_SIZE(64, 64), \ .frontPicYOffset = 0, \ .frontAnimFrames = sAnims_Ogerpon, \ /*.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE,*/ \ - .backPic = gMonBackPic_Ogerpon##Form, \ + .backPic = gMonBackPic_Ogerpon##Form1##Form2, \ .backPicSize = MON_COORDS_SIZE(64, 64), \ - .backPicYOffset = 16, \ + .backPicYOffset = 0, \ /*.backAnimId = BACK_ANIM_NONE,*/ \ - .palette = gMonPalette_Ogerpon##Form, \ - .shinyPalette = gMonShinyPalette_Ogerpon##Form, \ - .iconSprite = gMonIcon_Ogerpon##Form, \ + .palette = gMonPalette_Ogerpon##Form1##Form2, \ + .shinyPalette = gMonShinyPalette_Ogerpon##Form1##Form2, \ + .iconSprite = gMonIcon_Ogerpon##Form1##Mask, \ .iconPalIndex = iconpalette, \ FOOTPRINT(Ogerpon) \ .levelUpLearnset = sOgerponLevelUpLearnset, \ @@ -6329,15 +6329,15 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .isLegendary = TRUE, \ } - [SPECIES_OGERPON_TEAL_MASK] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_DEFIANT, BODY_COLOR_GREEN, 1), - [SPECIES_OGERPON_WELLSPRING_MASK] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_WATER_ABSORB, BODY_COLOR_BLUE, 0), - [SPECIES_OGERPON_HEARTHFLAME_MASK] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_MOLD_BREAKER, BODY_COLOR_RED, 0), - [SPECIES_OGERPON_CORNERSTONE_MASK] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_STURDY, BODY_COLOR_GRAY, 0), + [SPECIES_OGERPON_TEAL_MASK] = OGERPON_SPECIES_INFO(Teal, Mask, TYPE_GRASS, ABILITY_DEFIANT, BODY_COLOR_GREEN, 1), + [SPECIES_OGERPON_WELLSPRING_MASK] = OGERPON_SPECIES_INFO(Wellspring, Mask, TYPE_WATER, ABILITY_WATER_ABSORB, BODY_COLOR_BLUE, 0), + [SPECIES_OGERPON_HEARTHFLAME_MASK] = OGERPON_SPECIES_INFO(Hearthflame, Mask, TYPE_FIRE, ABILITY_MOLD_BREAKER, BODY_COLOR_RED, 0), + [SPECIES_OGERPON_CORNERSTONE_MASK] = OGERPON_SPECIES_INFO(Cornerstone, Mask, TYPE_ROCK, ABILITY_STURDY, BODY_COLOR_GRAY, 0), #if P_TERA_FORMS - [SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL_MASK, BODY_COLOR_GREEN, 1), - [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING_MASK, BODY_COLOR_BLUE, 0), - [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK, BODY_COLOR_RED, 0), - [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK, BODY_COLOR_GRAY, 0), + [SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(Teal, MaskTera, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL_MASK, BODY_COLOR_GREEN, 1), + [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(Wellspring, MaskTera, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING_MASK, BODY_COLOR_BLUE, 0), + [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(Hearthflame, MaskTera, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK, BODY_COLOR_RED, 0), + [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(Cornerstone, MaskTera, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK, BODY_COLOR_GRAY, 0), #endif //P_TERA_FORMS #endif //P_FAMILY_OGERPON @@ -6713,17 +6713,17 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .pokemonOffset = 0, .trainerScale = 365, .trainerOffset = 7, - //.frontPic = gMonFrontPic_TerapagosStellar, - //.frontPicSize = MON_COORDS_SIZE(64, 64), + .frontPic = gMonFrontPic_TerapagosStellar, + .frontPicSize = MON_COORDS_SIZE(64, 64), .frontPicYOffset = 0, .frontAnimFrames = sAnims_TerapagosStellar, //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //.backPic = gMonBackPic_TerapagosStellar, - //.backPicSize = MON_COORDS_SIZE(64, 64), + .backPic = gMonBackPic_TerapagosStellar, + .backPicSize = MON_COORDS_SIZE(64, 64), .backPicYOffset = 0, //.backAnimId = BACK_ANIM_NONE, - //.palette = gMonPalette_TerapagosStellar, - //.shinyPalette = gMonShinyPalette_TerapagosStellar, + .palette = gMonPalette_TerapagosStellar, + .shinyPalette = gMonShinyPalette_TerapagosStellar, .iconSprite = gMonIcon_TerapagosStellar, .iconPalIndex = 0, FOOTPRINT(TerapagosStellar) diff --git a/src/daycare.c b/src/daycare.c index d007de359ded..c13037421ad6 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -1038,8 +1038,7 @@ static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parent { u16 i; u16 species[DAYCARE_MON_COUNT]; - u16 eggSpecies, parentSpecies; - bool8 hasMotherEverstone, hasFatherEverstone; + u16 eggSpecies; for (i = 0; i < DAYCARE_MON_COUNT; i++) { @@ -1056,18 +1055,7 @@ static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parent } } - hasMotherEverstone = ItemId_GetHoldEffect(GetBoxMonData(&daycare->mons[0].mon, MON_DATA_HELD_ITEM)) == HOLD_EFFECT_PREVENT_EVOLVE; - hasFatherEverstone = ItemId_GetHoldEffect(GetBoxMonData(&daycare->mons[1].mon, MON_DATA_HELD_ITEM)) == HOLD_EFFECT_PREVENT_EVOLVE; - - if (hasMotherEverstone) - parentSpecies = species[parentSlots[0]]; - else if (hasFatherEverstone && GET_BASE_SPECIES_ID(GetEggSpecies(species[parentSlots[0]])) == GET_BASE_SPECIES_ID(GetEggSpecies(species[parentSlots[1]]))) - parentSpecies = species[parentSlots[1]]; - else - parentSpecies = GET_BASE_SPECIES_ID(GetEggSpecies(species[parentSlots[0]])); - - eggSpecies = GetEggSpecies(parentSpecies); - + eggSpecies = GetEggSpecies(species[parentSlots[0]]); if (eggSpecies == SPECIES_NIDORAN_F && daycare->offspringPersonality & EGG_GENDER_MALE) eggSpecies = SPECIES_NIDORAN_M; else if (eggSpecies == SPECIES_ILLUMISE && daycare->offspringPersonality & EGG_GENDER_MALE) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index d5c252415df9..9f4d6b5dd65a 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -88,6 +88,7 @@ void FieldClearPlayerInput(struct FieldInput *input) input->input_field_1_2 = FALSE; input->input_field_1_3 = FALSE; input->dpadDirection = 0; + input->pressedRButton = FALSE; } void FieldGetPlayerInput(struct FieldInput *input, u16 newKeys, u16 heldKeys) @@ -115,6 +116,9 @@ void FieldGetPlayerInput(struct FieldInput *input, u16 newKeys, u16 heldKeys) input->heldDirection = TRUE; input->heldDirection2 = TRUE; } + + if (newKeys & R_BUTTON) + input->pressedRButton = TRUE; } if (forcedMove == FALSE) @@ -210,6 +214,25 @@ int ProcessPlayerFieldInput(struct FieldInput *input) } #endif + if (input->pressedRButton && TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE)) + { + ObjectEventClearHeldMovementIfActive(&gObjectEvents[gPlayerAvatar.objectEventId]); + if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_MACH_BIKE) + { + gPlayerAvatar.flags -= PLAYER_AVATAR_FLAG_MACH_BIKE; + gPlayerAvatar.flags += PLAYER_AVATAR_FLAG_ACRO_BIKE; + SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ACRO_BIKE); + PlaySE(SE_BIKE_HOP); + } + else + { + gPlayerAvatar.flags -= PLAYER_AVATAR_FLAG_ACRO_BIKE; + gPlayerAvatar.flags += PLAYER_AVATAR_FLAG_MACH_BIKE; + SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_MACH_BIKE); + PlaySE(SE_BIKE_BELL); + } + } + return FALSE; } diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 50438863afb8..8a875ee05537 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -631,8 +631,11 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING) { - // same speed as running - PlayerWalkFast(direction); + if (heldKeys & B_BUTTON) + PlayerWalkFaster(direction); + else + // same speed as running + PlayerWalkFast(direction); return; } diff --git a/src/frontier_util.c b/src/frontier_util.c index 7f9b37b95762..cdc82a5293c0 100644 --- a/src/frontier_util.c +++ b/src/frontier_util.c @@ -1929,8 +1929,7 @@ static void AppendIfValid(u16 species, u16 heldItem, u16 hp, u8 lvlMode, u8 monL return; for (i = 0; gFrontierBannedSpecies[i] != 0xFFFF - && gFrontierBannedSpecies[i] != GET_BASE_SPECIES_ID(species) - && IsSpeciesEnabled(gFrontierBannedSpecies[i]); i++) + && gFrontierBannedSpecies[i] != GET_BASE_SPECIES_ID(species); i++) ; if (gFrontierBannedSpecies[i] != 0xFFFF) diff --git a/src/naming_screen.c b/src/naming_screen.c index 0f2e2dbb7f94..cdceb73d9f73 100644 --- a/src/naming_screen.c +++ b/src/naming_screen.c @@ -1480,6 +1480,11 @@ static bool8 KeyboardKeyHandler_Character(u8 input) { bool8 textFull = AddTextCharacter(); + if (sNamingScreen ->currentPage == KBPAGE_LETTERS_UPPER && GetTextEntryPosition() == 1) + { + MainState_StartPageSwap(); + } + SquishCursor(); if (textFull) { diff --git a/src/new_game.c b/src/new_game.c index ece85ac544dc..25ae40b9e0aa 100644 --- a/src/new_game.c +++ b/src/new_game.c @@ -90,10 +90,10 @@ static void InitPlayerTrainerId(void) // L=A isnt set here for some reason. static void SetDefaultOptions(void) { - gSaveBlock2Ptr->optionsTextSpeed = OPTIONS_TEXT_SPEED_MID; + gSaveBlock2Ptr->optionsTextSpeed = OPTIONS_TEXT_SPEED_FAST; gSaveBlock2Ptr->optionsWindowFrameType = 0; - gSaveBlock2Ptr->optionsSound = OPTIONS_SOUND_MONO; - gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SHIFT; + gSaveBlock2Ptr->optionsSound = OPTIONS_SOUND_STEREO; + gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SET; gSaveBlock2Ptr->optionsBattleSceneOff = FALSE; gSaveBlock2Ptr->regionMapZoom = FALSE; } diff --git a/src/party_menu.c b/src/party_menu.c index 7a14d0fb720b..0a0cc709fdad 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -82,6 +82,7 @@ enum { MENU_ITEM, MENU_GIVE, MENU_TAKE_ITEM, + MENU_MOVE_ITEM, MENU_MAIL, MENU_TAKE_MAIL, MENU_READ, @@ -477,6 +478,7 @@ static void CursorCb_Cancel1(u8); static void CursorCb_Item(u8); static void CursorCb_Give(u8); static void CursorCb_TakeItem(u8); +static void CursorCb_MoveItem(u8); static void CursorCb_Mail(u8); static void CursorCb_Read(u8); static void CursorCb_TakeMail(u8); @@ -7739,3 +7741,116 @@ void IsLastMonThatKnowsSurf(void) gSpecialVar_Result = TRUE; } } + +void CursorCb_MoveItemCallback(u8 taskId) +{ + u16 item1, item2; + u8 buffer[100]; + + if (gPaletteFade.active || MenuHelpers_ShouldWaitForLinkRecv()) + return; + + switch (PartyMenuButtonHandler(&gPartyMenu.slotId2)) + { + case 2: // User hit B or A while on Cancel + HandleChooseMonCancel(taskId, &gPartyMenu.slotId2); + break; + case 1: // User hit A on a Pokemon + // Pokemon can't give away items to eggs or themselves + if (GetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_IS_EGG) + || gPartyMenu.slotId == gPartyMenu.slotId2) + { + PlaySE(SE_FAILURE); + return; + } + + PlaySE(SE_SELECT); + gPartyMenu.action = PARTY_ACTION_CHOOSE_MON; + + // look up held items + item1 = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM); + item2 = GetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_HELD_ITEM); + + // swap the held items + SetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_HELD_ITEM, &item2); + SetMonData(&gPlayerParty[gPartyMenu.slotId2], MON_DATA_HELD_ITEM, &item1); + + // update the held item icons + UpdatePartyMonHeldItemSprite( + &gPlayerParty[gPartyMenu.slotId], + &sPartyMenuBoxes[gPartyMenu.slotId] + ); + + UpdatePartyMonHeldItemSprite( + &gPlayerParty[gPartyMenu.slotId2], + &sPartyMenuBoxes[gPartyMenu.slotId2] + ); + + // create the string describing the move + if (item2 == ITEM_NONE) + { + GetMonNickname(&gPlayerParty[gPartyMenu.slotId2], gStringVar1); + CopyItemName(item1, gStringVar2); + StringExpandPlaceholders(gStringVar4, gText_PkmnWasGivenItem); + } + else + { + GetMonNickname(&gPlayerParty[gPartyMenu.slotId], gStringVar1); + CopyItemName(item1, gStringVar2); + StringExpandPlaceholders(buffer, gText_XsYAnd); + + StringAppend(buffer, gText_XsYWereSwapped); + GetMonNickname(&gPlayerParty[gPartyMenu.slotId2], gStringVar1); + CopyItemName(item2, gStringVar2); + StringExpandPlaceholders(gStringVar4, buffer); + } + + // display the string + DisplayPartyMenuMessage(gStringVar4, TRUE); + + // update colors of selected boxes + AnimatePartySlot(gPartyMenu.slotId2, 0); + AnimatePartySlot(gPartyMenu.slotId, 1); + + // return to the main party menu + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = Task_UpdateHeldItemSprite; + break; + } +} + +void CursorCb_MoveItem(u8 taskId) +{ + struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; + + PlaySE(SE_SELECT); + + // delete old windows + PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); + PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); + + if (GetMonData(mon, MON_DATA_HELD_ITEM) != ITEM_NONE) + { + gPartyMenu.action = PARTY_ACTION_SWITCH; + + // show "Move item to where" in bottom left + DisplayPartyMenuStdMessage(PARTY_MSG_MOVE_ITEM_WHERE); + // update color of first selected box + AnimatePartySlot(gPartyMenu.slotId, 1); + + // set up callback + gPartyMenu.slotId2 = gPartyMenu.slotId; + gTasks[taskId].func = CursorCb_MoveItemCallback; + } + else + { + // create and display string about lack of hold item + GetMonNickname(mon, gStringVar1); + StringExpandPlaceholders(gStringVar4, gText_PkmnNotHolding); + DisplayPartyMenuMessage(gStringVar4, TRUE); + + // return to the main party menu + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = Task_UpdateHeldItemSprite; + } +} \ No newline at end of file diff --git a/src/pokemon.c b/src/pokemon.c index 240dab132286..a827d5ff4f07 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -56,6 +56,7 @@ #include "constants/trainers.h" #include "constants/union_room.h" #include "constants/weather.h" +#include "constants/region_map_sections.h" #define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_9) ? 160 : 220) @@ -5391,6 +5392,10 @@ bool32 IsSpeciesInHoennDex(u16 species) u16 GetBattleBGM(void) { + if (FlagGet(FLAG_SYS_SET_BATTLE_BGM)){ + FlagClear(FLAG_SYS_SET_BATTLE_BGM); + return VarGet(VAR_TEMP_F); + } if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) { switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL)) @@ -5455,7 +5460,10 @@ u16 GetBattleBGM(void) case TRAINER_CLASS_PYRAMID_KING: return MUS_VS_FRONTIER_BRAIN; default: - return MUS_VS_TRAINER; + if (gMapHeader.regionMapSectionId == MAPSEC_ROUTE_102 || FALSE) // add maps with specific music here - Map Based Trainer Battle Music + return MUS_RG_VS_TRAINER; + else + return MUS_VS_TRAINER; } } else diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index 1bcc89244c14..50a12fa9550e 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -53,10 +53,10 @@ // PC main menu options enum { - OPTION_WITHDRAW, - OPTION_DEPOSIT, OPTION_MOVE_MONS, OPTION_MOVE_ITEMS, + OPTION_WITHDRAW, + OPTION_DEPOSIT, OPTION_EXIT, OPTIONS_COUNT }; diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index d47d5d7f2719..5fc05cbf1a1d 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -320,6 +320,7 @@ static void DestroyMoveSelectorSprites(u8); static void SetMainMoveSelectorColor(u8); static void KeepMoveSelectorVisible(u8); static void SummaryScreen_DestroyAnimDelayTask(void); +static void BufferIvOrEvStats(u8 mode); static const struct BgTemplate sBgTemplates[] = { @@ -731,6 +732,7 @@ static void (*const sTextPrinterTasks[])(u8 taskId) = static const u8 sMemoNatureTextColor[] = _("{COLOR LIGHT_RED}{SHADOW GREEN}"); static const u8 sMemoMiscTextColor[] = _("{COLOR WHITE}{SHADOW DARK_GRAY}"); // This is also affected by palettes, apparently static const u8 sStatsLeftColumnLayout[] = _("{DYNAMIC 0}/{DYNAMIC 1}\n{DYNAMIC 2}\n{DYNAMIC 3}"); +static const u8 sStatsLeftColumnLayoutIVEV[] = _("{DYNAMIC 0}\n{DYNAMIC 1}\n{DYNAMIC 2}"); static const u8 sStatsRightColumnLayout[] = _("{DYNAMIC 0}\n{DYNAMIC 1}\n{DYNAMIC 2}"); static const u8 sMovesPPLayout[] = _("{PP}{DYNAMIC 0}/{DYNAMIC 1}"); @@ -1633,6 +1635,28 @@ static void Task_HandleInput(u8 taskId) PlaySE(SE_SELECT); BeginCloseSummaryScreen(taskId); } + // show IVs/EVs/stats on button presses + else if (JOY_NEW(R_BUTTON)) + { + if (sMonSummaryScreen->currPageIndex == PSS_PAGE_SKILLS) + { + BufferIvOrEvStats(0); + } + } + else if (JOY_NEW(L_BUTTON)) + { + if (sMonSummaryScreen->currPageIndex == PSS_PAGE_SKILLS) + { + BufferIvOrEvStats(1); + } + } + else if (JOY_NEW(START_BUTTON)) + { + if (sMonSummaryScreen->currPageIndex == PSS_PAGE_SKILLS) + { + BufferIvOrEvStats(2); + } + } #if DEBUG_POKEMON_MENU == TRUE else if (JOY_NEW(SELECT_BUTTON) && !gMain.inBattle) { @@ -1766,28 +1790,22 @@ static void Task_ChangeSummaryMon(u8 taskId) static s8 AdvanceMonIndex(s8 delta) { struct Pokemon *mon = sMonSummaryScreen->monList.mons; + u8 index = sMonSummaryScreen->curMonIndex; + u8 numMons = sMonSummaryScreen->maxMonIndex + 1; + delta += numMons; - if (sMonSummaryScreen->currPageIndex == PSS_PAGE_INFO) - { - if (delta == -1 && sMonSummaryScreen->curMonIndex == 0) - return -1; - else if (delta == 1 && sMonSummaryScreen->curMonIndex >= sMonSummaryScreen->maxMonIndex) - return -1; - else - return sMonSummaryScreen->curMonIndex + delta; - } - else - { - s8 index = sMonSummaryScreen->curMonIndex; + index = (index + delta) % numMons; - do - { - index += delta; - if (index < 0 || index > sMonSummaryScreen->maxMonIndex) - return -1; - } while (GetMonData(&mon[index], MON_DATA_IS_EGG)); + // skip over any Eggs unless on the Info Page + if (sMonSummaryScreen->currPageIndex != PSS_PAGE_INFO) + while (GetMonData(&mon[index], MON_DATA_IS_EGG)) + index = (index + delta) % numMons; + + // to avoid "scrolling" to the same Pokemon + if (index == sMonSummaryScreen->curMonIndex) + return -1; + else return index; - } } static s8 AdvanceMultiBattleMonIndex(s8 delta) @@ -3475,6 +3493,84 @@ static void BufferStat(u8 *dst, s8 natureMod, u32 stat, u32 strId, u32 n) DynamicPlaceholderTextUtil_SetPlaceholderPtr(strId, dst); } +static void BufferIvOrEvStats(u8 mode) +{ + u16 hp, hp2, atk, def, spA, spD, spe; + u8 *currHPString = Alloc(20); + const s8 *natureMod = gNatureStatTable[sMonSummaryScreen->summary.nature]; + + switch (mode) + { + case 0: // iv mode + hp = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_HP_IV); + atk = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_ATK_IV); + def = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_DEF_IV); + + spA = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_SPATK_IV); + spD = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_SPDEF_IV); + spe = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_SPEED_IV); + break; + case 1: // ev mode + hp = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_HP_EV); + atk = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_ATK_EV); + def = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_DEF_EV); + + spA = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_SPATK_EV); + spD = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_SPDEF_EV); + spe = GetMonData(&sMonSummaryScreen->currentMon, MON_DATA_SPEED_EV); + break; + case 2: // stats mode + default: + hp = sMonSummaryScreen->summary.currentHP; + hp2 = sMonSummaryScreen->summary.maxHP; + atk = sMonSummaryScreen->summary.atk; + def = sMonSummaryScreen->summary.def; + + spA = sMonSummaryScreen->summary.spatk; + spD = sMonSummaryScreen->summary.spdef; + spe = sMonSummaryScreen->summary.speed; + break; + } + + FillWindowPixelBuffer(sMonSummaryScreen->windowIds[PSS_DATA_WINDOW_SKILLS_STATS_LEFT], 0); + FillWindowPixelBuffer(sMonSummaryScreen->windowIds[PSS_DATA_WINDOW_SKILLS_STATS_RIGHT], 0); + + switch (mode) + { + case 0: + case 1: + BufferStat(gStringVar1, 0, hp, 0, 7); + BufferStat(gStringVar2, 0, atk, 1, 7); + BufferStat(gStringVar3, 0, def, 2, 7); + DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, sStatsLeftColumnLayoutIVEV); + PrintLeftColumnStats(); + + BufferStat(gStringVar1, 0, spA, 0, 3); + BufferStat(gStringVar2, 0, spD, 1, 3); + BufferStat(gStringVar3, 0, spe, 2, 3); + DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, sStatsRightColumnLayout); + PrintRightColumnStats(); + break; + case 2: + default: + BufferStat(currHPString, 0, hp, 0, 3); + BufferStat(gStringVar1, 0, hp2, 1, 3); + BufferStat(gStringVar2, natureMod[STAT_ATK - 1], atk, 2, 7); + BufferStat(gStringVar3, natureMod[STAT_DEF - 1], def, 3, 7); + DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, sStatsLeftColumnLayout); + PrintLeftColumnStats(); + + BufferStat(gStringVar1, natureMod[STAT_SPATK - 1], spA, 0, 3); + BufferStat(gStringVar2, natureMod[STAT_SPDEF - 1], spD, 1, 3); + BufferStat(gStringVar3, natureMod[STAT_SPEED - 1], spe, 2, 3); + DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, sStatsRightColumnLayout); + PrintRightColumnStats(); + break; + } + + Free(currHPString); +} + static void BufferLeftColumnStats(void) { u8 *currentHPString = Alloc(20); diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 31e40fa31a6f..86eed1914372 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -446,12 +446,12 @@ void ScrCmd_givemon(struct ScriptContext *ctx) u8 speedEv = PARSE_FLAG(8, 0); u8 spAtkEv = PARSE_FLAG(9, 0); u8 spDefEv = PARSE_FLAG(10, 0); - u8 hpIv = PARSE_FLAG(11, Random() % MAX_PER_STAT_IVS + 1); - u8 atkIv = PARSE_FLAG(12, Random() % MAX_PER_STAT_IVS + 1); - u8 defIv = PARSE_FLAG(13, Random() % MAX_PER_STAT_IVS + 1); - u8 speedIv = PARSE_FLAG(14, Random() % MAX_PER_STAT_IVS + 1); - u8 spAtkIv = PARSE_FLAG(15, Random() % MAX_PER_STAT_IVS + 1); - u8 spDefIv = PARSE_FLAG(16, Random() % MAX_PER_STAT_IVS + 1); + u8 hpIv = PARSE_FLAG(11, Random() % (MAX_PER_STAT_IVS + 1)); + u8 atkIv = PARSE_FLAG(12, Random() % (MAX_PER_STAT_IVS + 1)); + u8 defIv = PARSE_FLAG(13, Random() % (MAX_PER_STAT_IVS + 1)); + u8 speedIv = PARSE_FLAG(14, Random() % (MAX_PER_STAT_IVS + 1)); + u8 spAtkIv = PARSE_FLAG(15, Random() % (MAX_PER_STAT_IVS + 1)); + u8 spDefIv = PARSE_FLAG(16, Random() % (MAX_PER_STAT_IVS + 1)); u16 move1 = PARSE_FLAG(17, MOVE_NONE); u16 move2 = PARSE_FLAG(18, MOVE_NONE); u16 move3 = PARSE_FLAG(19, MOVE_NONE); diff --git a/src/shop.c b/src/shop.c old mode 100644 new mode 100755 index 88d66a43e9fd..6178e71f48c5 --- a/src/shop.c +++ b/src/shop.c @@ -4,6 +4,7 @@ #include "decompress.h" #include "decoration.h" #include "decoration_inventory.h" +#include "event_data.h" #include "event_object_movement.h" #include "field_player_avatar.h" #include "field_screen_effect.h" @@ -155,6 +156,179 @@ static void Task_HandleShopMenuSell(u8 taskId); static void BuyMenuPrintItemDescriptionAndShowItemIcon(s32 item, bool8 onInit, struct ListMenu *list); static void BuyMenuPrintPriceInList(u8 windowId, u32 itemId, u8 y); +//mart badges +static const u16 sShopInventory_ZeroBadges[] = { + ITEM_POKE_BALL, + ITEM_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_REVIVE, + ITEM_NONE +}; + +static const u16 sShopInventory_OneBadge[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_REVIVE, + ITEM_NONE +}; + +static const u16 sShopInventory_TwoBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_REVIVE, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 sShopInventory_ThreeBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_REVIVE, + ITEM_REPEL, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 sShopInventory_FourBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_HYPER_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_REVIVE, + ITEM_REPEL, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 sShopInventory_FiveBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_ULTRA_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_HYPER_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_REVIVE, + ITEM_REPEL, + ITEM_SUPER_REPEL, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 sShopInventory_SixBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_ULTRA_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_HYPER_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_FULL_HEAL, + ITEM_REVIVE, + ITEM_REPEL, + ITEM_SUPER_REPEL, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 sShopInventory_SevenBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_ULTRA_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_HYPER_POTION, + ITEM_MAX_POTION, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_FULL_HEAL, + ITEM_REVIVE, + ITEM_REPEL, + ITEM_SUPER_REPEL, + ITEM_MAX_REPEL, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 sShopInventory_EightBadges[] = { + ITEM_POKE_BALL, + ITEM_GREAT_BALL, + ITEM_ULTRA_BALL, + ITEM_POTION, + ITEM_SUPER_POTION, + ITEM_HYPER_POTION, + ITEM_MAX_POTION, + ITEM_FULL_RESTORE, + ITEM_ANTIDOTE, + ITEM_BURN_HEAL, + ITEM_ICE_HEAL, + ITEM_AWAKENING, + ITEM_PARALYZE_HEAL, + ITEM_FULL_HEAL, + ITEM_REVIVE, + ITEM_REPEL, + ITEM_SUPER_REPEL, + ITEM_MAX_REPEL, + ITEM_POKE_DOLL, + ITEM_NONE +}; + +static const u16 *const sShopInventories[] = +{ + sShopInventory_ZeroBadges, + sShopInventory_OneBadge, + sShopInventory_TwoBadges, + sShopInventory_ThreeBadges, + sShopInventory_FourBadges, + sShopInventory_FiveBadges, + sShopInventory_SixBadges, + sShopInventory_SevenBadges, + sShopInventory_EightBadges +}; + + static const struct YesNoFuncTable sShopPurchaseYesNoFuncs = { BuyMenuTryMakePurchase, @@ -374,13 +548,31 @@ static void SetShopMenuCallback(void (* callback)(void)) sMartInfo.callback = callback; } +static u8 GetNumberOfBadges(void) +{ + u16 badgeFlag; + u8 count = 0; + + for (badgeFlag = FLAG_BADGE01_GET; badgeFlag < FLAG_BADGE01_GET + NUM_BADGES; badgeFlag++) + { + if (FlagGet(badgeFlag)) + count++; + } + + return count; +} + static void SetShopItemsForSale(const u16 *items) { u16 i = 0; + u8 badgeCount = GetNumberOfBadges(); - sMartInfo.itemList = items; - sMartInfo.itemCount = 0; + if (items == NULL) + sMartInfo.itemList = sShopInventories[badgeCount]; + else + sMartInfo.itemList = items; + sMartInfo.itemCount = 0; // Read items until ITEM_NONE / DECOR_NONE is reached while (sMartInfo.itemList[i]) { @@ -628,7 +820,7 @@ static void BuyMenuPrintPriceInList(u8 windowId, u32 itemId, u8 y) gStringVar1, ItemId_GetPrice(itemId) >> IsPokeNewsActive(POKENEWS_SLATEPORT), STR_CONV_MODE_LEFT_ALIGN, - 5); + 6); } else { @@ -636,7 +828,7 @@ static void BuyMenuPrintPriceInList(u8 windowId, u32 itemId, u8 y) gStringVar1, gDecorations[itemId].price, STR_CONV_MODE_LEFT_ALIGN, - 5); + 6); } if (ItemId_GetImportance(itemId) && (CheckBagHasItem(itemId, 1) || CheckPCHasItem(itemId, 1))) @@ -1298,3 +1490,5 @@ void CreateDecorationShop2Menu(const u16 *itemsForSale) SetShopItemsForSale(itemsForSale); SetShopMenuCallback(ScriptContext_Enable); } + + diff --git a/src/start_menu.c b/src/start_menu.c index 0525ccf70d7c..eba2906df5c8 100644 --- a/src/start_menu.c +++ b/src/start_menu.c @@ -121,7 +121,6 @@ static u8 SaveYesNoCallback(void); static u8 SaveConfirmInputCallback(void); static u8 SaveFileExistsCallback(void); static u8 SaveConfirmOverwriteDefaultNoCallback(void); -static u8 SaveConfirmOverwriteCallback(void); static u8 SaveOverwriteInputCallback(void); static u8 SaveSavingMessageCallback(void); static u8 SaveDoSaveCallback(void); @@ -1087,7 +1086,7 @@ static u8 SaveFileExistsCallback(void) } else { - ShowSaveMessage(gText_AlreadySavedFile, SaveConfirmOverwriteCallback); + sSaveDialogCallback = SaveSavingMessageCallback; } return SAVE_IN_PROGRESS; @@ -1100,12 +1099,6 @@ static u8 SaveConfirmOverwriteDefaultNoCallback(void) return SAVE_IN_PROGRESS; } -static u8 SaveConfirmOverwriteCallback(void) -{ - DisplayYesNoMenuDefaultYes(); // Show Yes/No menu - sSaveDialogCallback = SaveOverwriteInputCallback; - return SAVE_IN_PROGRESS; -} static u8 SaveOverwriteInputCallback(void) { diff --git a/src/strings.c b/src/strings.c index 46067a7fe83b..505ccad91ca2 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1849,3 +1849,9 @@ const u8 gText_ExpShareOff[] = _("The Exp. Share has been turned off.{PAUSE_UNTI const u8 gText_BasePointsResetToZero[] = _("{STR_VAR_1}'s base points\nwere all reset to zero!{PAUSE_UNTIL_PRESS}"); const u8 gText_Fertilize[] = _("FERTILIZE"); const u8 gText_PlantBerry[] = _("PLANT BERRY"); + +// Move Item +const u8 gMenuText_Move[] = _("Move"); +const u8 gText_MoveItemWhere[] = _("Move item to where?"); +const u8 gText_XsYAnd[] = _("{STR_VAR_1}'s\n{STR_VAR_2} and\l"); +const u8 gText_XsYWereSwapped[] = _("{STR_VAR_1}'s\l{STR_VAR_2} were swapped!{PAUSE_UNTIL_PRESS}"); \ No newline at end of file diff --git a/test/battle/ability/lightning_rod.c b/test/battle/ability/lightning_rod.c new file mode 100644 index 000000000000..33dd27a6fbf5 --- /dev/null +++ b/test/battle/ability/lightning_rod.c @@ -0,0 +1,73 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Lightning Rod absorbs Electric-type moves and increases the Sp. Attack [Gen5+]") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); } + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent); + }; + ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Raichu's Sp. Atk rose!"); + } else { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Raichu's Sp. Atk rose!"); + }; + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} + +DOUBLE_BATTLE_TEST("Lightning Rod forces single-target Electric-type moves to target the Pokémon with this Ability.") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentRight); + MOVE(playerRight, MOVE_THUNDERBOLT, target: opponentRight); + MOVE(opponentLeft, MOVE_CELEBRATE); + MOVE(opponentRight, MOVE_CELEBRATE); + } + } SCENE { + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) { + NONE_OF { + HP_BAR(opponentLeft); + HP_BAR(opponentRight); + }; + ABILITY_POPUP(opponentLeft, ABILITY_LIGHTNING_ROD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Raichu's Sp. Atk rose!"); + ABILITY_POPUP(opponentLeft, ABILITY_LIGHTNING_ROD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Raichu's Sp. Atk rose!"); + } else { + NONE_OF { + HP_BAR(opponentRight); + }; + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, playerLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, playerRight); + HP_BAR(opponentLeft); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} diff --git a/test/battle/ability/opportunist.c b/test/battle/ability/opportunist.c index f2a9f5a37a90..b627b8ec6ea7 100644 --- a/test/battle/ability/opportunist.c +++ b/test/battle/ability/opportunist.c @@ -16,16 +16,16 @@ SINGLE_BATTLE_TEST("Opportunist only copies foe's positive stat changes in a tur OPPONENT(SPECIES_ESPATHRA) { Speed(5); Ability(ability); } } WHEN { TURN { MOVE(player, MOVE_SHELL_SMASH); } - TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(opponent, MOVE_TACKLE); } } SCENE { if (ability == ABILITY_FRISK) { ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); HP_BAR(player, captureDamage: &results[i].damage); } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); HP_BAR(player, captureDamage: &results[i].damage); - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); } } FINALLY { EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); @@ -78,12 +78,10 @@ DOUBLE_BATTLE_TEST("Opportunist raises Attack only once when partner has Intimid if ((abilityLeft == ABILITY_CONTRARY && abilityRight != ABILITY_CONTRARY) || (abilityLeft != ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY)) { ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); - MESSAGE("Espathra copied its opponent's stat changes!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); MESSAGE("Espathra's Attack rose!"); } else if (abilityLeft == ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY) { ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); - MESSAGE("Espathra copied its opponent's stat changes!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); MESSAGE("Espathra's Attack sharply rose!"); } @@ -124,6 +122,177 @@ SINGLE_BATTLE_TEST("Opportunist does not accumulate opposing mon's stat changes" } } -TO_DO_BATTLE_TEST("Opportunist doesn't copy ally stat increases"); -TO_DO_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist"); -TO_DO_BATTLE_TEST("Opportunist copies foe stat increased gained via Swagger and Flatter"); +SINGLE_BATTLE_TEST("Opportunist copies each stat increase individually from ability and move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); } + OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + } WHEN { + TURN { SWITCH(player, 1); } + TURN { MOVE(player, MOVE_SWORDS_DANCE); } + } SCENE { + ABILITY_POPUP(player, ABILITY_INTREPID_SWORD); + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player); + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3); + } +} + +SINGLE_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist") +{ + GIVEN { + PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + } WHEN { + TURN { MOVE(player, MOVE_SWORDS_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player); + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + NOT ABILITY_POPUP(player, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], player->statStages[STAT_ATK]); + } +} + +SINGLE_BATTLE_TEST("Opportunist copies foe stat increase gained via Swagger and Flatter") +{ + GIVEN { + PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + } WHEN { + TURN { MOVE(opponent, MOVE_FLATTER); } + TURN { MOVE(opponent, MOVE_SWAGGER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLATTER, opponent); + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWAGGER, opponent); + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +DOUBLE_BATTLE_TEST("Opportunist doesn't copy ally stat increases") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SWORDS_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, playerLeft); + NOT ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE ); + } +} + +DOUBLE_BATTLE_TEST("Opportunist copies the stat increase of each opposing mon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_SWORDS_DANCE); MOVE(opponentLeft, MOVE_SWORDS_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponentLeft); + ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 4); + } +} + + +DOUBLE_BATTLE_TEST("Opportunist copies the stat of each pokemon that were raised at the same time") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); } + OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_INTREPID_SWORD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + ABILITY_POPUP(opponentRight, ABILITY_INTREPID_SWORD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST); + MESSAGE("Espathra's Attack sharply rose!"); + } THEN { + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Opportunist copies the increase not the stages") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + } WHEN { + TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_CHARM); } + TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_CHARM); } + TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_GROWL); } + TURN { MOVE(player, MOVE_BELLY_DRUM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, opponent); + + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 5); // + 11 + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 6); // + 11 + } +} + +SINGLE_BATTLE_TEST("Opportunist copies the stat increase from the incoming mon") +{ + GIVEN { + PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD); + ABILITY_POPUP(player, ABILITY_OPPORTUNIST); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Opportunist and Mirror Herb stack stat increases") +{ + GIVEN { + PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); } + OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); Item(ITEM_MIRROR_HERB); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(player, ABILITY_INTREPID_SWORD); + ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/ability/own_tempo.c b/test/battle/ability/own_tempo.c index d5a6f58a723c..a37bc0024a83 100644 --- a/test/battle/ability/own_tempo.c +++ b/test/battle/ability/own_tempo.c @@ -56,6 +56,23 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user") } } +SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker") +{ + KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef + GIVEN { + ASSUME(gMovesInfo[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + } SCENE { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Slowpoke's Own Tempo prevents confusion!"); + } + } +} + SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker") { KNOWN_FAILING; diff --git a/test/battle/ability/parental_bond.c b/test/battle/ability/parental_bond.c index 46e71096de41..d262de362d4f 100644 --- a/test/battle/ability/parental_bond.c +++ b/test/battle/ability/parental_bond.c @@ -239,6 +239,30 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil } } +SINGLE_BATTLE_TEST("Parental Bond Smack Down effect triggers after 2nd hit") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SMACK_DOWN].category != DAMAGE_CATEGORY_STATUS); + ASSUME(gMovesInfo[MOVE_SMACK_DOWN].strikeCount < 2); + ASSUME(MoveHasAdditionalEffect(MOVE_SMACK_DOWN, MOVE_EFFECT_SMACK_DOWN)); + PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } + OPPONENT(SPECIES_SKARMORY); + } WHEN { + TURN { MOVE(player, MOVE_SMACK_DOWN, megaEvolve: TRUE); } + } SCENE { + MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player); + MESSAGE("Kangaskhan has Mega Evolved into Mega Kangaskhan!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SMACK_DOWN, player); + HP_BAR(opponent); + NOT MESSAGE("Foe Skarmory fell straight down!"); + HP_BAR(opponent); + MESSAGE("Foe Skarmory fell straight down!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_KANGASKHAN_MEGA); + } +} + TO_DO_BATTLE_TEST("Parental Bond tests"); // Temporary TODO: Convert Bulbapedia description into tests. diff --git a/test/battle/ability/storm_drain.c b/test/battle/ability/storm_drain.c new file mode 100644 index 000000000000..38d4a9be7595 --- /dev/null +++ b/test/battle/ability/storm_drain.c @@ -0,0 +1,73 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Storm Drain absorbs Water-type moves and increases the Sp. Attack [Gen5+]") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GASTRODON_EAST_SEA) { Ability(ABILITY_STORM_DRAIN); } + } WHEN { + TURN { MOVE(player, MOVE_WATER_GUN); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player); + HP_BAR(opponent); + }; + ABILITY_POPUP(opponent, ABILITY_STORM_DRAIN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Gastrodon's Sp. Atk rose!"); + } else { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_STORM_DRAIN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Gastrodon's Sp. Atk rose!"); + }; + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player); + HP_BAR(opponent); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} + +DOUBLE_BATTLE_TEST("Storm Drain forces single-target Water-type moves to target the Pokémon with this Ability.") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GASTRODON_EAST_SEA) { Ability(ABILITY_STORM_DRAIN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(playerLeft, MOVE_WATER_GUN, target: opponentRight); + MOVE(playerRight, MOVE_WATER_GUN, target: opponentRight); + MOVE(opponentLeft, MOVE_CELEBRATE); + MOVE(opponentRight, MOVE_CELEBRATE); + } + } SCENE { + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) { + NONE_OF { + HP_BAR(opponentLeft); + HP_BAR(opponentRight); + }; + ABILITY_POPUP(opponentLeft, ABILITY_STORM_DRAIN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Gastrodon's Sp. Atk rose!"); + ABILITY_POPUP(opponentLeft, ABILITY_STORM_DRAIN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Gastrodon's Sp. Atk rose!"); + } else { + NONE_OF { + HP_BAR(opponentRight); + }; + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerRight); + HP_BAR(opponentLeft); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} diff --git a/test/battle/ability/trace.c b/test/battle/ability/trace.c index bb72f38860bf..acc49bcf1398 100644 --- a/test/battle/ability/trace.c +++ b/test/battle/ability/trace.c @@ -79,3 +79,20 @@ SINGLE_BATTLE_TEST("Trace will copy an opponent's ability whenever it has the ch MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); } } + +DOUBLE_BATTLE_TEST("Trace respects the turn order") +{ + GIVEN { + PLAYER(SPECIES_DEOXYS_SPEED) { Speed(40); Ability(ABILITY_PRESSURE); } + PLAYER(SPECIES_GARDEVOIR) { Speed(20); Ability(ABILITY_TRACE); } + OPPONENT(SPECIES_HIPPOWDON) { Speed(10); Ability(ABILITY_SAND_STREAM); } + OPPONENT(SPECIES_DEOXYS_SPEED) { Speed(30); Ability(ABILITY_PRESSURE); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_PRESSURE); + ABILITY_POPUP(opponentRight, ABILITY_PRESSURE); + ABILITY_POPUP(playerRight, ABILITY_TRACE); + ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM); + } +} diff --git a/test/battle/ai.c b/test/battle/ai.c index 31a354b52c69..578ca345ffd3 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -699,6 +699,19 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Post-KO switches prioritize of } } +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: U-Turn will send out Ace Mon if it's the only one remaining") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_ACE_POKEMON); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_U_TURN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 1); } + } +} + AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI switches out after sufficient stat drops") { GIVEN { @@ -1039,3 +1052,4 @@ AI_SINGLE_BATTLE_TEST("AI avoids contact moves against rocky helmet") TURN { EXPECT_MOVES(opponent, MOVE_LEAFAGE, MOVE_BRANCH_POKE); } } } + diff --git a/test/battle/form_change/status.c b/test/battle/form_change/status.c index f0d5a9f3f026..0b94202bbd0d 100644 --- a/test/battle/form_change/status.c +++ b/test/battle/form_change/status.c @@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("Shaymin-Sky reverts to Shaymin-Land when frozen or frostbitt } SCENE { ANIMATION(ANIM_TYPE_MOVE, move, opponent); if (move == MOVE_POWDER_SNOW) { - STATUS_ICON(player, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(player, TRUE); NOT HP_BAR(player); // Regression caused by Mimikyu form change MESSAGE("Shaymin transformed!"); } else { diff --git a/test/battle/hold_effect/cure_status.c b/test/battle/hold_effect/cure_status.c index 37b88c4d07ea..79ab1b4e3ca1 100644 --- a/test/battle/hold_effect/cure_status.c +++ b/test/battle/hold_effect/cure_status.c @@ -72,7 +72,7 @@ SINGLE_BATTLE_TEST("Rawst and Lum Berries cure burn") } } -SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze") +SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze or frostbite") { u16 item; @@ -88,9 +88,9 @@ SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze") } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_PUNCH, player); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); - STATUS_ICON(opponent, freeze: FALSE); + FREEZE_OR_FROSTBURN_STATUS(opponent, FALSE); } } diff --git a/test/battle/hold_effect/mirror_herb.c b/test/battle/hold_effect/mirror_herb.c index 135ec270b3c1..52326cc60fa0 100644 --- a/test/battle/hold_effect/mirror_herb.c +++ b/test/battle/hold_effect/mirror_herb.c @@ -73,3 +73,18 @@ DOUBLE_BATTLE_TEST("Mirror Herb does not trigger for Ally's Soul Heart's stat ra EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); } } + +SINGLE_BATTLE_TEST("Mirror Herb copies the boost gained by an ability") +{ + GIVEN { + PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(player, ABILITY_INTREPID_SWORD); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + } +} diff --git a/test/battle/move_effect/flame_burst.c b/test/battle/move_effect/flame_burst.c new file mode 100644 index 000000000000..d222325a7b69 --- /dev/null +++ b/test/battle/move_effect/flame_burst.c @@ -0,0 +1,24 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_FLAME_BURST].additionalEffects->moveEffect == MOVE_EFFECT_FLAME_BURST); +} + +// Flame Burst AoE is supposed to hit through Substitute +DOUBLE_BATTLE_TEST("Flame Burst Substitute") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SUBSTITUTE); MOVE(playerRight, MOVE_FLAME_BURST, target: opponentRight); } + } SCENE { + MESSAGE("The bursting flames hit Foe Wynaut!"); + NOT MESSAGE("The SUBSTITUTE took damage for Foe Wynaut!"); + } +} diff --git a/test/battle/move_effect/ion_deluge.c b/test/battle/move_effect/ion_deluge.c index ab64667b06f3..93e147e07d0d 100644 --- a/test/battle/move_effect/ion_deluge.c +++ b/test/battle/move_effect/ion_deluge.c @@ -41,7 +41,7 @@ WILD_BATTLE_TEST("Ion Deluge works the same way as always when used by a mon wit NONE_OF { ABILITY_POPUP(opponent, ability); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - MESSAGE("Wild Zebstrika's Sp.Atk rose!"); + MESSAGE("Wild Zebstrika's Sp. Atk rose!"); MESSAGE("Wild Zebstrika's Speed rose!"); } MESSAGE("A deluge of ions showers the battlefield!"); diff --git a/test/battle/move_effect/sleep_talk.c b/test/battle/move_effect/sleep_talk.c new file mode 100644 index 000000000000..0a42eb90e921 --- /dev/null +++ b/test/battle/move_effect/sleep_talk.c @@ -0,0 +1,93 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SLEEP_TALK].effect == EFFECT_SLEEP_TALK); + ASSUME(gMovesInfo[MOVE_RAZOR_WIND].sleepTalkBanned == TRUE); + ASSUME(gMovesInfo[MOVE_FLY].sleepTalkBanned == TRUE); + ASSUME(gMovesInfo[MOVE_DIG].sleepTalkBanned == TRUE); +} + +SINGLE_BATTLE_TEST("Sleep Talk fails if not asleep") +{ + u32 status; + PARAMETRIZE { status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_NONE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(status); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLEEP_TALK); } + } SCENE { + if (status == STATUS1_SLEEP) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + NOT MESSAGE("But it failed!"); + } + else { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + MESSAGE("But it failed!"); + } + } +} + + +SINGLE_BATTLE_TEST("Sleep Talk works if user has Comatose") +{ + + GIVEN { + PLAYER(SPECIES_KOMALA) { Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLEEP_TALK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + NOT MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Sleep Talk fails if no moves work") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_RAZOR_WIND, MOVE_FLY, MOVE_DIG); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLEEP_TALK); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Sleep Talk can still use moves with no PP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); MovesWithPP({MOVE_SLEEP_TALK, 10}, {MOVE_TACKLE, 0}, {MOVE_FLY, 10}, {MOVE_DIG, 10}); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLEEP_TALK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + NOT MESSAGE("But it failed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE); + } +} + +SINGLE_BATTLE_TEST("Sleep Talk can use moves while choiced into Sleep Talk") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_CHOICE_BAND); Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_FLY, MOVE_DIG); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLEEP_TALK); } + TURN { MOVE(player, MOVE_SLEEP_TALK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + NOT MESSAGE("But it failed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player); + NOT MESSAGE("But it failed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE); + } +} diff --git a/test/battle/move_effect/toxic.c b/test/battle/move_effect/toxic.c index 35bfaa064486..804ed56b8f75 100644 --- a/test/battle/move_effect/toxic.c +++ b/test/battle/move_effect/toxic.c @@ -48,3 +48,21 @@ SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type") } } } + +AI_SINGLE_BATTLE_TEST("AI avoids toxic when it can not poison target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_SNORLAX; ability = ABILITY_IMMUNITY; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_BULBASAUR; ability = ABILITY_OVERGROW; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TOXIC); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_TOXIC); } // Both get -10 + } +} diff --git a/test/battle/move_effect/upper_hand.c b/test/battle/move_effect/upper_hand.c index 04f2b8522565..6a041182fe5f 100644 --- a/test/battle/move_effect/upper_hand.c +++ b/test/battle/move_effect/upper_hand.c @@ -116,3 +116,19 @@ SINGLE_BATTLE_TEST("Upper Hand is boosted by Sheer Force") HP_BAR(player); } } + +AI_SINGLE_BATTLE_TEST("AI won't use Upper Hand unless it has seen a priority move") +{ + u16 move; + PARAMETRIZE { move = MOVE_TACKLE; } + PARAMETRIZE { move = MOVE_QUICK_ATTACK; } + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1); + PLAYER(SPECIES_WOBBUFFET) {Moves(move); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_UPPER_HAND, MOVE_KARATE_CHOP); } + } WHEN { + TURN { MOVE(player, move); EXPECT_MOVE(opponent, MOVE_KARATE_CHOP); } + TURN { MOVE(player, move); EXPECT_MOVE(opponent, move == MOVE_QUICK_ATTACK ? MOVE_UPPER_HAND : MOVE_KARATE_CHOP); } + } +} diff --git a/test/battle/move_effect_secondary/dire_claw.c b/test/battle/move_effect_secondary/dire_claw.c index 67c361bac0eb..35c6e73d94b3 100644 --- a/test/battle/move_effect_secondary/dire_claw.c +++ b/test/battle/move_effect_secondary/dire_claw.c @@ -67,7 +67,8 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep pokemo u8 statusAnim; u16 species, ability; u32 rng; - PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; } + if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) + PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = MOVE_EFFECT_POISON; species = SPECIES_ZANGOOSE; ability = ABILITY_IMMUNITY; } diff --git a/test/battle/move_effect_secondary/freeze.c b/test/battle/move_effect_secondary/freeze.c index d7a182b3d9c4..1539c8a25b76 100644 --- a/test/battle/move_effect_secondary/freeze.c +++ b/test/battle/move_effect_secondary/freeze.c @@ -7,7 +7,11 @@ ASSUMPTIONS ASSUME(gMovesInfo[MOVE_BLIZZARD].accuracy == 70); } +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Powder Snow inflicts frostbite") +#else SINGLE_BATTLE_TEST("Powder Snow inflicts freeze") +#endif { GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -18,11 +22,15 @@ SINGLE_BATTLE_TEST("Powder Snow inflicts freeze") ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER_SNOW, player); HP_BAR(opponent); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } } +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Powder Snow cannot frostbite an Ice-type Pokémon") +#else SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type Pokémon") +#endif { GIVEN { ASSUME(gSpeciesInfo[SPECIES_SNORUNT].types[0] == TYPE_ICE); @@ -35,7 +43,7 @@ SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type Pokémon") HP_BAR(opponent); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } } } @@ -68,10 +76,18 @@ SINGLE_BATTLE_TEST("Blizzard bypasses accuracy checks in Hail and Snow") } #if B_STATUS_TYPE_IMMUNITY > GEN_1 +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Freezing Glare should frostbite Psychic-types") +#else SINGLE_BATTLE_TEST("Freezing Glare should freeze Psychic-types") +#endif +#else +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types") #else SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types") #endif +#endif { GIVEN { ASSUME(gSpeciesInfo[SPECIES_ARTICUNO_GALARIAN].types[0] == TYPE_PSYCHIC); @@ -86,11 +102,11 @@ SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types") HP_BAR(opponent); #if B_STATUS_TYPE_IMMUNITY > GEN_1 ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); #else NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } #endif } diff --git a/test/battle/move_effect_secondary/tri_attack.c b/test/battle/move_effect_secondary/tri_attack.c index 97eaa76ad876..e84573dcdf5b 100644 --- a/test/battle/move_effect_secondary/tri_attack.c +++ b/test/battle/move_effect_secondary/tri_attack.c @@ -6,7 +6,11 @@ ASSUMPTIONS ASSUME(MoveHasAdditionalEffect(MOVE_TRI_ATTACK, MOVE_EFFECT_TRI_ATTACK) == TRUE); } +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or frostbite") +#else SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze") +#endif { u8 statusAnim; PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; } @@ -26,14 +30,18 @@ SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze") if (statusAnim == B_ANIM_STATUS_BRN) { STATUS_ICON(opponent, burn: TRUE); } else if (statusAnim == B_ANIM_STATUS_FRZ) { - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } else if (statusAnim == B_ANIM_STATUS_PRZ) { STATUS_ICON(opponent, paralysis: TRUE); } } } +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite electric/fire/ice types respectively") +#else SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice types respectively") +#endif { u8 statusAnim; u16 species; @@ -42,7 +50,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; } #endif // B_PARALYZE_ELECTRIC PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_ARCANINE; } - PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_GLALIE; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(species); @@ -57,7 +65,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ if (statusAnim == B_ANIM_STATUS_BRN) { STATUS_ICON(opponent, burn: TRUE); } else if (statusAnim == B_ANIM_STATUS_FRZ) { - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } else if (statusAnim == B_ANIM_STATUS_PRZ) { STATUS_ICON(opponent, paralysis: TRUE); } @@ -65,7 +73,11 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ } } +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite pokemon with abilities preventing respective statuses") +#else SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilities preventing respective statuses") +#endif { u8 statusAnim; u16 species, ability; @@ -75,8 +87,8 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_SEAKING; ability = ABILITY_WATER_VEIL; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } - PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; } - PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -92,7 +104,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie if (statusAnim == B_ANIM_STATUS_BRN) { STATUS_ICON(opponent, burn: TRUE); } else if (statusAnim == B_ANIM_STATUS_FRZ) { - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } else if (statusAnim == B_ANIM_STATUS_PRZ) { STATUS_ICON(opponent, paralysis: TRUE); } @@ -100,13 +112,17 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie } } +#if B_USE_FROSTBITE == TRUE +SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite a mon which is already statused") +#else SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is already statused") +#endif { u8 statusAnim; u32 rng; PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; } - PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; } + PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } @@ -121,7 +137,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is alread if (statusAnim == B_ANIM_STATUS_BRN) { STATUS_ICON(opponent, burn: TRUE); } else if (statusAnim == B_ANIM_STATUS_FRZ) { - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } else if (statusAnim == B_ANIM_STATUS_PRZ) { STATUS_ICON(opponent, paralysis: TRUE); } diff --git a/test/battle/move_effects_combined/flinch_status.c b/test/battle/move_effects_combined/flinch_status.c index 6ef9e09bf1dc..bad28eac4e7a 100644 --- a/test/battle/move_effects_combined/flinch_status.c +++ b/test/battle/move_effects_combined/flinch_status.c @@ -5,7 +5,7 @@ ASSUMPTIONS { ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_FANG, MOVE_EFFECT_PARALYSIS) == TRUE); ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_FANG, MOVE_EFFECT_FLINCH) == TRUE); - ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FREEZE) == TRUE); + ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FREEZE_OR_FROSTBITE) == TRUE); ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FLINCH) == TRUE); ASSUME(MoveHasAdditionalEffect(MOVE_FIRE_FANG, MOVE_EFFECT_BURN) == TRUE); ASSUME(MoveHasAdditionalEffect(MOVE_FIRE_FANG, MOVE_EFFECT_FLINCH) == TRUE); @@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Thunder, Ice and Fire Fang inflict status 10% of the time") STATUS_ICON(opponent, paralysis: TRUE); } if (move == MOVE_ICE_FANG) { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); - STATUS_ICON(opponent, freeze: TRUE); + FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } if (move == MOVE_FIRE_FANG) { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); STATUS_ICON(opponent, burn: TRUE); diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c index c49bd7a3f250..4da40589fb54 100644 --- a/test/battle/status1/burn.c +++ b/test/battle/status1/burn.c @@ -35,3 +35,23 @@ SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage) EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); } } + +AI_SINGLE_BATTLE_TEST("AI avoids Will-o-Wisp when it can not burn target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_BUIZEL; ability = ABILITY_WATER_VEIL; } + PARAMETRIZE { species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_ARCTIBAX; ability = ABILITY_THERMAL_EXCHANGE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_CHARMANDER; ability = ABILITY_BLAZE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_WILL_O_WISP); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_WILL_O_WISP); } // Both get -10 + } +} diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c index b6edaf26352f..558ce4fc3752 100644 --- a/test/battle/status1/paralysis.c +++ b/test/battle/status1/paralysis.c @@ -42,3 +42,21 @@ SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") MESSAGE("Wobbuffet is paralyzed! It can't move!"); } } + +AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_HITMONLEE; ability = ABILITY_LIMBER; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10 + } +} diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c index b3dd403eb1e8..f42f4bd71411 100644 --- a/test/battle/status1/sleep.c +++ b/test/battle/status1/sleep.c @@ -21,3 +21,21 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move") MESSAGE("Wobbuffet used Celebrate!"); } } + +AI_SINGLE_BATTLE_TEST("AI avoids hypnosis when it can not put target to sleep") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_HOOTHOOT; ability = ABILITY_INSOMNIA; } + PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_VITAL_SPIRIT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_HYPNOSIS); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_HYPNOSIS); } // Both get -10 + } +} diff --git a/test/battle/terrain/grassy.c b/test/battle/terrain/grassy.c index 678352dc5ec1..862c9052fd30 100644 --- a/test/battle/terrain/grassy.c +++ b/test/battle/terrain/grassy.c @@ -85,3 +85,27 @@ SINGLE_BATTLE_TEST("Grassy Terrain lasts for 5 turns") MESSAGE("The grass disappeared from the battlefield."); } } + +SINGLE_BATTLE_TEST("Grassy Terrain heals the pokemon on the field for the duration of the terrain, including last turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); }; + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_GRASSY_TERRAIN); } + TURN {} + TURN {} + TURN {} + TURN {} + } SCENE { + MESSAGE("Foe Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); + MESSAGE("Grass grew to cover the battlefield!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("The grass disappeared from the battlefield."); + } +} diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index 941536094822..d5bc1479f020 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -144,3 +144,50 @@ TEST("ModifyPersonalityForNature can set any nature") ModifyPersonalityForNature(&personality, nature); EXPECT_EQ(GetNatureFromPersonality(personality), nature); } + +static const struct TrainerMon sTestParty2[] = +{ + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, +}; + +static const struct Trainer sTestTrainer2 = +{ + .trainerName = _("Test2"), + .trainerClass = TRAINER_CLASS_BLACK_BELT, + .party = TRAINER_PARTY(sTestParty2), +}; + +TEST("Trainer Class Balls apply to the entire party") +{ + u32 j; + struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon)); + CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer2, TRUE, BATTLE_TYPE_TRAINER); + for(j = 0; j < 6; j++) + { + EXPECT(GetMonData(&testParty[j], MON_DATA_POKEBALL, 0) == gTrainerClasses[sTestTrainer2.trainerClass].ball); + } + Free(testParty); +} diff --git a/test/test_runner.c b/test/test_runner.c index cbe9a6c6e07b..169d2e40619d 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -179,6 +179,7 @@ void CB2_TestRunner(void) } MgbaPrintf_(":N%s", gTestRunnerState.test->name); + MgbaPrintf_(":L%s:%d", gTestRunnerState.test->filename); gTestRunnerState.result = TEST_RESULT_PASS; gTestRunnerState.expectedResult = TEST_RESULT_PASS; gTestRunnerState.expectLeaks = FALSE; @@ -216,6 +217,7 @@ void CB2_TestRunner(void) // NOTE: Assumes that the compiler interns __FILE__. if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) // Assumption fails for tests in this file. { + MgbaPrintf_(":L%s:%d", gTestRunnerState.test->filename, gTestRunnerState.failedAssumptionsBlockLine); gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL; return; } @@ -480,9 +482,10 @@ static void Intr_Timer2(void) } } -void Test_ExitWithResult(enum TestResult result, const char *fmt, ...) +void Test_ExitWithResult(enum TestResult result, u32 stopLine, const char *fmt, ...) { gTestRunnerState.result = result; + gTestRunnerState.failedAssumptionsBlockLine = stopLine; ReinitCallbacks(); if (gTestRunnerState.state == STATE_REPORT_RESULT && gTestRunnerState.result != gTestRunnerState.expectedResult) @@ -690,3 +693,24 @@ void DACSHandle(void) ReinitCallbacks(); DACS_LR = ((uintptr_t)JumpToAgbMainLoop & ~1) + 4; } + +static const struct Test *GetTest(void) +{ + const struct Test *test = gTestRunnerState.test; + return test; +} + +u32 SourceLine(u32 sourceLineOffset) +{ + const struct Test *test = GetTest(); + return test->sourceLine + sourceLineOffset; +} + +u32 SourceLineOffset(u32 sourceLine) +{ + const struct Test *test = GetTest(); + if (sourceLine - test->sourceLine > 0xFF) + return 0; + else + return sourceLine - test->sourceLine; +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 376c3be8a55b..2930c7dd6158 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -25,10 +25,10 @@ #undef TestRunner_Battle_GetForcedAbility #endif -#define INVALID(fmt, ...) Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__) -#define INVALID_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0) +#define INVALID(fmt, ...) Test_ExitWithResult(TEST_RESULT_INVALID, sourceLine, ":L%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__) +#define INVALID_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_INVALID, sourceLine, ":L%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0) -#define ASSUMPTION_FAIL_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, "%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0) +#define ASSUMPTION_FAIL_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, sourceLine, ":L%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0) #define STATE gBattleTestRunnerState #define DATA gBattleTestRunnerState->data @@ -145,21 +145,6 @@ static bool32 IsAITest(void) return FALSE; } -static u32 SourceLine(u32 sourceLineOffset) -{ - const struct BattleTest *test = GetBattleTest(); - return test->sourceLine + sourceLineOffset; -} - -static u32 SourceLineOffset(u32 sourceLine) -{ - const struct BattleTest *test = GetBattleTest(); - if (sourceLine - test->sourceLine > 0xFF) - return 0; - else - return sourceLine - test->sourceLine; -} - static u32 BattleTest_EstimateCost(void *data) { u32 cost; @@ -184,9 +169,9 @@ static void BattleTest_SetUp(void *data) InvokeTestFunction(test); STATE->parameters = STATE->parametersCount; if (STATE->parametersCount == 0 && test->resultsSize > 0) - Test_ExitWithResult(TEST_RESULT_INVALID, "results without PARAMETRIZE"); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":Lresults without PARAMETRIZE"); if (sizeof(*STATE) + test->resultsSize * STATE->parameters > sizeof(sBackupMapData)) - Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE (%d) + STATE->results (%d) too big for sBackupMapData (%d)", sizeof(*STATE), test->resultsSize * STATE->parameters, sizeof(sBackupMapData)); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LOOM: STATE (%d) + STATE->results (%d) too big for sBackupMapData (%d)", sizeof(*STATE), test->resultsSize * STATE->parameters, sizeof(sBackupMapData)); STATE->results = (void *)((char *)sBackupMapData + sizeof(struct BattleTestRunnerState)); memset(STATE->results, 0, test->resultsSize * STATE->parameters); switch (test->type) @@ -260,7 +245,7 @@ static void SetImplicitSpeeds(void) } } if (!madeProgress) - Test_ExitWithResult(TEST_RESULT_INVALID, "TURNs have contradictory speeds"); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LTURNs have contradictory speeds"); } } @@ -334,9 +319,9 @@ static void BattleTest_Run(void *data) requiredOpponentPartySize = DATA.currentMonIndexes[i] + 1; } if (DATA.playerPartySize < requiredPlayerPartySize) - Test_ExitWithResult(TEST_RESULT_INVALID, "%d PLAYER Pokemon required", requiredPlayerPartySize); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%d PLAYER Pokemon required", requiredPlayerPartySize); if (DATA.opponentPartySize < requiredOpponentPartySize) - Test_ExitWithResult(TEST_RESULT_INVALID, "%d OPPONENT Pokemon required", requiredOpponentPartySize); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%d OPPONENT Pokemon required", requiredOpponentPartySize); for (i = 0; i < STATE->battlersCount; i++) PushBattlerAction(0, i, RECORDED_BYTE, 0xFF); @@ -346,7 +331,7 @@ static void BattleTest_Run(void *data) if (DATA.explicitSpeeds[B_SIDE_PLAYER] != (1 << DATA.playerPartySize) - 1 && DATA.explicitSpeeds[B_SIDE_OPPONENT] != (1 << DATA.opponentPartySize) - 1) { - Test_ExitWithResult(TEST_RESULT_INVALID, "Speed required for all PLAYERs and OPPONENTs"); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LSpeed required for all PLAYERs and OPPONENTs"); } } else @@ -392,7 +377,7 @@ u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi) } else if (STATE->trials != n) { - Test_ExitWithResult(TEST_RESULT_ERROR, "RandomUniform called with inconsistent trials %d and %d", STATE->trials, n); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomUniform called with inconsistent trials %d and %d", STATE->trials, n); } STATE->trialRatio = Q_4_12(1) / STATE->trials; return STATE->runTrial + lo; @@ -413,7 +398,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32 if (turn && turn->rng.tag == tag) { if (reject(turn->rng.value)) - Test_ExitWithResult(TEST_RESULT_INVALID, "WITH_RNG specified a rejected value (%d)", turn->rng.value); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LWITH_RNG specified a rejected value (%d)", turn->rng.value); return turn->rng.value; } } @@ -434,7 +419,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32 while (reject(STATE->runTrial + lo + STATE->rngTrialOffset)) { if (STATE->runTrial + lo + STATE->rngTrialOffset > hi) - Test_ExitWithResult(TEST_RESULT_INVALID, "RandomUniformExcept called with inconsistent reject"); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LRandomUniformExcept called with inconsistent reject"); STATE->rngTrialOffset++; } @@ -445,7 +430,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32 while (reject(default_)) { if (default_ == lo) - Test_ExitWithResult(TEST_RESULT_INVALID, "RandomUniformExcept rejected all values"); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LRandomUniformExcept rejected all values"); default_--; } return default_; @@ -456,7 +441,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) const struct BattlerTurn *turn = NULL; if (sum == 0) - Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeightedArray called with zero sum"); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomWeightedArray called with zero sum"); if (gCurrentTurnActionNumber < gBattlersCount) { @@ -475,7 +460,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) } else if (STATE->trials != n) { - Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeighted called with inconsistent trials %d and %d", STATE->trials, n); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomWeighted called with inconsistent trials %d and %d", STATE->trials, n); } // TODO: Detect inconsistent sum. STATE->trialRatio = Q_4_12(weights[STATE->runTrial]) / sum; @@ -509,7 +494,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) while (weights[n-1] == 0) { if (n == 1) - Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeightedArray called with all zero weights"); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomWeightedArray called with all zero weights"); n--; } return n-1; @@ -535,7 +520,7 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz return (const u8 *)array + size * index; } // TODO: Incorporate the line number. - Test_ExitWithResult(TEST_RESULT_ERROR, "%s: RandomElement illegal value requested: %d", gTestRunnerState.test->filename, turn->rng.value); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":L%s: RandomElement illegal value requested: %d", gTestRunnerState.test->filename, turn->rng.value); } } @@ -548,7 +533,7 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz } else if (STATE->trials != count) { - Test_ExitWithResult(TEST_RESULT_ERROR, "RandomElement called with inconsistent trials %d and %d", STATE->trials, count); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomElement called with inconsistent trials %d and %d", STATE->trials, count); } STATE->trialRatio = Q_4_12(1) / count; return (const u8 *)array + size * STATE->runTrial; @@ -599,7 +584,7 @@ void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, u32 ability) { const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset); - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched ABILITY_POPUP", filename, line); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched ABILITY_POPUP", filename, line); } queuedEvent += event->groupSize; @@ -662,7 +647,7 @@ void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId) { const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset); - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched ANIMATION", filename, line); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched ANIMATION", filename, line); } queuedEvent += event->groupSize; @@ -752,7 +737,7 @@ void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP) { const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset); - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched HP_BAR", filename, line); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched HP_BAR", filename, line); } queuedEvent += event->groupSize; @@ -807,10 +792,10 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target) bool32 movePasses = FALSE; if (expectedAction->type != B_ACTION_USE_MOVE) - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected %s, got MOVE", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected %s, got MOVE", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]); if (expectedAction->explicitTarget && expectedAction->target != target) - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected target %s, got %s", filename, expectedAction->sourceLine, BattlerIdentifier(expectedAction->target), BattlerIdentifier(target)); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected target %s, got %s", filename, expectedAction->sourceLine, BattlerIdentifier(expectedAction->target), BattlerIdentifier(target)); for (i = 0; i < MAX_MON_MOVES; i++) { @@ -844,16 +829,16 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target) u32 moveSlot = GetMoveSlot(gBattleMons[battlerId].moves, moveId); PrintAiMoveLog(battlerId, moveSlot, moveId, gBattleStruct->aiFinalScore[battlerId][expectedAction->target][moveSlot]); if (countExpected > 1) - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched EXPECT_MOVES %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId)); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched EXPECT_MOVES %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId)); else - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched EXPECT_MOVE %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId)); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched EXPECT_MOVE %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId)); } if (expectedAction->notMove && !movePasses) { if (countExpected > 1) - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched NOT_EXPECT_MOVES %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId)); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched NOT_EXPECT_MOVES %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId)); else - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched NOT_EXPECT_MOVE %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId)); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched NOT_EXPECT_MOVE %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId)); } } // Turn passed, clear logs from the turn @@ -873,17 +858,17 @@ void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex) if (!expectedAction->pass) { if (expectedAction->type != B_ACTION_SWITCH) - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected %s, got SWITCH/SEND_OUT", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected %s, got SWITCH/SEND_OUT", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]); if (expectedAction->target != partyIndex) - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected partyIndex %d, got %d", filename, expectedAction->sourceLine, expectedAction->target, partyIndex); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected partyIndex %d, got %d", filename, expectedAction->sourceLine, expectedAction->target, partyIndex); } DATA.aiActionsPlayed[battlerId]++; } void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex) { - Test_ExitWithResult(TEST_RESULT_INVALID, "%s: INVALID: %s trying to send out a mon(id: %d) with 0 HP.", + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%s: INVALID: %s trying to send out a mon(id: %d) with 0 HP.", gTestRunnerState.test->filename, BattlerIdentifier(battlerId), gBattlerPartyIndexes[battlerId]); } @@ -933,7 +918,7 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp && (aiAction->moveSlots & gBitTable[i]) && !(aiAction->moveSlots & gBitTable[bestScoreId])) { - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_MOVE %S has the same best score(%d) as not expected MOVE %S", filename, + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: EXPECT_MOVE %S has the same best score(%d) as not expected MOVE %S", filename, aiAction->sourceLine, GetMoveName(moves[i]), scores[i], GetMoveName(moves[bestScoreId])); } // We DO NOT expect move 'i', but it has the same best score as another move. @@ -942,7 +927,7 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp && (aiAction->moveSlots & gBitTable[i]) && !(aiAction->moveSlots & gBitTable[bestScoreId])) { - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: NOT_EXPECT_MOVE %S has the same best score(%d) as MOVE %S", filename, + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: NOT_EXPECT_MOVE %S has the same best score(%d) as MOVE %S", filename, aiAction->sourceLine, GetMoveName(moves[i]), scores[i], GetMoveName(moves[bestScoreId])); } } @@ -985,7 +970,7 @@ static void PrintAiMoveLog(u32 battlerId, u32 moveSlot, u32 moveId, s32 totalSco } if (scoreFromLogs != totalScore) { - Test_ExitWithResult(TEST_RESULT_ERROR, "Warning! Score from logs(%d) is different than actual score(%d). Make sure all of the score adjustments use the ADJUST_SCORE macro\n", scoreFromLogs, totalScore); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LWarning! Score from logs(%d) is different than actual score(%d). Make sure all of the score adjustments use the ADJUST_SCORE macro\n", scoreFromLogs, totalScore); } MgbaPrintf_("Total: %d\n", totalScore); } @@ -1023,7 +1008,7 @@ void TestRunner_Battle_CheckAiMoveScores(u32 battlerId) PrintAiMoveLog(battlerId, scoreCtx->moveSlot1, moveId1, scores[scoreCtx->moveSlot1]); if (!CheckComparision(scores[scoreCtx->moveSlot1], scoreCtx->value, scoreCtx->cmp)) { - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched SCORE_%s_VAL %S %d, got %d", + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched SCORE_%s_VAL %S %d, got %d", filename, scoreCtx->sourceLine, sCmpToStringTable[scoreCtx->cmp], GetMoveName(moveId1), scoreCtx->value, scores[scoreCtx->moveSlot1]); } } @@ -1034,7 +1019,7 @@ void TestRunner_Battle_CheckAiMoveScores(u32 battlerId) PrintAiMoveLog(battlerId, scoreCtx->moveSlot2, moveId2, scores[scoreCtx->moveSlot2]); if (!CheckComparision(scores[scoreCtx->moveSlot1], scores[scoreCtx->moveSlot2], scoreCtx->cmp)) { - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched SCORE_%s, got %S: %d, %S: %d", + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched SCORE_%s, got %S: %d, %S: %d", filename, scoreCtx->sourceLine, sCmpToStringTable[scoreCtx->cmp], GetMoveName(moveId1), scores[scoreCtx->moveSlot1], GetMoveName(moveId2), scores[scoreCtx->moveSlot2]); } } @@ -1134,7 +1119,7 @@ void TestRunner_Battle_RecordExp(u32 battlerId, u32 oldExp, u32 newExp) { const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset); - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched EXPERIENCE_BAR", filename, line); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched EXPERIENCE_BAR", filename, line); } queuedEvent += event->groupSize; @@ -1223,7 +1208,7 @@ void TestRunner_Battle_RecordMessage(const u8 *string) { const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset); - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched MESSAGE", filename, line); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched MESSAGE", filename, line); } queuedEvent += event->groupSize; @@ -1288,7 +1273,7 @@ void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1) { const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset); - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched STATUS_ICON", filename, line); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched STATUS_ICON", filename, line); } queuedEvent += event->groupSize; @@ -1323,7 +1308,7 @@ void TestRunner_Battle_AfterLastTurn(void) if (DATA.turns - 1 != DATA.lastActionTurn) { const char *filename = gTestRunnerState.test->filename; - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: %d TURNs specified, but %d ran", filename, SourceLine(0), DATA.turns, DATA.lastActionTurn + 1); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: %d TURNs specified, but %d ran", filename, SourceLine(0), DATA.turns, DATA.lastActionTurn + 1); } while (DATA.queuedEvent < DATA.queuedEventsCount @@ -1336,7 +1321,7 @@ void TestRunner_Battle_AfterLastTurn(void) const char *filename = gTestRunnerState.test->filename; u32 line = SourceLine(DATA.queuedEvents[DATA.queuedEvent].sourceLineOffset); const char *macro = sEventTypeMacros[DATA.queuedEvents[DATA.queuedEvent].type]; - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched %s", filename, line, macro); + Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Unmatched %s", filename, line, macro); } STATE->runThen = TRUE; @@ -1418,7 +1403,7 @@ static void CB2_BattleTest_NextTrial(void) if (abs(STATE->observedRatio - STATE->expectedRatio) <= Q_4_12(0.02)) gTestRunnerState.result = TEST_RESULT_PASS; else - Test_ExitWithResult(TEST_RESULT_FAIL, "Expected %q passes/successes, observed %q", STATE->expectedRatio, STATE->observedRatio); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected %q passes/successes, observed %q", gTestRunnerState.test->filename, SourceLine(0), STATE->expectedRatio, STATE->observedRatio); } } @@ -1831,7 +1816,7 @@ static void PushBattlerAction(u32 sourceLine, s32 battlerId, u32 actionType, u32 { u32 recordIndex = DATA.recordIndexes[battlerId]++; if (recordIndex >= BATTLER_RECORD_SIZE) - Test_ExitWithResult(TEST_RESULT_INVALID, "Too many actions"); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LToo many actions"); DATA.battleRecordTypes[battlerId][recordIndex] = actionType; DATA.battleRecordSourceLineOffsets[battlerId][recordIndex] = SourceLineOffset(sourceLine); DATA.recordedBattle.battleRecord[battlerId][recordIndex] = byte; @@ -1853,10 +1838,10 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde && DATA.recordedBattle.battleRecord[battlerId][i-1] == B_ACTION_USE_MOVE) { u32 line = SourceLine(DATA.battleRecordSourceLineOffsets[battlerId][i-1]); - Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Illegal MOVE", filename, line); + Test_ExitWithResult(TEST_RESULT_INVALID, line, ":L%s:%d: Illegal MOVE", filename, line); } } - Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Illegal MOVE", filename, SourceLine(0)); + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%s:%d: Illegal MOVE", filename, SourceLine(0)); } if (DATA.battleRecordTypes[battlerId][recordIndex] != RECORDED_BYTE) @@ -1884,13 +1869,13 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde switch (actionType) { case RECORDED_ACTION_TYPE: - Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Expected MOVE/SWITCH, got %s", filename, line, actualMacro); + Test_ExitWithResult(TEST_RESULT_INVALID, line, ":L%s:%d: Expected MOVE/SWITCH, got %s", filename, line, actualMacro); case RECORDED_PARTY_INDEX: - Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Expected SEND_OUT, got %s", filename, line, actualMacro); + Test_ExitWithResult(TEST_RESULT_INVALID, line, ":L%s:%d: Expected SEND_OUT, got %s", filename, line, actualMacro); } } - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: Illegal battle record", filename, line); + Test_ExitWithResult(TEST_RESULT_ERROR, line, ":L%s:%d: Illegal battle record", filename, line); } } else @@ -1898,7 +1883,7 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde if (DATA.lastActionTurn == gBattleResults.battleTurnCounter) { const char *filename = gTestRunnerState.test->filename; - Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: TURN %d incomplete", filename, SourceLine(0), gBattleResults.battleTurnCounter + 1); + Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: TURN %d incomplete", filename, SourceLine(0), gBattleResults.battleTurnCounter + 1); } } } @@ -1907,7 +1892,7 @@ void OpenTurn(u32 sourceLine) { INVALID_IF(DATA.turnState != TURN_CLOSED, "Nested TURN"); if (DATA.turns == MAX_TURNS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: TURN exceeds MAX_TURNS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: TURN exceeds MAX_TURNS", gTestRunnerState.test->filename, sourceLine); DATA.turnState = TURN_OPEN; DATA.actionBattlers = 0x00; DATA.moveBattlers = 0x00; @@ -2422,7 +2407,7 @@ void QueueAbility(u32 sourceLine, struct BattlePokemon *battler, struct AbilityE s32 battlerId = battler - gBattleMons; INVALID_IF(!STATE->runScene, "ABILITY_POPUP outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: ABILITY exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: ABILITY exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); DATA.queuedEvents[DATA.queuedEventsCount++] = (struct QueuedEvent) { .type = QUEUED_ABILITY_POPUP_EVENT, .sourceLineOffset = SourceLineOffset(sourceLine), @@ -2442,7 +2427,7 @@ void QueueAnimation(u32 sourceLine, u32 type, u32 id, struct AnimationEventConte INVALID_IF(!STATE->runScene, "ANIMATION outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: ANIMATION exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: ANIMATION exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); attackerId = ctx.attacker ? ctx.attacker - gBattleMons : 0xF; if (type == ANIM_TYPE_MOVE) @@ -2477,7 +2462,7 @@ void QueueHP(u32 sourceLine, struct BattlePokemon *battler, struct HPEventContex INVALID_IF(!STATE->runScene, "HP_BAR outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: HP_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: HP_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); if (ctx.explicitHP) { @@ -2530,7 +2515,7 @@ void QueueExp(u32 sourceLine, struct BattlePokemon *battler, struct ExpEventCont INVALID_IF(!STATE->runScene, "EXPERIENCE_BAR outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: EXPERIENCE_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: EXPERIENCE_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); if (ctx.explicitExp) { @@ -2567,7 +2552,7 @@ void QueueMessage(u32 sourceLine, const u8 *pattern) { INVALID_IF(!STATE->runScene, "MESSAGE outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: MESSAGE exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: MESSAGE exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); DATA.queuedEvents[DATA.queuedEventsCount++] = (struct QueuedEvent) { .type = QUEUED_MESSAGE_EVENT, .sourceLineOffset = SourceLineOffset(sourceLine), @@ -2586,7 +2571,7 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve INVALID_IF(!STATE->runScene, "STATUS_ICON outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) - Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: STATUS_ICON exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); + Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: STATUS_ICON exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine); if (ctx.none) mask = 0; @@ -2645,7 +2630,7 @@ struct AILogLine *GetLogLine(u32 battlerId, u32 moveIndex) } } - Test_ExitWithResult(TEST_RESULT_ERROR, "Too many AI log lines"); + Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LToo many AI log lines"); return NULL; } diff --git a/tools/mgba-rom-test-hydra/main.c b/tools/mgba-rom-test-hydra/main.c index d4f39feb736e..d9d8417e180d 100644 --- a/tools/mgba-rom-test-hydra/main.c +++ b/tools/mgba-rom-test-hydra/main.c @@ -8,6 +8,7 @@ * * COMMANDS * N: Sets the test name to the remainder of the line. + * L: Sets the filename to the remainder of the line. * R: Sets the result to the remainder of the line, and flushes any * output buffered since the previous R. * P/K/F/A: Sets the result to the remaining of the line, flushes any @@ -46,6 +47,7 @@ struct Runner int outfd; char rom_path[FILENAME_MAX]; char test_name[256]; + char filename_line[256]; size_t input_buffer_size; size_t input_buffer_capacity; char *input_buffer; @@ -59,8 +61,12 @@ struct Runner int assumptionFails; int fails; int results; - char failedTestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH]; - char knownFailingPassedTestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH]; + char failed_TestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH]; + char failed_TestFilenameLine[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH]; + char knownFailingPassed_TestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH]; + char knownFailingPassed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH]; + char assumeFailed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + char assumeFailed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; }; static unsigned nrunners = 0; @@ -102,6 +108,16 @@ static void handle_read(int i, struct Runner *runner) strncpy(runner->test_name, soc, eol - soc - 1); runner->test_name[eol - soc - 1] = '\0'; break; + case 'L': + soc += 2; + if (sizeof(runner->filename_line) <= eol - soc - 1) + { + fprintf(stderr, "filename_line too long\n"); + exit(2); + } + strncpy(runner->filename_line, soc, eol - soc - 1); + runner->filename_line[eol - soc - 1] = '\0'; + break; case 'P': runner->passes++; @@ -111,18 +127,29 @@ static void handle_read(int i, struct Runner *runner) goto add_to_results; case 'U': if (runner->knownFailsPassing < MAX_SUMMARY_TESTS_TO_LIST) - strcpy(runner->knownFailingPassedTestNames[runner->knownFailsPassing], runner->test_name); + { + strcpy(runner->knownFailingPassed_TestNames[runner->knownFailsPassing], runner->test_name); + strcpy(runner->knownFailingPassed_FilenameLine[runner->knownFailsPassing], runner->filename_line); + } runner->knownFailsPassing++; goto add_to_results; case 'T': runner->todos++; goto add_to_results; case 'A': + if (runner->assumptionFails < MAX_SUMMARY_TESTS_TO_LIST) + { + strcpy(runner->assumeFailed_TestNames[runner->assumptionFails], runner->test_name); + strcpy(runner->assumeFailed_FilenameLine[runner->assumptionFails], runner->filename_line); + } runner->assumptionFails++; goto add_to_results; case 'F': if (runner->fails < MAX_SUMMARY_TESTS_TO_LIST) - strcpy(runner->failedTestNames[runner->fails], runner->test_name); + { + strcpy(runner->failed_TestNames[runner->fails], runner->test_name); + strcpy(runner->failed_TestFilenameLine[runner->fails], runner->filename_line); + } runner->fails++; add_to_results: runner->results++; @@ -532,8 +559,14 @@ int main(int argc, char *argv[]) int fails = 0; int results = 0; - char failedTestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; - char knownFailingPassedTestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + char failed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + char failed_TestFilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + + char knownFailingPassed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + char knownFailingPassed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + + char assumeFailed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; + char assumeFailed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH]; for (int i = 0; i < nrunners; i++) { @@ -552,33 +585,43 @@ int main(int argc, char *argv[]) for (int j = 0; j < runners[i].knownFailsPassing; j++) { if (j < MAX_SUMMARY_TESTS_TO_LIST) - strcpy(knownFailingPassedTestNames[fails], runners[i].knownFailingPassedTestNames[j]); + { + strcpy(knownFailingPassed_TestNames[knownFailsPassing], runners[i].knownFailingPassed_TestNames[j]); + strcpy(knownFailingPassed_FilenameLine[knownFailsPassing], runners[i].knownFailingPassed_FilenameLine[j]); + } knownFailsPassing++; } todos += runners[i].todos; - assumptionFails += runners[i].assumptionFails; + for (int j = 0; j < runners[i].assumptionFails; j++) + { + if (j < MAX_SUMMARY_TESTS_TO_LIST) + { + strcpy(assumeFailed_TestNames[assumptionFails], runners[i].assumeFailed_TestNames[j]); + strcpy(assumeFailed_FilenameLine[assumptionFails], runners[i].assumeFailed_FilenameLine[j]); + } + assumptionFails++; + } for (int j = 0; j < runners[i].fails; j++) { if (j < MAX_SUMMARY_TESTS_TO_LIST) - strcpy(failedTestNames[fails], runners[i].failedTestNames[j]); + { + strcpy(failed_TestNames[fails], runners[i].failed_TestNames[j]); + strcpy(failed_TestFilenameLine[fails], runners[i].failed_TestFilenameLine[j]); + } fails++; } results += runners[i].results; } - qsort(failedTestNames, min(fails, MAX_SUMMARY_TESTS_TO_LIST), sizeof(char) * MAX_TEST_LIST_BUFFER_LENGTH, compare_strings); - qsort(knownFailingPassedTestNames, min(fails, MAX_SUMMARY_TESTS_TO_LIST), sizeof(char) * MAX_TEST_LIST_BUFFER_LENGTH, compare_strings); - if (results == 0) { fprintf(stdout, "\nNo tests found.\n"); } else { - fprintf(stdout, "\n"); if (fails > 0) { - fprintf(stdout, "- Tests \e[31mFAILED\e[0m : %d Add TESTS='X' to run tests with the defined prefix.\n", fails); + fprintf(stdout, "\n \e[31mFAILED\e[0m tests:\n"); for (int i = 0; i < fails; i++) { if (i >= MAX_SUMMARY_TESTS_TO_LIST) @@ -586,31 +629,51 @@ int main(int argc, char *argv[]) fprintf(stdout, " - \e[31mand %d more...\e[0m\n", fails - MAX_SUMMARY_TESTS_TO_LIST); break; } - fprintf(stdout, " - \e[31m%s\e[0m.\n", failedTestNames[i]); + fprintf(stdout, " - \e[31m%s\e[0m - %s.\n", failed_TestFilenameLine[i], failed_TestNames[i]); } } + + if (assumptionFails > 0) + { + fprintf(stdout, "\n Tests with \e[33mASSUMPTIONS_FAILED\e[0m:\n"); + for (int i = 0; i < assumptionFails; i++) + { + if (i >= MAX_SUMMARY_TESTS_TO_LIST) + { + fprintf(stdout, " - \e[33mand %d more...\e[0m\n", assumptionFails - MAX_SUMMARY_TESTS_TO_LIST); + break; + } + fprintf(stdout, " - \e[33m%s\e[0m - %s.\n", assumeFailed_FilenameLine[i], assumeFailed_TestNames[i]); + } + } + if (knownFailsPassing > 0) { - fprintf(stdout, "- \e[31mKNOWN_FAILING_PASSED\e[0m: %d \e[31mPlease remove KNOWN_FAILING if these tests intentionally PASS\e[0m\n", knownFailsPassing); + fprintf(stdout, "\n \e[33mKNOWN_FAILING\e[0m tests \e[32mPASSING\e[0m:\n"); for (int i = 0; i < knownFailsPassing; i++) { if (i >= MAX_SUMMARY_TESTS_TO_LIST) { - fprintf(stdout, " - \e[31mand %d more...\e[0m\n", knownFailsPassing - MAX_SUMMARY_TESTS_TO_LIST); + fprintf(stdout, " - \e[32mand %d more...\e[0m\n", knownFailsPassing - MAX_SUMMARY_TESTS_TO_LIST); break; } - fprintf(stdout, " - \e[31m%s\e[0m.\n", knownFailingPassedTestNames[i]); + fprintf(stdout, " - \e[32m%s\e[0m - %s.\n", knownFailingPassed_FilenameLine[i], knownFailingPassed_TestNames[i]); } } - fprintf(stdout, "- Tests \e[32mPASSED\e[0m: %d\n", passes); + + fprintf(stdout, "\n"); + if (fails > 0) + fprintf(stdout, "- Tests \e[31mFAILED\e[0m : %d Add TESTS='X' to run tests with the defined prefix.\n", fails); if (knownFails > 0) - fprintf(stdout, "- Tests \e[33mKNOWN_FAILING\e[0m: %d\n", knownFails); - if (todos > 0) - fprintf(stdout, "- Tests \e[33mTO_DO\e[0m: %d\n", todos); + fprintf(stdout, "- Tests \e[33mKNOWN_FAILING\e[0m: %d\n", knownFails); if (assumptionFails > 0) - fprintf(stdout, "- \e[33mASSUMPTIONS_FAILED\e[0m: %d\n", assumptionFails); - - fprintf(stdout, "- Tests \e[34mTOTAL\e[0m: %d\n", results); + fprintf(stdout, "- \e[33mASSUMPTIONS_FAILED\e[0m: %d\n", assumptionFails); + if (todos > 0) + fprintf(stdout, "- Tests \e[33mTO_DO\e[0m: %d\n", todos); + if (knownFailsPassing > 0) + fprintf(stdout, "- \e[32mKNOWN_FAILING_PASSING\e[0m: %d \e[33mPlease remove KNOWN_FAILING if these tests intentionally PASS\e[0m\n", knownFailsPassing); + fprintf(stdout, "- Tests \e[32mPASSED\e[0m: %d\n", passes); + fprintf(stdout, "- Tests \e[34mTOTAL\e[0m: %d\n", results); } fprintf(stdout, "\n"); diff --git a/tools/preproc/Makefile b/tools/preproc/Makefile index 1507c973f27e..606318944c63 100644 --- a/tools/preproc/Makefile +++ b/tools/preproc/Makefile @@ -3,10 +3,10 @@ CXX ?= g++ CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror SRCS := asm_file.cpp c_file.cpp charmap.cpp preproc.cpp string_parser.cpp \ - utf8.cpp + utf8.cpp io.cpp HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \ - utf8.h + utf8.h io.h ifeq ($(OS),Windows_NT) EXE := .exe diff --git a/tools/preproc/asm_file.cpp b/tools/preproc/asm_file.cpp index 04a7410e00bf..ca8b8cc4ae96 100644 --- a/tools/preproc/asm_file.cpp +++ b/tools/preproc/asm_file.cpp @@ -27,33 +27,12 @@ #include "utf8.h" #include "string_parser.h" #include "../../gflib/characters.h" +#include "io.h" -AsmFile::AsmFile(std::string filename) : m_filename(filename) +AsmFile::AsmFile(std::string filename, bool isStdin, bool doEnum) : m_filename(filename) { - FILE *fp = std::fopen(filename.c_str(), "rb"); - - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str()); - - std::fseek(fp, 0, SEEK_END); - - m_size = std::ftell(fp); - - if (m_size < 0) - FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str()); - else if (m_size == 0) - return; // Empty file - - m_buffer = new char[m_size + 1]; - - std::rewind(fp); - - if (std::fread(m_buffer, m_size, 1, fp) != 1) - FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str()); - - m_buffer[m_size] = 0; - - std::fclose(fp); + m_buffer = ReadFileToBuffer(filename.c_str(), isStdin, &m_size); + m_doEnum = doEnum; m_pos = 0; m_lineNum = 1; @@ -65,6 +44,7 @@ AsmFile::AsmFile(std::string filename) : m_filename(filename) AsmFile::AsmFile(AsmFile&& other) : m_filename(std::move(other.m_filename)) { m_buffer = other.m_buffer; + m_doEnum = other.m_doEnum; m_pos = other.m_pos; m_size = other.m_size; m_lineNum = other.m_lineNum; @@ -174,6 +154,8 @@ Directive AsmFile::GetDirective() return Directive::String; else if (CheckForDirective(".braille")) return Directive::Braille; + else if (CheckForDirective("enum")) + return Directive::Enum; else return Directive::Unknown; } @@ -527,6 +509,70 @@ void AsmFile::OutputLine() } } +// parses an assumed C `enum`. Returns false if `enum { ...` is not matched +bool AsmFile::ParseEnum() +{ + if (!m_doEnum) + return false; + + long fallbackPosition = m_pos; + std::string headerFilename = ""; + long currentHeaderLine = SkipWhitespaceAndEol(); + std::string enumName = ReadIdentifier(); + currentHeaderLine += SkipWhitespaceAndEol(); + long enumCounter = 0; + long symbolCount = 0; + + if (m_buffer[m_pos] != '{') // assume assembly macro, otherwise assume enum and report errors accordingly + { + m_pos = fallbackPosition - 4; + return false; + } + + currentHeaderLine += FindLastLineNumber(headerFilename); + m_pos++; + for (;;) + { + currentHeaderLine += SkipWhitespaceAndEol(); + std::string currentIdentName = ReadIdentifier(); + if (!currentIdentName.empty()) + { + std::printf("# %ld \"%s\"\n", currentHeaderLine, headerFilename.c_str()); + currentHeaderLine += SkipWhitespaceAndEol(); + if (m_buffer[m_pos] == '=') + { + m_pos++; + currentHeaderLine += SkipWhitespaceAndEol(); + enumCounter = ReadInteger(headerFilename, currentHeaderLine); + currentHeaderLine += SkipWhitespaceAndEol(); + } + std::printf(".equiv %s, %ld\n", currentIdentName.c_str(), enumCounter); + enumCounter++; + symbolCount++; + } + else if (symbolCount == 0) + { + RaiseError("%s:%ld: empty enum is invalid", headerFilename.c_str(), currentHeaderLine); + } + + if (m_buffer[m_pos] != ',') + { + currentHeaderLine += SkipWhitespaceAndEol(); + if (m_buffer[m_pos++] == '}' && m_buffer[m_pos++] == ';') + { + ExpectEmptyRestOfLine(); + break; + } + else + { + RaiseError("unterminated enum from included file %s:%ld", headerFilename.c_str(), currentHeaderLine); + } + } + m_pos++; + } + return true; +} + // Asserts that the rest of the line is empty and moves to the next one. void AsmFile::ExpectEmptyRestOfLine() { @@ -599,3 +645,130 @@ void AsmFile::RaiseWarning(const char* format, ...) { DO_REPORT("warning"); } + +// Skips Whitespace including newlines and returns the amount of newlines skipped +int AsmFile::SkipWhitespaceAndEol() +{ + int newlines = 0; + while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ' || m_buffer[m_pos] == '\n') + { + if (m_buffer[m_pos] == '\n') + newlines++; + m_pos++; + } + return newlines; +} + +// returns the last line indicator and its corresponding file name without modifying the token index +int AsmFile::FindLastLineNumber(std::string& filename) +{ + long pos = m_pos; + long linebreaks = 0; + while (m_buffer[pos] != '#' && pos >= 0) + { + if (m_buffer[pos] == '\n') + linebreaks++; + pos--; + } + + if (pos < 0) + RaiseError("line indicator for header file not found before `enum`"); + + pos++; + while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t') + pos++; + + if (!IsAsciiDigit(m_buffer[pos])) + RaiseError("malformatted line indicator found before `enum`, expected line number"); + + unsigned n = 0; + int digit = 0; + while ((digit = ConvertDigit(m_buffer[pos++], 10)) != -1) + n = 10 * n + digit; + + while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t') + pos++; + + if (m_buffer[pos++] != '"') + RaiseError("malformatted line indicator found before `enum`, expected filename"); + + while (m_buffer[pos] != '"') + { + unsigned char c = m_buffer[pos++]; + + if (c == 0) + { + if (pos >= m_size) + RaiseError("unexpected EOF in line indicator"); + else + RaiseError("unexpected null character in line indicator"); + } + + if (!IsAsciiPrintable(c)) + RaiseError("unexpected character '\\x%02X' in line indicator", c); + + if (c == '\\') + { + c = m_buffer[pos]; + RaiseError("unexpected escape '\\%c' in line indicator", c); + } + + filename += c; + } + + return n + linebreaks - 1; +} + +std::string AsmFile::ReadIdentifier() +{ + long start = m_pos; + if (!IsIdentifierStartingChar(m_buffer[m_pos])) + return std::string(); + + m_pos++; + + while (IsIdentifierChar(m_buffer[m_pos])) + m_pos++; + + return std::string(&m_buffer[start], m_pos - start); +} + +long AsmFile::ReadInteger(std::string filename, long line) +{ + bool negate = false; + int radix = 10; + if (!IsAsciiDigit(m_buffer[m_pos])) + { + if (m_buffer[m_pos++] == '-') + negate = true; + else + RaiseError("expected number in included file %s:%ld", filename.c_str(), line); + } + + if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x') + { + radix = 16; + m_pos += 2; + } + else if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'b') + { + radix = 2; + m_pos += 2; + } + else if (m_buffer[m_pos] == '0' && IsAsciiDigit(m_buffer[m_pos+1])) + { + radix = 8; + m_pos++; + } + + long n = 0; + int digit; + + while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1) + { + n = n * radix + digit; + m_pos++; + } + + return negate ? -n : n; +} diff --git a/tools/preproc/asm_file.h b/tools/preproc/asm_file.h index 29435f76a47a..33e6ce5c49c1 100644 --- a/tools/preproc/asm_file.h +++ b/tools/preproc/asm_file.h @@ -31,13 +31,14 @@ enum class Directive Include, String, Braille, + Enum, Unknown }; class AsmFile { public: - AsmFile(std::string filename); + AsmFile(std::string filename, bool isStdin, bool doEnum); AsmFile(AsmFile&& other); AsmFile(const AsmFile&) = delete; ~AsmFile(); @@ -49,9 +50,11 @@ class AsmFile bool IsAtEnd(); void OutputLine(); void OutputLocation(); + bool ParseEnum(); private: char* m_buffer; + bool m_doEnum; long m_pos; long m_size; long m_lineNum; @@ -68,6 +71,10 @@ class AsmFile void RaiseError(const char* format, ...); void RaiseWarning(const char* format, ...); void VerifyStringLength(int length); + int SkipWhitespaceAndEol(); + int FindLastLineNumber(std::string& filename); + std::string ReadIdentifier(); + long ReadInteger(std::string filename, long line); }; #endif // ASM_FILE_H diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp index 508c6287313a..191ca4c152e6 100644 --- a/tools/preproc/c_file.cpp +++ b/tools/preproc/c_file.cpp @@ -30,56 +30,16 @@ #include "char_util.h" #include "utf8.h" #include "string_parser.h" +#include "io.h" CFile::CFile(const char * filenameCStr, bool isStdin) { - FILE *fp; - - if (isStdin) { - fp = stdin; + if (isStdin) m_filename = std::string{"/"}.append(filenameCStr); - } else { - fp = std::fopen(filenameCStr, "rb"); + else m_filename = std::string(filenameCStr); - } - - std::string& filename = m_filename; - - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str()); - - m_size = 0; - m_buffer = (char *)malloc(CHUNK_SIZE + 1); - if (m_buffer == NULL) { - FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename.c_str()); - } - - std::size_t numAllocatedBytes = CHUNK_SIZE + 1; - std::size_t bufferOffset = 0; - std::size_t count; - - while ((count = std::fread(m_buffer + bufferOffset, 1, CHUNK_SIZE, fp)) != 0) { - if (!std::ferror(fp)) { - m_size += count; - if (std::feof(fp)) { - break; - } - - numAllocatedBytes += CHUNK_SIZE; - bufferOffset += CHUNK_SIZE; - m_buffer = (char *)realloc(m_buffer, numAllocatedBytes); - if (m_buffer == NULL) { - FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename.c_str()); - } - } else { - FATAL_ERROR("Failed to read \"%s\". (error: %s)", filename.c_str(), std::strerror(errno)); - } - } - - m_buffer[m_size] = 0; - - std::fclose(fp); + m_buffer = ReadFileToBuffer(filenameCStr, isStdin, &m_size); m_pos = 0; m_lineNum = 1; diff --git a/tools/preproc/c_file.h b/tools/preproc/c_file.h index 49e633a18dd4..c40c33c96277 100644 --- a/tools/preproc/c_file.h +++ b/tools/preproc/c_file.h @@ -56,6 +56,4 @@ class CFile void RaiseWarning(const char* format, ...); }; -#define CHUNK_SIZE 4096 - #endif // C_FILE_H diff --git a/tools/preproc/io.cpp b/tools/preproc/io.cpp new file mode 100644 index 000000000000..321676180d70 --- /dev/null +++ b/tools/preproc/io.cpp @@ -0,0 +1,51 @@ +#include "preproc.h" +#include "io.h" +#include +#include +#include + +char *ReadFileToBuffer(const char *filename, bool isStdin, long *size) +{ + FILE *fp; + if (isStdin) + fp = stdin; + else + fp = std::fopen(filename, "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename); + + *size = 0; + char *buffer = (char *)malloc(CHUNK_SIZE + 1); + if (buffer == NULL) { + FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename); + } + + std::size_t numAllocatedBytes = CHUNK_SIZE + 1; + std::size_t bufferOffset = 0; + std::size_t count; + + while ((count = std::fread(buffer + bufferOffset, 1, CHUNK_SIZE, fp)) != 0) { + if (!std::ferror(fp)) { + *size += count; + + if (std::feof(fp)) { + break; + } + + numAllocatedBytes += CHUNK_SIZE; + bufferOffset += CHUNK_SIZE; + buffer = (char *)realloc(buffer, numAllocatedBytes); + if (buffer == NULL) { + FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename); + } + } else { + FATAL_ERROR("Failed to read \"%s\". (error: %s)", filename, std::strerror(errno)); + } + } + + buffer[*size] = 0; + + std::fclose(fp); + return buffer; +} diff --git a/tools/preproc/io.h b/tools/preproc/io.h new file mode 100644 index 000000000000..ac4e91051f1c --- /dev/null +++ b/tools/preproc/io.h @@ -0,0 +1,8 @@ +#ifndef IO_H_ +#define IO_H_ + +#define CHUNK_SIZE 4096 + +char *ReadFileToBuffer(const char *filename, bool isStdin, long *size); + +#endif // IO_H_ diff --git a/tools/preproc/preproc.cpp b/tools/preproc/preproc.cpp index eb2d4c8a23f7..20c2de51b635 100644 --- a/tools/preproc/preproc.cpp +++ b/tools/preproc/preproc.cpp @@ -20,11 +20,14 @@ #include #include +#include #include "preproc.h" #include "asm_file.h" #include "c_file.h" #include "charmap.h" +static void UsageAndExit(const char *program); + Charmap* g_charmap; void PrintAsmBytes(unsigned char *s, int length) @@ -43,11 +46,12 @@ void PrintAsmBytes(unsigned char *s, int length) } } -void PreprocAsmFile(std::string filename) +void PreprocAsmFile(std::string filename, bool isStdin, bool doEnum) { std::stack stack; - stack.push(AsmFile(filename)); + stack.push(AsmFile(filename, isStdin, doEnum)); + std::printf("# 1 \"%s\"\n", filename.c_str()); for (;;) { @@ -66,7 +70,7 @@ void PreprocAsmFile(std::string filename) switch (directive) { case Directive::Include: - stack.push(AsmFile(stack.top().ReadPath())); + stack.push(AsmFile(stack.top().ReadPath(), false, doEnum)); stack.top().OutputLocation(); break; case Directive::String: @@ -83,6 +87,12 @@ void PreprocAsmFile(std::string filename) PrintAsmBytes(s, length); break; } + case Directive::Enum: + { + if (!stack.top().ParseEnum()) + stack.top().OutputLine(); + break; + } case Directive::Unknown: { std::string globalLabel = stack.top().GetGlobalLabel(); @@ -109,9 +119,9 @@ void PreprocCFile(const char * filename, bool isStdin) cFile.Preproc(); } -char* GetFileExtension(char* filename) +const char* GetFileExtension(const char* filename) { - char* extension = filename; + const char* extension = filename; while (*extension != 0) extension++; @@ -130,35 +140,64 @@ char* GetFileExtension(char* filename) return extension; } +static void UsageAndExit(const char *program) +{ + std::fprintf(stderr, "Usage: %s [-i] [-e] SRC_FILE CHARMAP_FILE\nwhere -i denotes if input is from stdin\n -e enables enum handling\n", program); + std::exit(EXIT_FAILURE); +} + int main(int argc, char **argv) { - if (argc < 3 || argc > 4) + int opt; + const char *source = NULL; + const char *charmap = NULL; + bool isStdin = false; + bool doEnum = false; + + /* preproc [-i] [-e] SRC_FILE CHARMAP_FILE */ + while ((opt = getopt(argc, argv, "ie")) != -1) { - std::fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE [-i]\nwhere -i denotes if input is from stdin\n", argv[0]); - return 1; + switch (opt) + { + case 'i': + isStdin = true; + break; + case 'e': + doEnum = true; + break; + default: + UsageAndExit(argv[0]); + break; + } } - g_charmap = new Charmap(argv[2]); + if (optind + 2 != argc) + UsageAndExit(argv[0]); + + source = argv[optind + 0]; + charmap = argv[optind + 1]; + + g_charmap = new Charmap(charmap); - char* extension = GetFileExtension(argv[1]); + const char* extension = GetFileExtension(source); if (!extension) FATAL_ERROR("\"%s\" has no file extension.\n", argv[1]); if ((extension[0] == 's') && extension[1] == 0) - PreprocAsmFile(argv[1]); - else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0) { - if (argc == 4) { - if (argv[3][0] == '-' && argv[3][1] == 'i' && argv[3][2] == '\0') { - PreprocCFile(argv[1], true); - } else { - FATAL_ERROR("unknown argument flag \"%s\".\n", argv[3]); - } - } else { - PreprocCFile(argv[1], false); - } - } else + { + PreprocAsmFile(source, isStdin, doEnum); + } + else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0) + { + if (doEnum) + FATAL_ERROR("-e is invalid for C sources\n"); + PreprocCFile(source, isStdin); + } + else + { FATAL_ERROR("\"%s\" has an unknown file extension of \"%s\".\n", argv[1], extension); + } return 0; }