From c5c0e91215e05dcc8d517f9780f4d54afa1a7d52 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Thu, 2 Feb 2023 21:39:01 -0500 Subject: [PATCH 01/18] fix BattleScript_TargetAbilityStatRaiseRet overwriting gBattlerAttacker --- data/battle_scripts_1.s | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9ff5838b1..e526f6e96 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -10200,6 +10200,7 @@ BattleScript_SymbiosisActivates:: return BattleScript_TargetAbilityStatRaiseRet:: + copybyte sSAVED_BATTLER, gBattlerAttacker copybyte gBattlerAbility, gEffectBattler copybyte gBattlerAttacker, gBattlerTarget call BattleScript_AbilityPopUp @@ -10207,6 +10208,7 @@ BattleScript_TargetAbilityStatRaiseRet:: setgraphicalstatchangevalues call BattleScript_StatUp BattleScript_TargetAbilityStatRaiseRet_End: + copybyte gBattlerAttacker, sSAVED_BATTLER return BattleScript_PokemonCantUseTheMove:: From f060d0185f2da05378f51c577baadbf3fb131cef Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Thu, 2 Feb 2023 21:59:40 -0500 Subject: [PATCH 02/18] fix wind power, electromorphosis stomping on gbattlerattacker --- data/battle_scripts_1.s | 33 +++------------------------------ src/battle_script_commands.c | 9 +++++---- 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e526f6e96..0dada5566 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2951,12 +2951,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindRider: BattleScript_TryTailwindAbilitiesLoop_WindPower: call BattleScript_AbilityPopUp - copybyte sSAVED_BATTLER, gBattlerAttacker - copybyte gBattlerAttacker, gBattlerTarget - setcharge + setcharge BS_TARGET printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER waitmessage B_WAIT_TIME_LONG - copybyte gBattlerAttacker, sSAVED_BATTLER goto BattleScript_TryTailwindAbilitiesLoop_Increment BattleScript_EffectMircleEye: @@ -5675,7 +5672,7 @@ BattleScript_EffectCharge:: attackcanceler attackstring ppreduce - setcharge + setcharge BS_ATTACKER attackanimation waitanimation .if B_CHARGE_SPDEF_RAISE >= GEN_5 @@ -6738,27 +6735,6 @@ BattleScript_TailwindEnds:: waitmessage B_WAIT_TIME_LONG end2 -BattleScript_WindPowerActivatesEnd2:: - setbyte gBattlerAttacker, 0 -BattleScript_WindPowerLoop: - printstring STRINGID_EMPTYSTRING3 - jumpifability BS_ATTACKER, ABILITY_WIND_POWER, BattleScript_WindPowerLoop_Cont - goto BattleScript_WindPowerIncrement -BattleScript_WindPowerLoop_Cont: - jumpifstatus3 BS_ATTACKER, STATUS3_CHARGED_UP, BattleScript_WindPowerIncrement - goto BattleScript_WindPower_Activate -BattleScript_WindPower_Activate: - call BattleScript_AbilityPopUp - setcharge - printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER - waitmessage B_WAIT_TIME_LONG -BattleScript_WindPowerIncrement: - addbyte gBattlerAttacker, 1 - jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_WindPowerLoop -BattleScript_WindPowerEnd: - destroyabilitypopup - end2 - BattleScript_TrickRoomEnds:: printstring STRINGID_TRICKROOMENDS waitmessage B_WAIT_TIME_LONG @@ -7233,11 +7209,8 @@ BattleScript_AngerShellRet: return BattleScript_WindPowerActivates:: -.if B_CHECK_IF_CHARGED_UP == TRUE - jumpifstatus3 BS_ATTACKER, STATUS3_CHARGED_UP, BattleScript_WindPowerActivates_Ret -.endif call BattleScript_AbilityPopUp - setcharge + setcharge BS_TARGET printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER waitmessage B_WAIT_TIME_LONG BattleScript_WindPowerActivates_Ret: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ccec3f6a6..87cb185b4 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13198,10 +13198,11 @@ static void Cmd_setforcedtarget(void) static void Cmd_setcharge(void) { - gStatuses3[gBattlerAttacker] |= STATUS3_CHARGED_UP; - gDisableStructs[gBattlerAttacker].chargeTimer = 2; - gDisableStructs[gBattlerAttacker].chargeTimerStartValue = 2; - gBattlescriptCurrInstr++; + u8 battler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + gStatuses3[battler] |= STATUS3_CHARGED_UP; + gDisableStructs[battler].chargeTimer = 2; + gDisableStructs[battler].chargeTimerStartValue = 2; + gBattlescriptCurrInstr += 2; } // Nature Power From 843096d32ac6073c733763b96649872f45727c06 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Thu, 2 Feb 2023 22:46:24 -0500 Subject: [PATCH 03/18] update setcharge macro --- asm/macros/battle_script.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 4d0b9a614..591702850 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1053,8 +1053,9 @@ .byte 0xca .endm - .macro setcharge + .macro setcharge battler:req .byte 0xcb + .byte \battler .endm .macro callterrainattack From 39b8c8e9e7ec4b6a6489f9c5d328d6579f80ef8e Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 13 Feb 2023 08:39:04 -0500 Subject: [PATCH 04/18] fix sText_BeingHitChargedPkmnWithPower battler --- src/battle_message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_message.c b/src/battle_message.c index 7ec079e72..ad830de28 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -768,7 +768,7 @@ static const u8 sText_AttackerMeltedTheIce[] = _("{B_ATK_NAME_WITH_PREFIX} melte static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!"); static const u8 sText_AttackerLostElectricType[] = _("{B_ATK_NAME_WITH_PREFIX} used up all\nof its electricity!"); static const u8 sText_AttackerSwitchedStatWithTarget[] = _("{B_ATK_NAME_WITH_PREFIX} switched {B_BUFF1}\nwith its target!"); -static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_ATK_NAME_WITH_PREFIX} with power!"); +static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_DEF_NAME_WITH_PREFIX} with power!"); static const u8 sText_SunlightActivatedAbility[] = _("The harsh sunlight activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!"); static const u8 sText_StatWasHeightened[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1} was heightened!"); static const u8 sText_ElectricTerrainActivatedAbility[] = _("The Electric Terrain activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!"); From 010ce6bc634ed9cd0dd2133fbc1dc4f25af3fe90 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 6 Jul 2023 11:56:04 -0400 Subject: [PATCH 05/18] Revert "Install libpng using pacman instead of manual build for msys2" This reverts commit dc2e210ade38bc5789764d0c3836c3d995a1d4ef. --- INSTALL.md | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 01fbd5cee..9aca02a15 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -125,12 +125,12 @@ Otherwise, ask for help on Discord or IRC (see [README.md](README.md)), or conti Note that in msys2, Copy is Ctrl+Insert and Paste is Shift+Insert. -1. Open msys2 at C:\devkitPro\msys2\mingw64.exe or run `C:\devkitPro\msys2\msys2_shell.bat -mingw64`. +1. Open msys2 at C:\devkitPro\msys2\msys2_shell.bat. 2. Certain packages are required to build pokeemerald. Install these by running the following command: ```bash - pacman -S make zlib-devel git mingw-w64-x86_64-gcc mingw-w64-x86_64-libpng + pacman -S make gcc zlib-devel git ```
Note... @@ -138,6 +138,39 @@ Note that in msys2, Copy is Ctrl+Insert and Paste is Shift+Insert. > This command will ask for confirmation, just enter the yes action when prompted.
+3. Download [libpng](https://sourceforge.net/projects/libpng/files/libpng16/1.6.37/libpng-1.6.37.tar.xz/download). + +4. Change directory to where libpng was downloaded. By default, msys2 will start in the current user's profile folder, located at **C:\Users\\⁠_\_**, where *\* is your Windows username. In most cases, libpng should be saved within a subfolder of the profile folder. For example, if libpng was saved to **C:\Users\\_\_\Downloads** (the Downloads location for most users), enter this command: + + ```bash + cd Downloads + ``` + +
+ Notes... + + > Note 1: While not shown, msys uses forward slashes `/` instead of backwards slashes `\` as the directory separator. + > Note 2: If the path has spaces, then the path must be wrapped with quotations, e.g. `cd "Downloads/My Downloads"`. + > Note 3: Windows path names are case-insensitive so adhering to capitalization isn’t needed. + > Note 4: If libpng was saved elsewhere, you will need to specify the full path to where libpng was downloaded, e.g. `cd c:/devkitpro/msys2` if it was saved there. +
+ +5. Run the following commands to uncompress and install libpng. + + ```bash + tar xf libpng-1.6.37.tar.xz + cd libpng-1.6.37 + ./configure --prefix=/usr + make check + make install + ``` + +6. Then finally, run the following command to change back to the user profile folder. + + ```bash + cd + ``` + ### Choosing where to store pokeemerald (msys2) At this point, you can choose a folder to store pokeemerald into. If you're okay with storing pokeemerald in the user profile folder, then proceed to [Installation](#installation). Otherwise, you'll need to account for where pokeemerald is stored when changing directory to the pokeemerald folder. From f4111d8082cce24b94969d5b49e977a165abbcb4 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sun, 9 Jul 2023 14:57:51 -0400 Subject: [PATCH 06/18] Sync castform folder organization --- .../anim_front.png} | Bin .../{back_normal_form.png => normal/back.png} | Bin .../front.png} | Bin .../normal.pal} | 0 .../shiny.pal} | 0 .../anim_front.png} | Bin .../{back_rainy_form.png => rainy/back.png} | Bin .../{front_rainy_form.png => rainy/front.png} | Bin .../normal.pal} | 0 .../{shiny_rainy_form.pal => rainy/shiny.pal} | 0 .../anim_front.png} | Bin .../{back_snowy_form.png => snowy/back.png} | Bin .../{front_snowy_form.png => snowy/front.png} | Bin .../normal.pal} | 0 .../{shiny_snowy_form.pal => snowy/shiny.pal} | 0 .../anim_front.png} | Bin .../{back_sunny_form.png => sunny/back.png} | Bin .../{front_sunny_form.png => sunny/front.png} | Bin .../normal.pal} | 0 .../{shiny_sunny_form.pal => sunny/shiny.pal} | 0 graphics_file_rules.mk | 40 +++++++++--------- 21 files changed, 20 insertions(+), 20 deletions(-) rename graphics/pokemon/castform/{anim_front_normal_form.png => normal/anim_front.png} (100%) rename graphics/pokemon/castform/{back_normal_form.png => normal/back.png} (100%) rename graphics/pokemon/castform/{front_normal_form.png => normal/front.png} (100%) rename graphics/pokemon/castform/{normal_normal_form.pal => normal/normal.pal} (100%) rename graphics/pokemon/castform/{shiny_normal_form.pal => normal/shiny.pal} (100%) rename graphics/pokemon/castform/{anim_front_rainy_form.png => rainy/anim_front.png} (100%) rename graphics/pokemon/castform/{back_rainy_form.png => rainy/back.png} (100%) rename graphics/pokemon/castform/{front_rainy_form.png => rainy/front.png} (100%) rename graphics/pokemon/castform/{normal_rainy_form.pal => rainy/normal.pal} (100%) rename graphics/pokemon/castform/{shiny_rainy_form.pal => rainy/shiny.pal} (100%) rename graphics/pokemon/castform/{anim_front_snowy_form.png => snowy/anim_front.png} (100%) rename graphics/pokemon/castform/{back_snowy_form.png => snowy/back.png} (100%) rename graphics/pokemon/castform/{front_snowy_form.png => snowy/front.png} (100%) rename graphics/pokemon/castform/{normal_snowy_form.pal => snowy/normal.pal} (100%) rename graphics/pokemon/castform/{shiny_snowy_form.pal => snowy/shiny.pal} (100%) rename graphics/pokemon/castform/{anim_front_sunny_form.png => sunny/anim_front.png} (100%) rename graphics/pokemon/castform/{back_sunny_form.png => sunny/back.png} (100%) rename graphics/pokemon/castform/{front_sunny_form.png => sunny/front.png} (100%) rename graphics/pokemon/castform/{normal_sunny_form.pal => sunny/normal.pal} (100%) rename graphics/pokemon/castform/{shiny_sunny_form.pal => sunny/shiny.pal} (100%) diff --git a/graphics/pokemon/castform/anim_front_normal_form.png b/graphics/pokemon/castform/normal/anim_front.png similarity index 100% rename from graphics/pokemon/castform/anim_front_normal_form.png rename to graphics/pokemon/castform/normal/anim_front.png diff --git a/graphics/pokemon/castform/back_normal_form.png b/graphics/pokemon/castform/normal/back.png similarity index 100% rename from graphics/pokemon/castform/back_normal_form.png rename to graphics/pokemon/castform/normal/back.png diff --git a/graphics/pokemon/castform/front_normal_form.png b/graphics/pokemon/castform/normal/front.png similarity index 100% rename from graphics/pokemon/castform/front_normal_form.png rename to graphics/pokemon/castform/normal/front.png diff --git a/graphics/pokemon/castform/normal_normal_form.pal b/graphics/pokemon/castform/normal/normal.pal similarity index 100% rename from graphics/pokemon/castform/normal_normal_form.pal rename to graphics/pokemon/castform/normal/normal.pal diff --git a/graphics/pokemon/castform/shiny_normal_form.pal b/graphics/pokemon/castform/normal/shiny.pal similarity index 100% rename from graphics/pokemon/castform/shiny_normal_form.pal rename to graphics/pokemon/castform/normal/shiny.pal diff --git a/graphics/pokemon/castform/anim_front_rainy_form.png b/graphics/pokemon/castform/rainy/anim_front.png similarity index 100% rename from graphics/pokemon/castform/anim_front_rainy_form.png rename to graphics/pokemon/castform/rainy/anim_front.png diff --git a/graphics/pokemon/castform/back_rainy_form.png b/graphics/pokemon/castform/rainy/back.png similarity index 100% rename from graphics/pokemon/castform/back_rainy_form.png rename to graphics/pokemon/castform/rainy/back.png diff --git a/graphics/pokemon/castform/front_rainy_form.png b/graphics/pokemon/castform/rainy/front.png similarity index 100% rename from graphics/pokemon/castform/front_rainy_form.png rename to graphics/pokemon/castform/rainy/front.png diff --git a/graphics/pokemon/castform/normal_rainy_form.pal b/graphics/pokemon/castform/rainy/normal.pal similarity index 100% rename from graphics/pokemon/castform/normal_rainy_form.pal rename to graphics/pokemon/castform/rainy/normal.pal diff --git a/graphics/pokemon/castform/shiny_rainy_form.pal b/graphics/pokemon/castform/rainy/shiny.pal similarity index 100% rename from graphics/pokemon/castform/shiny_rainy_form.pal rename to graphics/pokemon/castform/rainy/shiny.pal diff --git a/graphics/pokemon/castform/anim_front_snowy_form.png b/graphics/pokemon/castform/snowy/anim_front.png similarity index 100% rename from graphics/pokemon/castform/anim_front_snowy_form.png rename to graphics/pokemon/castform/snowy/anim_front.png diff --git a/graphics/pokemon/castform/back_snowy_form.png b/graphics/pokemon/castform/snowy/back.png similarity index 100% rename from graphics/pokemon/castform/back_snowy_form.png rename to graphics/pokemon/castform/snowy/back.png diff --git a/graphics/pokemon/castform/front_snowy_form.png b/graphics/pokemon/castform/snowy/front.png similarity index 100% rename from graphics/pokemon/castform/front_snowy_form.png rename to graphics/pokemon/castform/snowy/front.png diff --git a/graphics/pokemon/castform/normal_snowy_form.pal b/graphics/pokemon/castform/snowy/normal.pal similarity index 100% rename from graphics/pokemon/castform/normal_snowy_form.pal rename to graphics/pokemon/castform/snowy/normal.pal diff --git a/graphics/pokemon/castform/shiny_snowy_form.pal b/graphics/pokemon/castform/snowy/shiny.pal similarity index 100% rename from graphics/pokemon/castform/shiny_snowy_form.pal rename to graphics/pokemon/castform/snowy/shiny.pal diff --git a/graphics/pokemon/castform/anim_front_sunny_form.png b/graphics/pokemon/castform/sunny/anim_front.png similarity index 100% rename from graphics/pokemon/castform/anim_front_sunny_form.png rename to graphics/pokemon/castform/sunny/anim_front.png diff --git a/graphics/pokemon/castform/back_sunny_form.png b/graphics/pokemon/castform/sunny/back.png similarity index 100% rename from graphics/pokemon/castform/back_sunny_form.png rename to graphics/pokemon/castform/sunny/back.png diff --git a/graphics/pokemon/castform/front_sunny_form.png b/graphics/pokemon/castform/sunny/front.png similarity index 100% rename from graphics/pokemon/castform/front_sunny_form.png rename to graphics/pokemon/castform/sunny/front.png diff --git a/graphics/pokemon/castform/normal_sunny_form.pal b/graphics/pokemon/castform/sunny/normal.pal similarity index 100% rename from graphics/pokemon/castform/normal_sunny_form.pal rename to graphics/pokemon/castform/sunny/normal.pal diff --git a/graphics/pokemon/castform/shiny_sunny_form.pal b/graphics/pokemon/castform/sunny/shiny.pal similarity index 100% rename from graphics/pokemon/castform/shiny_sunny_form.pal rename to graphics/pokemon/castform/sunny/shiny.pal diff --git a/graphics_file_rules.mk b/graphics_file_rules.mk index 97a7794b3..090620ebe 100644 --- a/graphics_file_rules.mk +++ b/graphics_file_rules.mk @@ -30,34 +30,34 @@ contest_types := cool beauty cute smart tough ### Castform ### -$(CASTFORMGFXDIR)/front.4bpp: $(CASTFORMGFXDIR)/front_normal_form.4bpp \ - $(CASTFORMGFXDIR)/front_sunny_form.4bpp \ - $(CASTFORMGFXDIR)/front_rainy_form.4bpp \ - $(CASTFORMGFXDIR)/front_snowy_form.4bpp +$(CASTFORMGFXDIR)/front.4bpp: $(CASTFORMGFXDIR)/normal/front.4bpp \ + $(CASTFORMGFXDIR)/sunny/front.4bpp \ + $(CASTFORMGFXDIR)/rainy/front.4bpp \ + $(CASTFORMGFXDIR)/snowy/front.4bpp @cat $^ >$@ -$(CASTFORMGFXDIR)/back.4bpp: $(CASTFORMGFXDIR)/back_normal_form.4bpp \ - $(CASTFORMGFXDIR)/back_sunny_form.4bpp \ - $(CASTFORMGFXDIR)/back_rainy_form.4bpp \ - $(CASTFORMGFXDIR)/back_snowy_form.4bpp +$(CASTFORMGFXDIR)/back.4bpp: $(CASTFORMGFXDIR)/normal/back.4bpp \ + $(CASTFORMGFXDIR)/sunny/back.4bpp \ + $(CASTFORMGFXDIR)/rainy/back.4bpp \ + $(CASTFORMGFXDIR)/snowy/back.4bpp @cat $^ >$@ -$(CASTFORMGFXDIR)/anim_front.4bpp: $(CASTFORMGFXDIR)/anim_front_normal_form.4bpp \ - $(CASTFORMGFXDIR)/anim_front_sunny_form.4bpp \ - $(CASTFORMGFXDIR)/anim_front_rainy_form.4bpp \ - $(CASTFORMGFXDIR)/anim_front_snowy_form.4bpp +$(CASTFORMGFXDIR)/anim_front.4bpp: $(CASTFORMGFXDIR)/normal/anim_front.4bpp \ + $(CASTFORMGFXDIR)/sunny/anim_front.4bpp \ + $(CASTFORMGFXDIR)/rainy/anim_front.4bpp \ + $(CASTFORMGFXDIR)/snowy/anim_front.4bpp @cat $^ >$@ -$(CASTFORMGFXDIR)/normal.gbapal: $(CASTFORMGFXDIR)/normal_normal_form.gbapal \ - $(CASTFORMGFXDIR)/normal_sunny_form.gbapal \ - $(CASTFORMGFXDIR)/normal_rainy_form.gbapal \ - $(CASTFORMGFXDIR)/normal_snowy_form.gbapal +$(CASTFORMGFXDIR)/normal.gbapal: $(CASTFORMGFXDIR)/normal/normal.gbapal \ + $(CASTFORMGFXDIR)/sunny/normal.gbapal \ + $(CASTFORMGFXDIR)/rainy/normal.gbapal \ + $(CASTFORMGFXDIR)/snowy/normal.gbapal @cat $^ >$@ -$(CASTFORMGFXDIR)/shiny.gbapal: $(CASTFORMGFXDIR)/shiny_normal_form.gbapal \ - $(CASTFORMGFXDIR)/shiny_sunny_form.gbapal \ - $(CASTFORMGFXDIR)/shiny_rainy_form.gbapal \ - $(CASTFORMGFXDIR)/shiny_snowy_form.gbapal +$(CASTFORMGFXDIR)/shiny.gbapal: $(CASTFORMGFXDIR)/normal/shiny.gbapal \ + $(CASTFORMGFXDIR)/sunny/shiny.gbapal \ + $(CASTFORMGFXDIR)/rainy/shiny.gbapal \ + $(CASTFORMGFXDIR)/snowy/shiny.gbapal @cat $^ >$@ From 5fd881a1b68b62679452591a6299fad0668db2e9 Mon Sep 17 00:00:00 2001 From: gruxor Date: Wed, 12 Jul 2023 14:44:23 -0400 Subject: [PATCH 07/18] Remove unused macro to fix syntax highlights --- include/global.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/global.h b/include/global.h index 184abdbe4..0ca226c03 100644 --- a/include/global.h +++ b/include/global.h @@ -20,7 +20,6 @@ #define BLOCK_CROSS_JUMP asm(""); // to help in decompiling -#define asm_comment(x) asm volatile("@ -- " x " -- ") #define asm_unified(x) asm(".syntax unified\n" x "\n.syntax divided") #define NAKED __attribute__((naked)) From 1b6f582600f334e9bb023e4b1bab274e10923516 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 17 Jul 2023 15:33:44 +0200 Subject: [PATCH 08/18] tests for stamina/wind power --- test/ability_stamina.c | 89 +++++++++++++++++++++++ test/ability_wind_power.c | 145 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 test/ability_stamina.c create mode 100644 test/ability_wind_power.c diff --git a/test/ability_stamina.c b/test/ability_stamina.c new file mode 100644 index 000000000..70cf3cdc0 --- /dev/null +++ b/test/ability_stamina.c @@ -0,0 +1,89 @@ +#include "global.h" +#include "test_battle.h" + +#define STAMINA_STAT_RAISE(target, msg) \ +{ \ + ABILITY_POPUP(target, ABILITY_STAMINA); \ + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, target); \ + MESSAGE(msg); \ +} + +#define STAMINA_HIT(attacker, target, move, msg, dmgVar) \ +{ \ + ANIMATION(ANIM_TYPE_MOVE, move, attacker); \ + HP_BAR(target, captureDamage: &dmgVar); \ + STAMINA_STAT_RAISE(target, msg); \ +} + +SINGLE_BATTLE_TEST("Stamina raises Defense by 1 when hit by a move") +{ + s16 turnOneHit, turnTwoHit; + u16 move; + + PARAMETRIZE {move = MOVE_TACKLE; } + PARAMETRIZE {move = MOVE_GUST; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + ASSUME(gBattleMoves[MOVE_GUST].power != 0); + ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL); + ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_STAMINA); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(opponent, move); } + } SCENE { + STAMINA_HIT(opponent, player, move, "Wobbuffet's Defense rose!", turnOneHit); + STAMINA_HIT(opponent, player, move, "Wobbuffet's Defense rose!", turnTwoHit); + } + THEN { + if (move == MOVE_TACKLE) { + EXPECT_MUL_EQ(turnTwoHit, Q_4_12(1.5), turnOneHit); + } + else { + EXPECT_EQ(turnTwoHit, turnOneHit); + } + } +} + +DOUBLE_BATTLE_TEST("Stamina activates correctly for every battler with the ability when hit by a multi target move") +{ + u16 abilityLeft, abilityRight; + + PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_STAMINA; } + PARAMETRIZE {abilityLeft = ABILITY_STAMINA, abilityRight = ABILITY_NONE; } + PARAMETRIZE {abilityLeft = ABILITY_STAMINA, abilityRight = ABILITY_STAMINA; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); } + PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(15); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_EARTHQUAKE);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, opponentLeft); + + HP_BAR(playerLeft); + if (abilityLeft == ABILITY_STAMINA) { + STAMINA_STAT_RAISE(playerLeft, "Wobbuffet's Defense rose!"); + } + NOT HP_BAR(opponentLeft); // We need to check the attacker itself does NOT get damaged. There was an issue when the targets would get overwritten by the Stamina's stat raise. + + HP_BAR(playerRight); + if (abilityRight == ABILITY_STAMINA) { + STAMINA_STAT_RAISE(playerRight, "Wobbuffet's Defense rose!"); + } + NOT HP_BAR(opponentLeft); // We need to check the attacker itself does NOT get damaged. There was an issue when the targets would get overwritten by the Stamina's stat raise. + + HP_BAR(opponentRight); + } + THEN { + EXPECT_NE(playerLeft->hp, playerLeft->maxHP); + EXPECT_NE(playerRight->hp, playerRight->maxHP); + EXPECT_NE(opponentRight->hp, opponentRight->maxHP); + EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP); + } +} diff --git a/test/ability_wind_power.c b/test/ability_wind_power.c new file mode 100644 index 000000000..83ec47103 --- /dev/null +++ b/test/ability_wind_power.c @@ -0,0 +1,145 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_THUNDERBOLT].power != 0); + ASSUME(gBattleMoves[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC); + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + ASSUME(gBattleMoves[MOVE_AIR_CUTTER].power != 0); + ASSUME(gBattleMoves[MOVE_AIR_CUTTER].target == MOVE_TARGET_BOTH); + ASSUME(gBattleMoves[MOVE_AIR_CUTTER].flags & FLAG_WIND_MOVE); + ASSUME(!(gBattleMoves[MOVE_TACKLE].flags & FLAG_WIND_MOVE)); +} + +SINGLE_BATTLE_TEST("Wind Power sets up Charge for player when hit by a wind move") +{ + s16 dmgBefore, dmgAfter; + u16 move; + + PARAMETRIZE {move = MOVE_TACKLE; } + PARAMETRIZE {move = MOVE_AIR_CUTTER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed. + } WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); } + TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &dmgBefore); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player); + if (move == MOVE_AIR_CUTTER) { + ABILITY_POPUP(player, ABILITY_WIND_POWER); + MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!"); + } + + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &dmgAfter); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player); + if (move == MOVE_AIR_CUTTER) { + ABILITY_POPUP(player, ABILITY_WIND_POWER); + MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!"); + } + } + THEN { + if (move == MOVE_AIR_CUTTER) { + EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter); + } + else { + EXPECT_EQ(dmgAfter, dmgBefore); + } + } +} + +SINGLE_BATTLE_TEST("Wind Power sets up Charge for opponent when hit by a wind move") +{ + s16 dmgBefore, dmgAfter; + u16 move; + + PARAMETRIZE {move = MOVE_TACKLE; } + PARAMETRIZE {move = MOVE_AIR_CUTTER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed. + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_THUNDERBOLT), MOVE(player, move); } + TURN { MOVE(opponent, MOVE_THUNDERBOLT), MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, opponent); + HP_BAR(player, captureDamage: &dmgBefore); + + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + if (move == MOVE_AIR_CUTTER) { + ABILITY_POPUP(opponent, ABILITY_WIND_POWER); + MESSAGE("Being hit by Air Cutter charged Foe Wobbuffet with power!"); + } + + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, opponent); + HP_BAR(player, captureDamage: &dmgAfter); + + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + if (move == MOVE_AIR_CUTTER) { + ABILITY_POPUP(opponent, ABILITY_WIND_POWER); + MESSAGE("Being hit by Air Cutter charged Foe Wobbuffet with power!"); + } + } + THEN { + if (move == MOVE_AIR_CUTTER) { + EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter); + } + else { + EXPECT_EQ(dmgAfter, dmgBefore); + } + } +} + +DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a multi target move") +{ + u16 abilityLeft, abilityRight; + + PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER; } + PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; } + PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); } + PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_AIR_CUTTER);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AIR_CUTTER, opponentLeft); + + HP_BAR(playerLeft); + if (abilityLeft == ABILITY_WIND_POWER) { + ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER); + MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!"); + } + NOT HP_BAR(opponentLeft); + NOT HP_BAR(opponentRight); + + HP_BAR(playerRight); + if (abilityRight == ABILITY_WIND_POWER) { + ABILITY_POPUP(playerRight, ABILITY_WIND_POWER); + MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!"); + } + NOT HP_BAR(opponentLeft); + NOT HP_BAR(opponentRight); + } + THEN { + EXPECT_NE(playerLeft->hp, playerLeft->maxHP); + EXPECT_NE(playerRight->hp, playerRight->maxHP); + EXPECT_EQ(opponentRight->hp, opponentRight->maxHP); + EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP); + } +} From 04feba83147c24fed0f533acab9b60baa054054f Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 17 Jul 2023 16:30:59 +0200 Subject: [PATCH 09/18] test for electromorphosis, small fix for wind power --- src/battle_util.c | 1 - test/ability_electromorphosis.c | 56 ++++++++++++++++++++ test/ability_wind_power.c | 90 ++++++++++++++++++++++++++++++--- 3 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 test/ability_electromorphosis.c diff --git a/src/battle_util.c b/src/battle_util.c index 105b29cac..f1048664b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5691,7 +5691,6 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && IsBattlerAlive(gBattlerTarget)) { - gBattlerAttacker = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WindPowerActivates; effect++; diff --git a/test/ability_electromorphosis.c b/test/ability_electromorphosis.c new file mode 100644 index 000000000..25d33aac0 --- /dev/null +++ b/test/ability_electromorphosis.c @@ -0,0 +1,56 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Electromorphosis sets up Charge when hit by any move") +{ + s16 dmgBefore, dmgAfter; + u16 move; + + PARAMETRIZE {move = MOVE_TACKLE; } + PARAMETRIZE {move = MOVE_GUST; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + ASSUME(gBattleMoves[MOVE_GUST].power != 0); + ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL); + ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL); + ASSUME(gBattleMoves[MOVE_THUNDERBOLT].power != 0); + ASSUME(gBattleMoves[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC); + + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ELECTROMORPHOSIS); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed. + } + WHEN { + TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); } + TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &dmgBefore); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player); + ABILITY_POPUP(player, ABILITY_ELECTROMORPHOSIS); + if (move == MOVE_TACKLE) { + MESSAGE("Being hit by Tackle charged Wobbuffet with power!"); + } + else { + MESSAGE("Being hit by Gust charged Wobbuffet with power!"); + } + + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player); + HP_BAR(opponent, captureDamage: &dmgAfter); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player); + ABILITY_POPUP(player, ABILITY_ELECTROMORPHOSIS); + if (move == MOVE_TACKLE) { + MESSAGE("Being hit by Tackle charged Wobbuffet with power!"); + } + else { + MESSAGE("Being hit by Gust charged Wobbuffet with power!"); + } + } + THEN { + EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter); + } +} diff --git a/test/ability_wind_power.c b/test/ability_wind_power.c index 83ec47103..8488db269 100644 --- a/test/ability_wind_power.c +++ b/test/ability_wind_power.c @@ -9,6 +9,9 @@ ASSUMPTIONS ASSUME(gBattleMoves[MOVE_AIR_CUTTER].power != 0); ASSUME(gBattleMoves[MOVE_AIR_CUTTER].target == MOVE_TARGET_BOTH); ASSUME(gBattleMoves[MOVE_AIR_CUTTER].flags & FLAG_WIND_MOVE); + ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].power != 0); + ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].target == MOVE_TARGET_FOES_AND_ALLY); + ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].flags & FLAG_WIND_MOVE); ASSUME(!(gBattleMoves[MOVE_TACKLE].flags & FLAG_WIND_MOVE)); } @@ -102,11 +105,11 @@ SINGLE_BATTLE_TEST("Wind Power sets up Charge for opponent when hit by a wind mo } } -DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a multi target move") +DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a 2/3 target move") { - u16 abilityLeft, abilityRight; + u16 move, abilityLeft, abilityRight; - PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER; } + PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER;} PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; } PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; } @@ -116,7 +119,7 @@ DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ab OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); } OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); } } WHEN { - TURN { MOVE(opponentLeft, MOVE_AIR_CUTTER);} + TURN { MOVE(opponentLeft, MOVE_AIR_CUTTER); MOVE(opponentRight, MOVE_AIR_CUTTER);} } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_AIR_CUTTER, opponentLeft); @@ -125,9 +128,6 @@ DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ab ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER); MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!"); } - NOT HP_BAR(opponentLeft); - NOT HP_BAR(opponentRight); - HP_BAR(playerRight); if (abilityRight == ABILITY_WIND_POWER) { ABILITY_POPUP(playerRight, ABILITY_WIND_POWER); @@ -143,3 +143,79 @@ DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ab EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP); } } + +DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a 3 target move") +{ + u16 abilityLeft, abilityRight; + + PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER; } + PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; } + PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); } + PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PETAL_BLIZZARD);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_BLIZZARD, opponentLeft); + + HP_BAR(playerLeft); + if (abilityLeft == ABILITY_WIND_POWER) { + ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER); + MESSAGE("Being hit by PetalBlizzrd charged Wobbuffet with power!"); + } + HP_BAR(playerRight); + if (abilityRight == ABILITY_WIND_POWER) { + ABILITY_POPUP(playerRight, ABILITY_WIND_POWER); + MESSAGE("Being hit by PetalBlizzrd charged Wobbuffet with power!"); + } + HP_BAR(opponentRight); + NOT HP_BAR(opponentLeft); + } + THEN { + EXPECT_NE(playerLeft->hp, playerLeft->maxHP); + EXPECT_NE(playerRight->hp, playerRight->maxHP); + EXPECT_NE(opponentRight->hp, opponentRight->maxHP); + EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Wind Power activates correctly when Tailwind is used") +{ + bool8 opponentSide; + + PARAMETRIZE {opponentSide = TRUE;} + PARAMETRIZE {opponentSide = FALSE;} + + GIVEN { + ASSUME(gBattleMoves[MOVE_TAILWIND].effect == EFFECT_TAILWIND); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(15); } + } WHEN { + TURN { MOVE((opponentSide == TRUE) ? opponentLeft : playerLeft, MOVE_TAILWIND);} + } SCENE { + if (opponentSide) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft); + + ABILITY_POPUP(opponentLeft, ABILITY_WIND_POWER); + MESSAGE("Being hit by Tailwind charged Foe Wobbuffet with power!"); + + ABILITY_POPUP(opponentRight, ABILITY_WIND_POWER); + MESSAGE("Being hit by Tailwind charged Foe Wobbuffet with power!"); + } + else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, playerLeft); + + ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER); + MESSAGE("Being hit by Tailwind charged Wobbuffet with power!"); + + ABILITY_POPUP(playerRight, ABILITY_WIND_POWER); + MESSAGE("Being hit by Tailwind charged Wobbuffet with power!"); + } + } +} From 8ec0bff0342413ac4996b63382c8d03a3b532899 Mon Sep 17 00:00:00 2001 From: luckytyphlosion <10688458+luckytyphlosion@users.noreply.github.com> Date: Tue, 18 Jul 2023 19:28:41 -0400 Subject: [PATCH 10/18] Update msys2 instructions Add pacman -Sy msys2-keyring --- INSTALL.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 9aca02a15..a76d0acc7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -127,15 +127,16 @@ Note that in msys2, Copy is Ctrl+Insert and Paste is Shift+Insert. 1. Open msys2 at C:\devkitPro\msys2\msys2_shell.bat. -2. Certain packages are required to build pokeemerald. Install these by running the following command: +2. Certain packages are required to build pokeemerald. Install these by running the following two commands: ```bash + pacman -Sy msys2-keyring pacman -S make gcc zlib-devel git ```
Note... - > This command will ask for confirmation, just enter the yes action when prompted. + > The commands will ask for confirmation, just enter the yes action when prompted.
3. Download [libpng](https://sourceforge.net/projects/libpng/files/libpng16/1.6.37/libpng-1.6.37.tar.xz/download). From 34beb3d662252ab309a094a224f4414e7771cabf Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 19 Jul 2023 10:59:36 +0200 Subject: [PATCH 11/18] add tests for rattled --- test/ability_rattled.c | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 test/ability_rattled.c diff --git a/test/ability_rattled.c b/test/ability_rattled.c new file mode 100644 index 000000000..bddc88751 --- /dev/null +++ b/test/ability_rattled.c @@ -0,0 +1,93 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_FURY_CUTTER].type == TYPE_BUG); + ASSUME(gBattleMoves[MOVE_FURY_CUTTER].power != 0); + ASSUME(gBattleMoves[MOVE_FEINT_ATTACK].type == TYPE_DARK); + ASSUME(gBattleMoves[MOVE_FEINT_ATTACK].power != 0); + ASSUME(gBattleMoves[MOVE_SHADOW_PUNCH].type == TYPE_GHOST); + ASSUME(gBattleMoves[MOVE_SHADOW_PUNCH].power != 0); + ASSUME(gBattleMoves[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); +} + +SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when hit by Bug, Dark or Ghost type move") +{ + u16 move; + PARAMETRIZE { move = MOVE_FURY_CUTTER; } + PARAMETRIZE { move = MOVE_FEINT_ATTACK; } + PARAMETRIZE { move = MOVE_SHADOW_PUNCH; } + PARAMETRIZE { move = MOVE_TACKLE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(42) ;} + OPPONENT(SPECIES_SUDOWOODO) {Speed(40); Ability(ABILITY_RATTLED);} + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + if (move != MOVE_TACKLE) { + ABILITY_POPUP(opponent, ABILITY_RATTLED); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Sudowoodo's Speed rose!"); + } + MESSAGE("Foe Sudowoodo used Celebrate!"); + // Sudowoodo is now faster + if (move != MOVE_TACKLE){ + MESSAGE("Foe Sudowoodo used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_RATTLED); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Sudowoodo's Speed rose!"); + } + else { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + MESSAGE("Foe Sudowoodo used Celebrate!"); + } + } +} + +SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when affected by Intimidate") +{ + GIVEN { + ASSUME(B_UPDATED_INTIMIDATE >= GEN_8); + PLAYER(SPECIES_GYARADOS) {Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_SUDOWOODO) {Ability(ABILITY_RATTLED); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Gyarados's Intimidate cuts Foe Sudowoodo's attack!"); + ABILITY_POPUP(opponent, ABILITY_RATTLED); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Sudowoodo's Speed rose!"); + } +} + +SINGLE_BATTLE_TEST("Rattled triggers correctly when hit by U-Turn") // Specific test here, because of #3124 +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE); + ASSUME(gBattleMoves[MOVE_U_TURN].type == TYPE_BUG); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_SUDOWOODO) {Ability(ABILITY_RATTLED); } + OPPONENT(SPECIES_SUDOWOODO); + } WHEN { + TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet used U-Turn!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_RATTLED); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Sudowoodo's Speed rose!"); + MESSAGE("Go! Wynaut!"); + } +} From 7fa6ed8b05d794b1e2656849d1631230cc8084fa Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Wed, 19 Jul 2023 17:33:28 +0200 Subject: [PATCH 12/18] Fix priortiy typos --- include/pokeball.h | 2 +- src/pokeball.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pokeball.h b/include/pokeball.h index 1149791b8..2f003c407 100644 --- a/include/pokeball.h +++ b/include/pokeball.h @@ -34,7 +34,7 @@ extern const struct SpriteTemplate gBallSpriteTemplates[]; #define POKEBALL_OPPONENT_SENDOUT 0xFE u8 DoPokeballSendOutAnimation(s16 pan, u8 kindOfThrow); -void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species); +void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriority, u8 delay, u32 fadePalettes, u16 species); u8 CreateTradePokeballSprite(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subPriority, u8 delay, u32 fadePalettes); void StartHealthboxSlideIn(u8 battler); void DoHitAnimHealthboxEffect(u8 battler); diff --git a/src/pokeball.c b/src/pokeball.c index 2633fd574..22d973381 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -1028,13 +1028,13 @@ static u8 LaunchBallFadeMonTaskForPokeball(bool8 unFadeLater, u8 spritePalNum, u #define sTrigIdx data[7] // Pokeball in Birch intro, and when receiving via trade -void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species) +void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriority, u8 delay, u32 fadePalettes, u16 species) { u8 spriteId; LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[BALL_POKE]); LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[BALL_POKE]); - spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriortiy); + spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriority); gSprites[spriteId].sMonSpriteId = monSpriteId; gSprites[spriteId].sFinalMonX = gSprites[monSpriteId].x; From b53cca1a064d3a565900c9a096f9f6a7779b1dd5 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 24 Jul 2023 14:57:37 +0100 Subject: [PATCH 13/18] Declarations for 2- and 3-argument GetMonData (#1756) * Type-safe GetMonData/GetBoxMonData * Comments --- include/global.h | 7 +++++++ include/pokemon.h | 17 +++++++++++------ src/daycare.c | 2 +- src/pokemon.c | 16 ++++++++++++++-- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/include/global.h b/include/global.h index 0ca226c03..00b08f48f 100644 --- a/include/global.h +++ b/include/global.h @@ -134,6 +134,13 @@ #define NUM_FLAG_BYTES ROUND_BITS_TO_BYTES(FLAGS_COUNT) #define NUM_TRENDY_SAYING_BYTES ROUND_BITS_TO_BYTES(NUM_TRENDY_SAYINGS) +// This returns the number of arguments passed to it (up to 8). +#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N + +#define CAT(a, b) CAT_(a, b) +#define CAT_(a, b) a ## b + // This produces an error at compile-time if expr is zero. // It looks like file.c:line: size of array `id' is negative #define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1]; diff --git a/include/pokemon.h b/include/pokemon.h index 54b4841f7..f4f6d6989 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -436,12 +436,17 @@ void SetMultiuseSpriteTemplateToPokemon(u16 speciesTag, u8 battlerPosition); void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition); void SetMultiuseSpriteTemplateToTrainerFront(u16 trainerPicId, u8 battlerPosition); -// These are full type signatures for GetMonData() and GetBoxMonData(), -// but they are not used since some code erroneously omits the third arg. -// u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data); -// u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data); -u32 GetMonData(); -u32 GetBoxMonData(); +/* GameFreak called Get(Box)MonData with either 2 or 3 arguments, for + * type safety we have a Get(Box)MonData macro which dispatches to + * either Get(Box)MonData2 or Get(Box)MonData3 based on the number of + * arguments. The two functions are aliases of each other, but they + * differ for matching purposes in the caller's codegen. */ +#define GetMonData(...) CAT(GetMonData, NARG_8(__VA_ARGS__))(__VA_ARGS__) +#define GetBoxMonData(...) CAT(GetBoxMonData, NARG_8(__VA_ARGS__))(__VA_ARGS__) +u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data); +u32 GetMonData2(struct Pokemon *mon, s32 field); +u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data); +u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field); void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg); void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg); diff --git a/src/daycare.c b/src/daycare.c index c3f4dd990..9d199f0f3 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -341,7 +341,7 @@ static void Debug_AddDaycareSteps(u16 numSteps) u8 GetNumLevelsGainedFromDaycare(void) { - if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004], MON_DATA_SPECIES) != 0) + if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004].mon, MON_DATA_SPECIES) != 0) return GetNumLevelsGainedForDaycareMon(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004]); return 0; diff --git a/src/pokemon.c b/src/pokemon.c index fa2eee42c..b8f7ae48b 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3622,7 +3622,11 @@ static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 perso return substruct; } -u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data) +/* GameFreak called GetMonData with either 2 or 3 arguments, for type + * safety we have a GetMonData macro (in include/pokemon.h) which + * dispatches to either GetMonData2 or GetMonData3 based on the number + * of arguments. */ +u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data) { u32 ret; @@ -3690,7 +3694,13 @@ u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data) return ret; } -u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data) +u32 GetMonData2(struct Pokemon *mon, s32 field) __attribute__((alias("GetMonData3"))); + +/* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type + * safety we have a GetBoxMonData macro (in include/pokemon.h) which + * dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the + * number of arguments. */ +u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) { s32 i; u32 retVal = 0; @@ -4046,6 +4056,8 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data) return retVal; } +u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field) __attribute__((alias("GetBoxMonData3"))); + #define SET8(lhs) (lhs) = *data #define SET16(lhs) (lhs) = data[0] + (data[1] << 8) #define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24) From 31a3ada7de629aa28c828762352b1d307113a1bd Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 24 Jul 2023 19:04:23 +0100 Subject: [PATCH 14/18] Detect potential misalignment in modern --- include/decompress.h | 2 +- include/gba/m4a_internal.h | 2 +- include/gba/macro.h | 83 ++++++++++++++++++++++++++++++++--- include/gba/syscall.h | 22 ++++++++++ include/global.h | 2 +- include/graphics.h | 6 +-- include/item_menu.h | 2 +- include/librfu.h | 2 +- include/link.h | 2 +- include/mon_markings.h | 2 +- include/palette.h | 6 +-- include/scanline_effect.h | 2 +- src/battle_factory_screen.c | 2 +- src/berry_fix_program.c | 2 +- src/contest.c | 4 +- src/decoration.c | 2 +- src/field_weather.c | 6 +-- src/fieldmap.c | 2 +- src/graphics.c | 4 +- src/item_menu.c | 6 +-- src/link.c | 2 +- src/mirage_tower.c | 2 +- src/palette.c | 2 +- src/pokedex_area_region_map.c | 2 +- src/pokemon_storage_system.c | 4 +- src/pokenav_conditions_gfx.c | 4 +- src/pokenav_region_map.c | 2 +- src/rayquaza_scene.c | 4 +- src/scanline_effect.c | 2 +- src/util.c | 2 +- 30 files changed, 142 insertions(+), 45 deletions(-) diff --git a/include/decompress.h b/include/decompress.h index 5e33665b7..18340d54b 100644 --- a/include/decompress.h +++ b/include/decompress.h @@ -3,7 +3,7 @@ #include "sprite.h" -extern u8 gDecompressionBuffer[0x4000]; +extern u8 ALIGNED(4) gDecompressionBuffer[0x4000]; void LZDecompressWram(const u32 *src, void *dest); void LZDecompressVram(const u32 *src, void *dest); diff --git a/include/gba/m4a_internal.h b/include/gba/m4a_internal.h index 40a25ba05..fc8205efd 100644 --- a/include/gba/m4a_internal.h +++ b/include/gba/m4a_internal.h @@ -213,7 +213,7 @@ struct SoundInfo ExtVolPitFunc ExtVolPit; u8 gap2[16]; struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS]; - s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2]; + s8 ALIGNED(4) pcmBuffer[PCM_DMA_BUF_SIZE * 2]; }; struct SongHeader diff --git a/include/gba/macro.h b/include/gba/macro.h index 3b35a1946..5239cd4c8 100644 --- a/include/gba/macro.h +++ b/include/gba/macro.h @@ -1,7 +1,7 @@ #ifndef GUARD_GBA_MACRO_H #define GUARD_GBA_MACRO_H -#define CPU_FILL(value, dest, size, bit) \ +#define CPU_FILL_UNCHECKED(value, dest, size, bit) \ { \ vu##bit tmp = (vu##bit)(value); \ CpuSet((void *)&tmp, \ @@ -9,10 +9,33 @@ CPU_SET_##bit##BIT | CPU_SET_SRC_FIXED | ((size)/(bit/8) & 0x1FFFFF)); \ } +#if MODERN +#define CPU_FILL(value, dest, size, bit) \ + do \ + { \ + _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \ + CPU_FILL_UNCHECKED(value, dest, size, bit); \ + } while (0) +#else +#define CPU_FILL(value, dest, size, bit) CPU_FILL_UNCHECKED(value, dest, size, bit) +#endif + #define CpuFill16(value, dest, size) CPU_FILL(value, dest, size, 16) #define CpuFill32(value, dest, size) CPU_FILL(value, dest, size, 32) -#define CPU_COPY(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF)) +#define CPU_COPY_UNCHECKED(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF)) + +#if MODERN +#define CPU_COPY(src, dest, size, bit) \ + do \ + { \ + _Static_assert(_Alignof(src) >= (bit / 8), "source potentially unaligned"); \ + _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \ + CPU_COPY_UNCHECKED(src, dest, size, bit); \ + } while (0) +#else +#define CPU_COPY(src, dest, size, bit) CPU_COPY_UNCHECKED(src, dest, size, bit) +#endif #define CpuCopy16(src, dest, size) CPU_COPY(src, dest, size, 16) #define CpuCopy32(src, dest, size) CPU_COPY(src, dest, size, 32) @@ -31,7 +54,7 @@ #define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF)) -#define DmaSet(dmaNum, src, dest, control) \ +#define DmaSetUnchecked(dmaNum, src, dest, control) \ { \ vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \ dmaRegs[0] = (vu32)(src); \ @@ -40,7 +63,21 @@ dmaRegs[2]; \ } -#define DMA_FILL(dmaNum, value, dest, size, bit) \ +#if MODERN +// NOTE: Assumes 16-bit DMAs. +#define DmaSet(dmaNum, src, dest, control) \ + do \ + { \ + _Static_assert(_Alignof(src) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & (DMA_32BIT << 16)) ? 4 : 2, 2), "source potentially unaligned"); \ + _Static_assert(_Alignof(dest) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & (DMA_32BIT << 16)) ? 4 : 2, 2), "destination potentially unaligned"); \ + DmaSetUnchecked(dmaNum, src, dest, control); \ + } while (0) +#else +#define DmaSet(dmaNum, src, dest, control) \ + DmaSetUnchecked(dmaNum, src, dest, control) +#endif + +#define DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit) \ { \ vu##bit tmp = (vu##bit)(value); \ DmaSet(dmaNum, \ @@ -50,6 +87,17 @@ | ((size)/(bit/8))); \ } +#if MODERN +#define DMA_FILL(dmaNum, value, dest, size, bit) \ + do \ + { \ + _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \ + DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit); \ + } while (0) +#else +#define DMA_FILL(dmaNum, value, dest, size, bit) DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit) +#endif + #define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16) #define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32) @@ -58,23 +106,46 @@ // unit size (2 or 4 bytes) and then combined with the DMA control flags using a // bitwise OR operation. -#define DMA_CLEAR(dmaNum, dest, size, bit) \ +#define DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit) \ { \ vu##bit *_dest = (vu##bit *)(dest); \ u32 _size = size; \ DmaFill##bit(dmaNum, 0, _dest, _size); \ } +#if MODERN +#define DMA_CLEAR(dmaNum, dest, size, bit) \ + do \ + { \ + _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \ + DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit); \ + } while (0) +#else +#define DMA_CLEAR(dmaNum, dest, size, bit) DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit) +#endif + #define DmaClear16(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 16) #define DmaClear32(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 32) -#define DMA_COPY(dmaNum, src, dest, size, bit) \ +#define DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit) \ DmaSet(dmaNum, \ src, \ dest, \ (DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \ | ((size)/(bit/8))) +#if MODERN +#define DMA_COPY(dmaNum, src, dest, size, bit) \ + do \ + { \ + _Static_assert(_Alignof(src) >= (bit / 8), "source potentially unaligned"); \ + _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \ + DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit); \ + } while (0) +#else +#define DMA_COPY(dmaNum, src, dest, size, bit) DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit) +#endif + #define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16) #define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32) diff --git a/include/gba/syscall.h b/include/gba/syscall.h index 56cd4ba58..c922084d5 100644 --- a/include/gba/syscall.h +++ b/include/gba/syscall.h @@ -27,10 +27,32 @@ u16 ArcTan2(s16 x, s16 y); void CpuSet(const void *src, void *dest, u32 control); +#if MODERN +// NOTE: Assumes 16-bit CpuSets unless control is a constant and has +// CPU_SET_32BIT set. +#define CpuSet(src, dest, control) \ + do \ + { \ + _Static_assert(_Alignof(src) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & CPU_SET_32BIT) ? 4 : 2, 2), "source potentially unaligned"); \ + _Static_assert(_Alignof(dest) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & CPU_SET_32BIT) ? 4 : 2, 2), "destination potentially unaligned"); \ + CpuSet(src, dest, control); \ + } while (0) +#endif + #define CPU_FAST_SET_SRC_FIXED 0x01000000 void CpuFastSet(const void *src, void *dest, u32 control); +#if MODERN +#define CpuFastSet(src, dest, control) \ + do \ + { \ + _Static_assert(_Alignof(src) >= 4, "source potentially unaligned"); \ + _Static_assert(_Alignof(dest) >= 4, "destination potentially unaligned"); \ + CpuFastSet(src, dest, control); \ + } while (0) +#endif + void BgAffineSet(struct BgAffineSrcData *src, struct BgAffineDstData *dest, s32 count); void ObjAffineSet(struct ObjAffineSrcData *src, void *dest, s32 count, s32 offset); diff --git a/include/global.h b/include/global.h index 00b08f48f..e0cecafd0 100644 --- a/include/global.h +++ b/include/global.h @@ -845,7 +845,7 @@ struct WaldaPhrase struct TrainerNameRecord { u32 trainerId; - u8 trainerName[PLAYER_NAME_LENGTH + 1]; + u8 ALIGNED(2) trainerName[PLAYER_NAME_LENGTH + 1]; }; struct TrainerHillSave diff --git a/include/graphics.h b/include/graphics.h index 135eaa95e..04ad64606 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -3893,7 +3893,7 @@ extern const u32 gIntroGroudon_Gfx[]; extern const u32 gIntroGroudon_Tilemap[]; extern const u32 gIntroLegendBg_Gfx[]; extern const u32 gIntroGroudonBg_Tilemap[]; -extern const u8 gIntro3Bg_Pal[0x200]; +extern const u8 ALIGNED(2) gIntro3Bg_Pal[0x200]; extern const u32 gIntroKyogre_Gfx[]; extern const u32 gIntroKyogre_Tilemap[]; extern const u32 gIntroKyogreBg_Tilemap[]; @@ -5018,8 +5018,8 @@ extern const u32 gPokenavOptions_Gfx[]; extern const u16 gPokenavOptions_Pal[]; // Battle Factory Screen -extern const u8 gFrontierFactorySelectMenu_Gfx[]; -extern const u8 gFrontierFactorySelectMenu_Tilemap[]; +extern const u16 gFrontierFactorySelectMenu_Gfx[]; +extern const u16 gFrontierFactorySelectMenu_Tilemap[]; extern const u16 gFrontierFactorySelectMenu_Pal[]; // Object event pals diff --git a/include/item_menu.h b/include/item_menu.h index ce03cdacb..09ddd729c 100644 --- a/include/item_menu.h +++ b/include/item_menu.h @@ -78,7 +78,7 @@ struct BagMenu u8 numShownItems[POCKETS_COUNT]; s16 graphicsLoadState; u8 unused2[14]; - u8 pocketNameBuffer[32][32]; + u8 ALIGNED(4) pocketNameBuffer[32][32]; u8 unused3[4]; }; diff --git a/include/librfu.h b/include/librfu.h index 0026adece..6b0bd97c7 100644 --- a/include/librfu.h +++ b/include/librfu.h @@ -329,7 +329,7 @@ struct RfuIntrStruct { union RfuPacket rxPacketAlloc; union RfuPacket txPacketAlloc; - u8 block1[0x960]; // size of librfu_intr.s binary + u8 ALIGNED(2) block1[0x960]; // size of librfu_intr.s binary struct STWIStatus block2; }; diff --git a/include/link.h b/include/link.h index f27cddc62..66dd5fecd 100644 --- a/include/link.h +++ b/include/link.h @@ -238,7 +238,7 @@ struct BlockRequest }; extern struct Link gLink; -extern u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH]; +extern u16 ALIGNED(4) gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH]; extern u8 gBlockSendBuffer[BLOCK_BUFFER_SIZE]; extern u16 gLinkType; extern u32 gLinkStatus; diff --git a/include/mon_markings.h b/include/mon_markings.h index fda7ad563..dbb53f8e8 100644 --- a/include/mon_markings.h +++ b/include/mon_markings.h @@ -18,7 +18,7 @@ struct MonMarkingsMenu struct Sprite *textSprite; const u8 *frameTiles; const u16 *framePalette; - u8 windowSpriteTiles[0x1000]; + u8 ALIGNED(2) windowSpriteTiles[0x1000]; u8 unused[0x80]; u8 tileLoadState; }; diff --git a/include/palette.h b/include/palette.h index d23a658b4..15c92cc2a 100644 --- a/include/palette.h +++ b/include/palette.h @@ -54,9 +54,9 @@ struct PaletteFadeControl extern struct PaletteFadeControl gPaletteFade; extern u32 gPlttBufferTransferPending; -extern u8 gPaletteDecompressionBuffer[]; -extern u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE]; -extern u16 gPlttBufferFaded[PLTT_BUFFER_SIZE]; +extern u8 ALIGNED(4) gPaletteDecompressionBuffer[]; +extern u16 ALIGNED(4) gPlttBufferUnfaded[PLTT_BUFFER_SIZE]; +extern u16 ALIGNED(4) gPlttBufferFaded[PLTT_BUFFER_SIZE]; void LoadCompressedPalette(const u32 *src, u16 offset, u16 size); void LoadPalette(const void *src, u16 offset, u16 size); diff --git a/include/scanline_effect.h b/include/scanline_effect.h index ae534d969..80d9df764 100644 --- a/include/scanline_effect.h +++ b/include/scanline_effect.h @@ -37,7 +37,7 @@ struct ScanlineEffect extern struct ScanlineEffect gScanlineEffect; -extern u16 gScanlineEffectRegBuffers[2][0x3C0]; +extern u16 ALIGNED(4) gScanlineEffectRegBuffers[2][0x3C0]; void ScanlineEffect_Stop(void); void ScanlineEffect_Clear(void); diff --git a/src/battle_factory_screen.c b/src/battle_factory_screen.c index 081295323..c5633ce28 100644 --- a/src/battle_factory_screen.c +++ b/src/battle_factory_screen.c @@ -268,7 +268,7 @@ static const u8 sActionHighlightMiddle_Gfx[] = INCBIN_U8( "graphics/battle_front static const u8 sActionHighlightRight_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/action_highlight_right.4bpp"); static const u8 sMonPicBgAnim_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg_anim.4bpp"); static const u8 sMonPicBg_Tilemap[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg.bin"); -static const u8 sMonPicBg_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg.4bpp"); +static const u16 sMonPicBg_Gfx[] = INCBIN_U16("graphics/battle_frontier/factory_screen/mon_pic_bg.4bpp"); static const u16 sMonPicBg_Pal[] = INCBIN_U16("graphics/battle_frontier/factory_screen/mon_pic_bg.gbapal"); static const struct SpriteSheet sSelect_SpriteSheets[] = diff --git a/src/berry_fix_program.c b/src/berry_fix_program.c index af21bb929..26af445a0 100644 --- a/src/berry_fix_program.c +++ b/src/berry_fix_program.c @@ -117,7 +117,7 @@ static const struct WindowTemplate sBerryFixWindowTemplates[] = { DUMMY_WIN_TEMPLATE }; -static const u16 sBerryFixPalColors[] = { +static const u16 ALIGNED(4) sBerryFixPalColors[] = { RGB_WHITE, RGB_WHITE, RGB(12, 12, 12), RGB(26, 26, 25), RGB(28, 1, 1), RGB(31, 23, 14), RGB(4, 19, 1), RGB(18, 30, 18), RGB(6, 10, 25), RGB(20, 24, 30), RGB_WHITE, RGB(12, 12, 12), diff --git a/src/contest.c b/src/contest.c index 84d295f13..741a0e12d 100644 --- a/src/contest.c +++ b/src/contest.c @@ -1301,8 +1301,8 @@ static void Task_ReadyStartLinkContest(u8 taskId) static bool8 SetupContestGraphics(u8 *stateVar) { - u16 tempPalette1[16]; - u16 tempPalette2[16]; + u16 ALIGNED(4) tempPalette1[16]; + u16 ALIGNED(4) tempPalette2[16]; switch (*stateVar) { diff --git a/src/decoration.c b/src/decoration.c index 27ef85de9..b33580d2d 100644 --- a/src/decoration.c +++ b/src/decoration.c @@ -1916,7 +1916,7 @@ static void CopyPalette(u16 *dest, u16 pal) static void CopyTile(u8 *dest, u16 tile) { - u8 buffer[TILE_SIZE_4BPP]; + u8 ALIGNED(4) buffer[TILE_SIZE_4BPP]; u16 mode; u16 i; diff --git a/src/field_weather.c b/src/field_weather.c index 73aef3746..bcc6a1c44 100644 --- a/src/field_weather.c +++ b/src/field_weather.c @@ -61,7 +61,7 @@ static void None_Main(void); static u8 None_Finish(void); EWRAM_DATA struct Weather gWeather = {0}; -EWRAM_DATA static u8 sFieldEffectPaletteColorMapTypes[32] = {0}; +EWRAM_DATA static u8 ALIGNED(2) sFieldEffectPaletteColorMapTypes[32] = {0}; static const u8 *sPaletteColorMapTypes; @@ -111,7 +111,7 @@ void (*const gWeatherPalStateFuncs[])(void) = // This table specifies which of the color maps should be // applied to each of the background and sprite palettes. -static const u8 sBasePaletteColorMapTypes[32] = +static const u8 ALIGNED(2) sBasePaletteColorMapTypes[32] = { // background palettes COLOR_MAP_DARK_CONTRAST, @@ -149,7 +149,7 @@ static const u8 sBasePaletteColorMapTypes[32] = COLOR_MAP_DARK_CONTRAST, }; -const u16 gFogPalette[] = INCBIN_U16("graphics/weather/fog.gbapal"); +const u16 ALIGNED(4) gFogPalette[] = INCBIN_U16("graphics/weather/fog.gbapal"); void StartWeather(void) { diff --git a/src/fieldmap.c b/src/fieldmap.c index bdacf7ab2..9534255c7 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -25,7 +25,7 @@ struct ConnectionFlags u8 east:1; }; -EWRAM_DATA static u16 sBackupMapData[MAX_MAP_DATA_SIZE] = {0}; +EWRAM_DATA static u16 ALIGNED(4) sBackupMapData[MAX_MAP_DATA_SIZE] = {0}; EWRAM_DATA struct MapHeader gMapHeader = {0}; EWRAM_DATA struct Camera gCamera = {0}; EWRAM_DATA static struct ConnectionFlags sMapConnectionFlags = {0}; diff --git a/src/graphics.c b/src/graphics.c index e8f891d8b..3cfd47ea7 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1219,8 +1219,8 @@ const u32 gRouletteMultiplier_Gfx[] = INCBIN_U32("graphics/roulette/multiplier.4 const u16 gFrontierFactorySelectMenu_Pal[] = INCBIN_U16("graphics/battle_frontier/factory_menu1.gbapal"); const u16 gFrontierFactorySelectMenu_Pal2[] = INCBIN_U16("graphics/battle_frontier/factory_menu2.gbapal"); -const u8 gFrontierFactorySelectMenu_Gfx[] = INCBIN_U8("graphics/battle_frontier/factory_menu1.4bpp"); -const u8 gFrontierFactorySelectMenu_Gfx2[] = INCBIN_U8("graphics/battle_frontier/factory_menu2.4bpp"); +const u16 gFrontierFactorySelectMenu_Gfx[] = INCBIN_U16("graphics/battle_frontier/factory_menu1.4bpp"); +const u16 gFrontierFactorySelectMenu_Gfx2[] = INCBIN_U16("graphics/battle_frontier/factory_menu2.4bpp"); const u16 gFrontierFactorySelectMenu_Tilemap[] = INCBIN_U16("graphics/battle_frontier/factory_menu.bin"); diff --git a/src/item_menu.c b/src/item_menu.c index 00449c6ec..223f028d0 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -2429,16 +2429,16 @@ static void PrintPocketNames(const u8 *pocketName1, const u8 *pocketName2) static void CopyPocketNameToWindow(u32 a) { - u8 (* tileDataBuffer)[32][32]; + u8 (*tileDataBuffer)[32][32]; u8 *windowTileData; int b; if (a > 8) a = 8; tileDataBuffer = &gBagMenu->pocketNameBuffer; windowTileData = (u8 *)GetWindowAttribute(2, WINDOW_TILE_DATA); - CpuCopy32(tileDataBuffer[0][a], windowTileData, 0x100); // Top half of pocket name + CpuCopy32(&tileDataBuffer[0][a], windowTileData, 0x100); // Top half of pocket name b = a + 16; - CpuCopy32(tileDataBuffer[0][b], windowTileData + 0x100, 0x100); // Bottom half of pocket name + CpuCopy32(&tileDataBuffer[0][b], windowTileData + 0x100, 0x100); // Bottom half of pocket name CopyWindowToVram(WIN_POCKET_NAME, COPYWIN_GFX); } diff --git a/src/link.c b/src/link.c index 0894021b5..c51abc952 100644 --- a/src/link.c +++ b/src/link.c @@ -78,7 +78,7 @@ bool8 gRemoteLinkPlayersNotReceived[MAX_LINK_PLAYERS]; u8 gBlockReceivedStatus[MAX_LINK_PLAYERS]; u32 gLinkFiller2; u16 gLinkHeldKeys; -u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH]; +u16 ALIGNED(4) gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH]; u32 gLinkStatus; bool8 gLinkDummy1; // Never read bool8 gLinkDummy2; // Never read diff --git a/src/mirage_tower.c b/src/mirage_tower.c index f9806e776..9b48ee24b 100644 --- a/src/mirage_tower.c +++ b/src/mirage_tower.c @@ -75,7 +75,7 @@ static void Task_FossilFallAndSink(u8); static void SpriteCB_FallingFossil(struct Sprite *); static void UpdateDisintegrationEffect(u8 *, u16, u8, u8, u8); -static const u8 sBlankTile_Gfx[32] = {0}; +static const u8 ALIGNED(2) sBlankTile_Gfx[32] = {0}; static const u8 sMirageTower_Gfx[] = INCBIN_U8("graphics/misc/mirage_tower.4bpp"); static const u16 sMirageTowerTilemap[] = INCBIN_U16("graphics/misc/mirage_tower.bin"); static const u16 sFossil_Pal[] = INCBIN_U16("graphics/object_events/pics/misc/fossil.gbapal"); // Unused diff --git a/src/palette.c b/src/palette.c index 6ce47a493..43a4c213c 100644 --- a/src/palette.c +++ b/src/palette.c @@ -64,7 +64,7 @@ static EWRAM_DATA struct PaletteStruct sPaletteStructs[NUM_PALETTE_STRUCTS] = {0 EWRAM_DATA struct PaletteFadeControl gPaletteFade = {0}; static EWRAM_DATA u32 sFiller = 0; static EWRAM_DATA u32 sPlttBufferTransferPending = 0; -EWRAM_DATA u8 gPaletteDecompressionBuffer[PLTT_SIZE] = {0}; +EWRAM_DATA u8 ALIGNED(2) gPaletteDecompressionBuffer[PLTT_SIZE] = {0}; static const struct PaletteStructTemplate sDummyPaletteStructTemplate = { .id = 0xFFFF, diff --git a/src/pokedex_area_region_map.c b/src/pokedex_area_region_map.c index da50f8c68..cd2975473 100644 --- a/src/pokedex_area_region_map.c +++ b/src/pokedex_area_region_map.c @@ -8,7 +8,7 @@ static EWRAM_DATA u8 *sPokedexAreaMapBgNum = NULL; -static const u16 sPokedexAreaMap_Pal[] = INCBIN_U16("graphics/pokedex/region_map.gbapal"); +static const u16 ALIGNED(4) sPokedexAreaMap_Pal[] = INCBIN_U16("graphics/pokedex/region_map.gbapal"); static const u32 sPokedexAreaMap_Gfx[] = INCBIN_U32("graphics/pokedex/region_map.8bpp.lz"); static const u32 sPokedexAreaMap_Tilemap[] = INCBIN_U32("graphics/pokedex/region_map.bin.lz"); static const u32 sPokedexAreaMapAffine_Gfx[] = INCBIN_U32("graphics/pokedex/region_map_affine.8bpp.lz"); diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index 99b0dae0e..9db0d77c6 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -550,8 +550,8 @@ struct PokemonStorageSystemData u16 *displayMonTilePtr; struct Sprite *displayMonSprite; u16 displayMonPalBuffer[0x40]; - u8 tileBuffer[MON_PIC_SIZE * MAX_MON_PIC_FRAMES]; - u8 itemIconBuffer[0x800]; + u8 ALIGNED(4) tileBuffer[MON_PIC_SIZE * MAX_MON_PIC_FRAMES]; + u8 ALIGNED(4) itemIconBuffer[0x800]; u8 wallpaperBgTilemapBuffer[0x1000]; u8 displayMenuTilemapBuffer[0x800]; }; diff --git a/src/pokenav_conditions_gfx.c b/src/pokenav_conditions_gfx.c index e382cbf69..9f4b2db61 100644 --- a/src/pokenav_conditions_gfx.c +++ b/src/pokenav_conditions_gfx.c @@ -116,10 +116,12 @@ static const LoopedTask sLoopedTaskFuncs[] = [CONDITION_FUNC_CLOSE_MARKINGS] = LoopedTask_CloseMonMarkingsWindow }; +typedef u8 ALIGNED(4) TilemapBuffer[BG_SCREEN_SIZE]; + struct Pokenav_ConditionMenuGfx { u32 loopedTaskId; - u8 tilemapBuffers[3][BG_SCREEN_SIZE]; + TilemapBuffer tilemapBuffers[3]; u8 filler[2]; u8 partyPokeballSpriteIds[PARTY_SIZE + 1]; u32 (*callback)(void); diff --git a/src/pokenav_region_map.c b/src/pokenav_region_map.c index e589e2818..f81ff4296 100755 --- a/src/pokenav_region_map.c +++ b/src/pokenav_region_map.c @@ -35,7 +35,7 @@ struct Pokenav_RegionMapGfx u32 loopTaskId; u16 infoWindowId; struct Sprite *cityZoomTextSprites[3]; - u8 tilemapBuffer[BG_SCREEN_SIZE]; + u8 ALIGNED(2) tilemapBuffer[BG_SCREEN_SIZE]; u8 cityZoomPics[NUM_CITY_MAPS][200]; }; diff --git a/src/rayquaza_scene.c b/src/rayquaza_scene.c index c984dfec8..cacadf528 100644 --- a/src/rayquaza_scene.c +++ b/src/rayquaza_scene.c @@ -60,10 +60,12 @@ enum #define MAX_SMOKE 10 +typedef u8 ALIGNED(4) TilemapBuffer[BG_SCREEN_SIZE]; + struct RayquazaScene { MainCallback exitCallback; - u8 tilemapBuffers[4][BG_SCREEN_SIZE]; + TilemapBuffer tilemapBuffers[4]; u16 unk; // never read u8 animId; bool8 endEarly; diff --git a/src/scanline_effect.c b/src/scanline_effect.c index dc3ca03f4..684c89546 100644 --- a/src/scanline_effect.c +++ b/src/scanline_effect.c @@ -13,7 +13,7 @@ static void CopyValue32Bit(void); // Per-scanline register values. // This is double buffered so that it can be safely written to at any time // without overwriting the buffer that the DMA is currently reading -EWRAM_DATA u16 gScanlineEffectRegBuffers[2][0x3C0] = {0}; +EWRAM_DATA u16 ALIGNED(4) gScanlineEffectRegBuffers[2][0x3C0] = {0}; EWRAM_DATA struct ScanlineEffect gScanlineEffect = {0}; EWRAM_DATA static bool8 sShouldStopWaveTask = FALSE; diff --git a/src/util.c b/src/util.c index 32f31a26d..ab5603b86 100644 --- a/src/util.c +++ b/src/util.c @@ -158,7 +158,7 @@ void CopySpriteTiles(u8 shape, u8 size, u8 *tiles, u16 *tilemap, u8 *output) { u8 x, y; s8 i, j; - u8 xflip[32]; + u8 ALIGNED(4) xflip[32]; u8 h = sSpriteDimensions[shape][size][1]; u8 w = sSpriteDimensions[shape][size][0]; From 6f31314c4aed40eb27c7dc2dd58c5fb34964cebd Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 26 Jul 2023 09:42:29 +0200 Subject: [PATCH 15/18] Fix uturn typo in test --- test/ability_rattled.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ability_rattled.c b/test/ability_rattled.c index bddc88751..493e3cfca 100644 --- a/test/ability_rattled.c +++ b/test/ability_rattled.c @@ -82,7 +82,7 @@ SINGLE_BATTLE_TEST("Rattled triggers correctly when hit by U-Turn") // Specific } WHEN { TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); } } SCENE { - MESSAGE("Wobbuffet used U-Turn!"); + MESSAGE("Wobbuffet used U-turn!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); ABILITY_POPUP(opponent, ABILITY_RATTLED); From 1baa57418ed087df597ebb4d5d8ab36fbc840fb0 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 27 Jul 2023 03:33:01 +0200 Subject: [PATCH 16/18] Fixed copyright screen not showing up on certain emulators (#2664) * Fix old intro not showing up on VBA * Update intro.c * Update intro.c * Update intro.c --- src/intro.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/intro.c b/src/intro.c index bfc23a74b..28b4120b1 100644 --- a/src/intro.c +++ b/src/intro.c @@ -1096,6 +1096,10 @@ static u8 SetUpCopyrightScreen(void) REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON; SetSerialCallback(SerialCB_CopyrightScreen); GameCubeMultiBoot_Init(&gMultibootProgramStruct); + // REG_DISPCNT needs to be overwritten the second time, because otherwise the intro won't show up on VBA 1.7.2 and John GBA Lite emulators. + // The REG_DISPCNT overwrite is NOT needed in m-GBA, No$GBA, VBA 1.8.0, My Boy and Pizza Boy GBA emulators. + case 1: + REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON; default: UpdatePaletteFade(); gMain.state++; From 5eec3b2fc3e863326e5bb0f6a1828528c15d7c29 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Sun, 30 Jul 2023 16:50:51 +0200 Subject: [PATCH 17/18] fix various issues with primal weather blocking water/fire type moves (#3138) * fix various issues with primal weather blocking water/fire type moves * forgot to change return to effect=1 * fix bugs --- data/battle_scripts_1.s | 20 +--- include/battle_scripts.h | 3 +- include/battle_util.h | 2 +- include/constants/battle_string_ids.h | 4 + src/battle_message.c | 6 ++ src/battle_script_commands.c | 37 ++++---- src/battle_util.c | 7 +- test/primal_weather.c | 127 ++++++++++++++++++++++++++ 8 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 test/primal_weather.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 39712db73..372e0e8e6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8782,15 +8782,14 @@ BattleScript_DesolateLandActivates:: call BattleScript_ActivateWeatherAbilities end3 -BattleScript_DesolateLandEvaporatesWaterTypeMoves:: - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE +BattleScript_PrimalWeatherBlocksMove:: + jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_ATTACKSTRING_PRINTED, BattleScript_MoveEnd @in case of multi-target moves, if move fails once, no point in printing the message twice + accuracycheck BattleScript_PrintMoveMissed, NO_ACC_CALC_CHECK_LOCK_ON attackstring pause B_WAIT_TIME_SHORT ppreduce - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd - printstring STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT + printfromtable gPrimalWeatherBlocksStringIds waitmessage B_WAIT_TIME_LONG - orword gHitMarker, HITMARKER_STRING_PRINTED goto BattleScript_MoveEnd BattleScript_PrimordialSeaActivates:: @@ -8802,17 +8801,6 @@ BattleScript_PrimordialSeaActivates:: call BattleScript_ActivateWeatherAbilities end3 -BattleScript_PrimordialSeaFizzlesOutFireTypeMoves:: - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - pause B_WAIT_TIME_SHORT - ppreduce - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd - printstring STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN - waitmessage B_WAIT_TIME_LONG - orword gHitMarker, HITMARKER_STRING_PRINTED - goto BattleScript_MoveEnd - BattleScript_DeltaStreamActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp diff --git a/include/battle_scripts.h b/include/battle_scripts.h index b20ac35dd..95f068cf5 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -409,9 +409,8 @@ extern const u8 BattleScript_GulpMissileGorging[]; extern const u8 BattleScript_GulpMissileGulping[]; extern const u8 BattleScript_BattleBondActivatesOnMoveEndAttacker[]; extern const u8 BattleScript_DesolateLandActivates[]; -extern const u8 BattleScript_DesolateLandEvaporatesWaterTypeMoves[]; extern const u8 BattleScript_PrimordialSeaActivates[]; -extern const u8 BattleScript_PrimordialSeaFizzlesOutFireTypeMoves[]; +extern const u8 BattleScript_PrimalWeatherBlocksMove[]; extern const u8 BattleScript_DeltaStreamActivates[]; extern const u8 BattleScript_MysteriousAirCurrentBlowsOn[]; extern const u8 BattleScript_AttackWeakenedByStrongWinds[]; diff --git a/include/battle_util.h b/include/battle_util.h index 15fbe2432..51375000c 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -136,8 +136,8 @@ u8 DoBattlerEndTurnEffects(void); bool8 HandleWishPerishSongOnTurnEnd(void); bool8 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); +u8 AtkCanceller_UnableToUseMove(u32 moveType); void SetAtkCancellerForCalledMove(void); -u8 AtkCanceller_UnableToUseMove(void); u8 AtkCanceller_UnableToUseMove2(void); bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2); bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility); diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 174c611e2..0597612d1 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -827,6 +827,10 @@ #define B_MSG_SOMEONES_BOX_FULL 2 #define B_MSG_LANETTES_BOX_FULL 3 +// gPrimalWeatherBlocksStringIds +#define B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN 0 +#define B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN 1 + // gInobedientStringIds #define B_MSG_LOAFING 0 #define B_MSG_WONT_OBEY 1 diff --git a/src/battle_message.c b/src/battle_message.c index 1585f8c47..0613906c3 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -1850,6 +1850,12 @@ const u16 gWeatherStartsStringIds[] = [WEATHER_ABNORMAL] = STRINGID_ITISRAINING }; +const u16 gPrimalWeatherBlocksStringIds[] = +{ + [B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN] = STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN, + [B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN] = STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT, +}; + const u16 gInobedientStringIds[] = { [B_MSG_LOAFING] = STRINGID_PKMNLOAFING, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ccd4a404f..cf793bf24 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1503,25 +1503,8 @@ static void Cmd_attackcanceler(void) s32 i, moveType; u16 attackerAbility = GetBattlerAbility(gBattlerAttacker); - GET_MOVE_TYPE(gCurrentMove, moveType); - if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power) - { - if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL)) - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_PrimordialSeaFizzlesOutFireTypeMoves; - return; - } - else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL)) - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_DesolateLandEvaporatesWaterTypeMoves; - return; - } - } - if (gBattleOutcome != 0) { gCurrentActionFuncId = B_ACTION_FINISHED; @@ -1537,9 +1520,27 @@ static void Cmd_attackcanceler(void) if (TryAegiFormChange()) return; #endif - if (AtkCanceller_UnableToUseMove()) + if (AtkCanceller_UnableToUseMove(moveType)) return; + if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power) + { + if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; + return; + } + else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; + return; + } + } + if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF && GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND && IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker) diff --git a/src/battle_util.c b/src/battle_util.c index d549b71b9..168d5432d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3389,10 +3389,9 @@ void SetAtkCancellerForCalledMove(void) gBattleStruct->isAtkCancelerForCalledMove = TRUE; } -u8 AtkCanceller_UnableToUseMove(void) +u8 AtkCanceller_UnableToUseMove(u32 moveType) { u8 effect = 0; - s32 *bideDmg = &gBattleScripting.bideDmg; do { switch (gBattleStruct->atkCancellerTracker) @@ -3653,7 +3652,7 @@ u8 AtkCanceller_UnableToUseMove(void) if (gTakenDmg[gBattlerAttacker]) { gCurrentMove = MOVE_BIDE; - *bideDmg = gTakenDmg[gBattlerAttacker] * 2; + gBattleScripting.bideDmg = gTakenDmg[gBattlerAttacker] * 2; gBattlerTarget = gTakenDmgByBattler[gBattlerAttacker]; if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetMoveTarget(MOVE_BIDE, MOVE_TARGET_SELECTED + 1); @@ -3723,8 +3722,6 @@ u8 AtkCanceller_UnableToUseMove(void) case CANCELLER_POWDER_STATUS: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER) { - u32 moveType; - GET_MOVE_TYPE(gCurrentMove, moveType); if (moveType == TYPE_FIRE) { gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE; diff --git a/test/primal_weather.c b/test/primal_weather.c new file mode 100644 index 000000000..650a79921 --- /dev/null +++ b/test/primal_weather.c @@ -0,0 +1,127 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_EMBER].power != 0); + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_WATER_GUN].power != 0); + ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER); +} + +SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves") +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + MESSAGE("Foe Wobbuffet used Ember!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT HP_BAR(player); + MESSAGE("Foe Wobbuffet used Ember!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT HP_BAR(player); + } THEN { + EXPECT_EQ(player->hp, player->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0); + ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH); + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}} + PLAYER(SPECIES_WOBBUFFET) {Speed(5);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(10);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(8);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_ERUPTION); } + } SCENE { + MESSAGE("Foe Wobbuffet used Eruption!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + } THEN { + EXPECT_EQ(playerLeft->hp, playerLeft->maxHP); + EXPECT_EQ(playerRight->hp, playerRight->maxHP); + } +} + +SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + MESSAGE("Foe Wobbuffet is fast asleep."); + } +} + +SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves") +{ + GIVEN { + PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_WATER_GUN); } + TURN { MOVE(opponent, MOVE_WATER_GUN); } + } SCENE { + MESSAGE("Foe Wobbuffet used Water Gun!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); + MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + NOT HP_BAR(player); + MESSAGE("Foe Wobbuffet used Water Gun!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); + MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + NOT HP_BAR(player); + } THEN { + EXPECT_EQ(player->hp, player->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves and prints the message only once with moves hitting multiple targets") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_SURF].power != 0); + ASSUME(gBattleMoves[MOVE_SURF].type == TYPE_WATER); + ASSUME(gBattleMoves[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB); {Speed(5);}} + PLAYER(SPECIES_WOBBUFFET) {Speed(5);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(10);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(8);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SURF); } + } SCENE { + MESSAGE("Foe Wobbuffet used Surf!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft); + MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + } THEN { + EXPECT_EQ(playerLeft->hp, playerLeft->maxHP); + EXPECT_EQ(playerRight->hp, playerRight->maxHP); + EXPECT_EQ(opponentRight->hp, opponentRight->maxHP); + } +} + +SINGLE_BATTLE_TEST("Desolate Land does not block a move if pokemon is asleep and uses a Water-type move") // Sleep/confusion/paralysis all happen before the check for primal weather +{ + GIVEN { + PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);} + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} + } WHEN { + TURN { MOVE(opponent, MOVE_WATER_GUN); } + } SCENE { + NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + MESSAGE("Foe Wobbuffet is fast asleep."); + } +} From efb98e97c76d453f3ecd7cd773696b18c325632a Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 31 Jul 2023 14:22:48 +0200 Subject: [PATCH 18/18] remove unused config B_CHECK_IF_CHARGED_UP --- include/config/battle.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index 20cb7eb81..10d2648a8 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -118,7 +118,6 @@ #define B_PLUS_MINUS_INTERACTION GEN_LATEST // In Gen5+, Plus and Minus can be activated with themselves and the opposite ability. Before, only the opposing ability could activate it. #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_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #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_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.