From 49c86f86c8e6f933f2ecdda9467eae2730fe14cb Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 20 Apr 2024 22:23:20 +0200 Subject: [PATCH 01/71] Adds Ability Poison Puppeteer (#4416) * Adds Ability Poison Puppeteer * nothing happened * parametrize poison powder and toxic * leftover --- include/battle.h | 1 + src/battle_script_commands.c | 6 ++- src/battle_util.c | 12 +++++ test/battle/ability/poison_puppeteer.c | 73 ++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 test/battle/ability/poison_puppeteer.c diff --git a/include/battle.h b/include/battle.h index 1274ffcbfc..2032c5a3d4 100644 --- a/include/battle.h +++ b/include/battle.h @@ -787,6 +787,7 @@ struct BattleStruct u8 trainerSlideDynamaxMsgDone:1; u8 pledgeMove:1; u8 isSkyBattle:1; + u8 poisonPuppeteerConfusion:1; u32 aiDelayTimer; // Counts number of frames AI takes to choose an action. u32 aiDelayFrames; // Number of frames it took to choose an action. u8 timesGotHit[NUM_BATTLE_SIDES][PARTY_SIZE]; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e3f6615832..feb0149c68 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3085,7 +3085,6 @@ void SetMoveEffect(bool32 primary, bool32 certain) } // for synchronize - if (gBattleScripting.moveEffect == MOVE_EFFECT_POISON || gBattleScripting.moveEffect == MOVE_EFFECT_TOXIC || gBattleScripting.moveEffect == MOVE_EFFECT_PARALYSIS @@ -3094,6 +3093,10 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattleStruct->synchronizeMoveEffect = gBattleScripting.moveEffect; gHitMarker |= HITMARKER_SYNCHRONISE_EFFECT; } + + if (gBattleScripting.moveEffect == MOVE_EFFECT_POISON || gBattleScripting.moveEffect == MOVE_EFFECT_TOXIC) + gBattleStruct->poisonPuppeteerConfusion = TRUE; + return; } else if (statusChanged == FALSE) @@ -6303,6 +6306,7 @@ static void Cmd_moveend(void) gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; + gBattleStruct->poisonPuppeteerConfusion = FALSE; gBattleScripting.moveendState++; break; case MOVEEND_COUNT: diff --git a/src/battle_util.c b/src/battle_util.c index 322efd0fea..b6fb998376 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5762,6 +5762,18 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_POISON_PUPPETEER: + if (gBattleMons[gBattlerAttacker].species == SPECIES_PECHARUNT + && gBattleStruct->poisonPuppeteerConfusion == TRUE + && CanBeConfused(gBattlerTarget)) + { + gBattleStruct->poisonPuppeteerConfusion = FALSE; + gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION; + PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; + effect++; + } } break; case ABILITYEFFECT_MOVE_END_OTHER: // Abilities that activate on *another* battler's moveend: Dancer, Soul-Heart, Receiver, Symbiosis diff --git a/test/battle/ability/poison_puppeteer.c b/test/battle/ability/poison_puppeteer.c new file mode 100644 index 0000000000..af5d5389fa --- /dev/null +++ b/test/battle/ability/poison_puppeteer.c @@ -0,0 +1,73 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ +} + +SINGLE_BATTLE_TEST("Poison Puppeteer confuses target if it was poisoned by a damaging move") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_POISON_STING, MOVE_EFFECT_POISON) == TRUE); + PLAYER(SPECIES_PECHARUNT) { Ability(ABILITY_POISON_PUPPETEER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_POISON_STING); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_STING, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + ABILITY_POPUP(player, ABILITY_POISON_PUPPETEER); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } +} + +SINGLE_BATTLE_TEST("Poison Puppeteer confuses target if it was (badly) poisoned by a status move") +{ + u32 move; + + PARAMETRIZE { move = MOVE_POISON_POWDER; } + PARAMETRIZE { move = MOVE_TOXIC; } + + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_POISON_STING, MOVE_EFFECT_POISON) == TRUE); + PLAYER(SPECIES_PECHARUNT) { Ability(ABILITY_POISON_PUPPETEER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + if (move == MOVE_POISON_POWDER) + STATUS_ICON(opponent, poison: TRUE); + else + STATUS_ICON(opponent, badPoison: TRUE); + ABILITY_POPUP(player, ABILITY_POISON_PUPPETEER); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } +} + +SINGLE_BATTLE_TEST("Poison Puppeteer does not trigger if poison is Toxic Spikes induced") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC_SPIKES].effect == EFFECT_TOXIC_SPIKES); + PLAYER(SPECIES_PECHARUNT) { Ability(ABILITY_POISON_PUPPETEER); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TOXIC_SPIKES);} + TURN { SWITCH(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + NONE_OF { + ABILITY_POPUP(player, ABILITY_POISON_PUPPETEER); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } + } +} From 620c453fbd2c049657c1ea9b15e6278741abcaef Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Sun, 21 Apr 2024 15:10:14 +0300 Subject: [PATCH 02/71] Intimidate and Super Sweet Sugar prints the right message and doesn't play animation when already at -6 (#4406) * Intimidate won't print message or play animation at -6 Super Sweet Syrup included * Update intimidate.c * Update supersweet_syrup.c * Update test/battle/ability/supersweet_syrup.c --------- Co-authored-by: Bassoonian --- data/battle_scripts_1.s | 10 ++++++++++ test/battle/ability/intimidate.c | 26 ++++++++++++++++++++++++++ test/battle/ability/supersweet_syrup.c | 26 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 2e22c0df08..b815c04d6d 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7784,6 +7784,7 @@ BattleScript_IntimidateEffect: statbuffchange STAT_CHANGE_NOT_PROTECT_AFFECTED | STAT_CHANGE_ALLOW_PTR, BattleScript_IntimidateLoopIncrement setgraphicalstatchangevalues jumpifability BS_TARGET, ABILITY_CONTRARY, BattleScript_IntimidateContrary + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_IntimidateWontDecrease playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 printstring STRINGID_PKMNCUTSATTACKWITH BattleScript_IntimidateEffect_WaitString: @@ -7800,6 +7801,10 @@ BattleScript_IntimidateEnd: pause B_WAIT_TIME_MED end3 +BattleScript_IntimidateWontDecrease: + printstring STRINGID_STATSWONTDECREASE + goto BattleScript_IntimidateEffect_WaitString + BattleScript_IntimidateContrary: call BattleScript_AbilityPopUpTarget jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_IntimidateContrary_WontIncrease @@ -7839,6 +7844,7 @@ BattleScript_SupersweetSyrupEffect: statbuffchange STAT_CHANGE_NOT_PROTECT_AFFECTED | STAT_CHANGE_ALLOW_PTR, BattleScript_SupersweetSyrupLoopIncrement setgraphicalstatchangevalues jumpifability BS_TARGET, ABILITY_CONTRARY, BattleScript_SupersweetSyrupContrary + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_SupersweetSyrupWontDecrease playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 printfromtable gStatDownStringIds BattleScript_SupersweetSyrupEffect_WaitString: @@ -7855,6 +7861,10 @@ BattleScript_SupersweetSyrupEnd: pause B_WAIT_TIME_MED end3 +BattleScript_SupersweetSyrupWontDecrease: + printstring STRINGID_STATSWONTDECREASE + goto BattleScript_SupersweetSyrupEffect_WaitString + BattleScript_SupersweetSyrupContrary: call BattleScript_AbilityPopUpTarget jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_SupersweetSyrupContrary_WontIncrease diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index 298e021732..08560ccb00 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -184,3 +184,29 @@ DOUBLE_BATTLE_TEST("Intimidate activates immediately after the mon was switched EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); } } + +SINGLE_BATTLE_TEST("Intimidate can not further lower opponents Atk stat if it is at minimum stages") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_CHARM); } + TURN { MOVE(opponent, MOVE_CHARM); } + TURN { MOVE(opponent, MOVE_CHARM); } + TURN { SWITCH(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent); + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Foe Arbok's Intimidate cuts Wobbuffet's attack!"); + } + MESSAGE("Wobbuffet's Attack won't go lower!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], MIN_STAT_STAGE); + } +} diff --git a/test/battle/ability/supersweet_syrup.c b/test/battle/ability/supersweet_syrup.c index 016c621ee5..6a2fd4fd76 100644 --- a/test/battle/ability/supersweet_syrup.c +++ b/test/battle/ability/supersweet_syrup.c @@ -45,3 +45,29 @@ DOUBLE_BATTLE_TEST("Supersweet Syrup lowers evasion of both opposing mon's in ba EXPECT_EQ(playerRight->statStages[STAT_EVASION], DEFAULT_STAT_STAGE - 1); } } + +SINGLE_BATTLE_TEST("Supersweet Syrup can not further lower opponents evasion if it is at minimum stages") +{ + GIVEN { + PLAYER(SPECIES_ODDISH); + OPPONENT(SPECIES_ODDISH); + OPPONENT(SPECIES_HYDRAPPLE) { Ability(ABILITY_SUPERSWEET_SYRUP); } + } WHEN { + TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + TURN { MOVE(opponent, MOVE_SWEET_SCENT); } + TURN { SWITCH(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWEET_SCENT, opponent); + ABILITY_POPUP(opponent, ABILITY_SUPERSWEET_SYRUP); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Oddish's evasiveness fell!"); + } + MESSAGE("Oddish's evasiveness won't go lower!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_EVASION], MIN_STAT_STAGE); + } +} From 81a73deb767e31756504c1426357faf5a566457c Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 21 Apr 2024 15:40:35 +0200 Subject: [PATCH 03/71] Fixes Quash (#4419) * Fixes Quash * Update test/battle/move_effect/quash.c Co-authored-by: Bassoonian * Update test/battle/move_effect/quash.c Co-authored-by: Bassoonian --------- Co-authored-by: Bassoonian --- asm/macros/battle_script.inc | 10 +- include/constants/battle_script_commands.h | 213 ++++++++++----------- src/battle_script_commands.c | 47 +++-- test/battle/move_effect/quash.c | 45 +++++ 4 files changed, 189 insertions(+), 126 deletions(-) create mode 100644 test/battle/move_effect/quash.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 25e0110a51..260257fc61 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1636,6 +1636,11 @@ callnative BS_TryGulpMissile .endm + .macro tryquash failInstr:req + callnative BS_TryQuash + .4byte \failInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES @@ -1827,11 +1832,6 @@ various \battler, VARIOUS_SET_LAST_USED_ABILITY .endm - .macro tryquash failInstr:req - various BS_ATTACKER, VARIOUS_TRY_QUASH - .4byte \failInstr - .endm - .macro tryafteryou failInstr:req various BS_ATTACKER, VARIOUS_AFTER_YOU .4byte \failInstr diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 641e644ebc..ef8803ce42 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -131,113 +131,112 @@ #define VARIOUS_SET_SIMPLE_BEAM 39 #define VARIOUS_TRY_ENTRAINMENT 40 #define VARIOUS_SET_LAST_USED_ABILITY 41 -#define VARIOUS_TRY_QUASH 42 -#define VARIOUS_INVERT_STAT_STAGES 43 -#define VARIOUS_TRY_ME_FIRST 44 -#define VARIOUS_JUMP_IF_BATTLE_END 45 -#define VARIOUS_TRY_ELECTRIFY 46 -#define VARIOUS_TRY_REFLECT_TYPE 47 -#define VARIOUS_TRY_SOAK 48 -#define VARIOUS_HANDLE_MEGA_EVO 49 -#define VARIOUS_TRY_LAST_RESORT 50 -#define VARIOUS_SET_ARG_TO_BATTLE_DAMAGE 51 -#define VARIOUS_TRY_HIT_SWITCH_TARGET 52 -#define VARIOUS_TRY_AUTOTOMIZE 53 -#define VARIOUS_ABILITY_POPUP 54 -#define VARIOUS_JUMP_IF_TARGET_ALLY 55 -#define VARIOUS_TRY_SYNCHRONOISE 56 -#define VARIOUS_PSYCHO_SHIFT 57 -#define VARIOUS_CURE_STATUS 58 -#define VARIOUS_POWER_TRICK 59 -#define VARIOUS_AFTER_YOU 60 -#define VARIOUS_BESTOW 61 -#define VARIOUS_JUMP_IF_NOT_GROUNDED 62 -#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 63 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 64 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 65 -#define VARIOUS_SET_AURORA_VEIL 66 -#define VARIOUS_TRY_THIRD_TYPE 67 -#define VARIOUS_ACUPRESSURE 68 -#define VARIOUS_SET_POWDER 69 -#define VARIOUS_SPECTRAL_THIEF 70 -#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 71 -#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 72 -#define VARIOUS_JUMP_IF_ROAR_FAILS 73 -#define VARIOUS_TRY_INSTRUCT 74 -#define VARIOUS_JUMP_IF_NOT_BERRY 75 -#define VARIOUS_TRACE_ABILITY 76 -#define VARIOUS_UPDATE_NICK 77 -#define VARIOUS_TRY_ILLUSION_OFF 78 -#define VARIOUS_SET_SPRITEIGNORE0HP 79 -#define VARIOUS_HANDLE_FORM_CHANGE 80 -#define VARIOUS_GET_STAT_VALUE 81 -#define VARIOUS_JUMP_IF_FULL_HP 82 -#define VARIOUS_LOSE_TYPE 83 -#define VARIOUS_TRY_ACTIVATE_SOULHEART 84 -#define VARIOUS_TRY_ACTIVATE_RECEIVER 85 -#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 86 -#define VARIOUS_TRY_FRISK 87 -#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 88 -#define VARIOUS_TRY_FAIRY_LOCK 89 -#define VARIOUS_JUMP_IF_NO_ALLY 90 -#define VARIOUS_POISON_TYPE_IMMUNITY 91 -#define VARIOUS_JUMP_IF_HOLD_EFFECT 92 -#define VARIOUS_INFATUATE_WITH_BATTLER 93 -#define VARIOUS_SET_LAST_USED_ITEM 94 -#define VARIOUS_PARALYZE_TYPE_IMMUNITY 95 -#define VARIOUS_JUMP_IF_ABSENT 96 -#define VARIOUS_DESTROY_ABILITY_POPUP 97 -#define VARIOUS_TOTEM_BOOST 98 -#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 99 -#define VARIOUS_MOVEEND_ITEM_EFFECTS 100 -#define VARIOUS_TERRAIN_SEED 101 -#define VARIOUS_MAKE_INVISIBLE 102 -#define VARIOUS_ROOM_SERVICE 103 -#define VARIOUS_EERIE_SPELL_PP_REDUCE 104 -#define VARIOUS_JUMP_IF_TEAM_HEALTHY 105 -#define VARIOUS_TRY_HEAL_QUARTER_HP 106 -#define VARIOUS_REMOVE_TERRAIN 107 -#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 108 -#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 109 -#define VARIOUS_GET_ROTOTILLER_TARGETS 110 -#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 111 -#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 112 -#define VARIOUS_CONSUME_BERRY 113 -#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 114 -#define VARIOUS_JUMP_IF_SPECIES 115 -#define VARIOUS_UPDATE_ABILITY_POPUP 116 -#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 117 -#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 118 -#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 119 -#define VARIOUS_SHELL_SIDE_ARM_CHECK 120 -#define VARIOUS_TRY_NO_RETREAT 121 -#define VARIOUS_TRY_TAR_SHOT 122 -#define VARIOUS_CAN_TAR_SHOT_WORK 123 -#define VARIOUS_CHECK_POLTERGEIST 124 -#define VARIOUS_CUT_1_3_HP_RAISE_STATS 125 -#define VARIOUS_TRY_END_NEUTRALIZING_GAS 126 -#define VARIOUS_JUMP_IF_UNDER_200 127 -#define VARIOUS_SET_SKY_DROP 128 -#define VARIOUS_CLEAR_SKY_DROP 129 -#define VARIOUS_SKY_DROP_YAWN 130 -#define VARIOUS_CURE_CERTAIN_STATUSES 131 -#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 132 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 133 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 134 -#define VARIOUS_SAVE_BATTLER_ITEM 135 -#define VARIOUS_RESTORE_BATTLER_ITEM 136 -#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 137 -#define VARIOUS_SET_BEAK_BLAST 138 -#define VARIOUS_SWAP_SIDE_STATUSES 139 -#define VARIOUS_SWAP_STATS 140 -#define VARIOUS_TEATIME_INVUL 141 -#define VARIOUS_TEATIME_TARGETS 142 -#define VARIOUS_TRY_WIND_RIDER_POWER 143 -#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 144 -#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 145 -#define VARIOUS_STORE_HEALING_WISH 146 -#define VARIOUS_HIT_SWITCH_TARGET_FAILED 147 -#define VARIOUS_TRY_REVIVAL_BLESSING 148 +#define VARIOUS_INVERT_STAT_STAGES 42 +#define VARIOUS_TRY_ME_FIRST 43 +#define VARIOUS_JUMP_IF_BATTLE_END 44 +#define VARIOUS_TRY_ELECTRIFY 45 +#define VARIOUS_TRY_REFLECT_TYPE 46 +#define VARIOUS_TRY_SOAK 47 +#define VARIOUS_HANDLE_MEGA_EVO 48 +#define VARIOUS_TRY_LAST_RESORT 49 +#define VARIOUS_SET_ARG_TO_BATTLE_DAMAGE 50 +#define VARIOUS_TRY_HIT_SWITCH_TARGET 51 +#define VARIOUS_TRY_AUTOTOMIZE 52 +#define VARIOUS_ABILITY_POPUP 53 +#define VARIOUS_JUMP_IF_TARGET_ALLY 54 +#define VARIOUS_TRY_SYNCHRONOISE 55 +#define VARIOUS_PSYCHO_SHIFT 56 +#define VARIOUS_CURE_STATUS 57 +#define VARIOUS_POWER_TRICK 58 +#define VARIOUS_AFTER_YOU 59 +#define VARIOUS_BESTOW 60 +#define VARIOUS_JUMP_IF_NOT_GROUNDED 61 +#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 62 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 63 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 64 +#define VARIOUS_SET_AURORA_VEIL 65 +#define VARIOUS_TRY_THIRD_TYPE 66 +#define VARIOUS_ACUPRESSURE 67 +#define VARIOUS_SET_POWDER 68 +#define VARIOUS_SPECTRAL_THIEF 69 +#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 70 +#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 71 +#define VARIOUS_JUMP_IF_ROAR_FAILS 72 +#define VARIOUS_TRY_INSTRUCT 73 +#define VARIOUS_JUMP_IF_NOT_BERRY 74 +#define VARIOUS_TRACE_ABILITY 75 +#define VARIOUS_UPDATE_NICK 76 +#define VARIOUS_TRY_ILLUSION_OFF 77 +#define VARIOUS_SET_SPRITEIGNORE0HP 78 +#define VARIOUS_HANDLE_FORM_CHANGE 79 +#define VARIOUS_GET_STAT_VALUE 80 +#define VARIOUS_JUMP_IF_FULL_HP 81 +#define VARIOUS_LOSE_TYPE 82 +#define VARIOUS_TRY_ACTIVATE_SOULHEART 83 +#define VARIOUS_TRY_ACTIVATE_RECEIVER 84 +#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 85 +#define VARIOUS_TRY_FRISK 86 +#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 87 +#define VARIOUS_TRY_FAIRY_LOCK 88 +#define VARIOUS_JUMP_IF_NO_ALLY 89 +#define VARIOUS_POISON_TYPE_IMMUNITY 90 +#define VARIOUS_JUMP_IF_HOLD_EFFECT 91 +#define VARIOUS_INFATUATE_WITH_BATTLER 92 +#define VARIOUS_SET_LAST_USED_ITEM 93 +#define VARIOUS_PARALYZE_TYPE_IMMUNITY 94 +#define VARIOUS_JUMP_IF_ABSENT 95 +#define VARIOUS_DESTROY_ABILITY_POPUP 96 +#define VARIOUS_TOTEM_BOOST 97 +#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 98 +#define VARIOUS_MOVEEND_ITEM_EFFECTS 99 +#define VARIOUS_TERRAIN_SEED 100 +#define VARIOUS_MAKE_INVISIBLE 101 +#define VARIOUS_ROOM_SERVICE 102 +#define VARIOUS_EERIE_SPELL_PP_REDUCE 103 +#define VARIOUS_JUMP_IF_TEAM_HEALTHY 104 +#define VARIOUS_TRY_HEAL_QUARTER_HP 105 +#define VARIOUS_REMOVE_TERRAIN 106 +#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 107 +#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 108 +#define VARIOUS_GET_ROTOTILLER_TARGETS 109 +#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 110 +#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 111 +#define VARIOUS_CONSUME_BERRY 112 +#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 113 +#define VARIOUS_JUMP_IF_SPECIES 114 +#define VARIOUS_UPDATE_ABILITY_POPUP 115 +#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 116 +#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 117 +#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 118 +#define VARIOUS_SHELL_SIDE_ARM_CHECK 119 +#define VARIOUS_TRY_NO_RETREAT 120 +#define VARIOUS_TRY_TAR_SHOT 121 +#define VARIOUS_CAN_TAR_SHOT_WORK 122 +#define VARIOUS_CHECK_POLTERGEIST 123 +#define VARIOUS_CUT_1_3_HP_RAISE_STATS 124 +#define VARIOUS_TRY_END_NEUTRALIZING_GAS 125 +#define VARIOUS_JUMP_IF_UNDER_200 126 +#define VARIOUS_SET_SKY_DROP 127 +#define VARIOUS_CLEAR_SKY_DROP 128 +#define VARIOUS_SKY_DROP_YAWN 129 +#define VARIOUS_CURE_CERTAIN_STATUSES 130 +#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 131 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 132 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 133 +#define VARIOUS_SAVE_BATTLER_ITEM 134 +#define VARIOUS_RESTORE_BATTLER_ITEM 135 +#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 136 +#define VARIOUS_SET_BEAK_BLAST 137 +#define VARIOUS_SWAP_SIDE_STATUSES 138 +#define VARIOUS_SWAP_STATS 139 +#define VARIOUS_TEATIME_INVUL 140 +#define VARIOUS_TEATIME_TARGETS 141 +#define VARIOUS_TRY_WIND_RIDER_POWER 142 +#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 143 +#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 144 +#define VARIOUS_STORE_HEALING_WISH 145 +#define VARIOUS_HIT_SWITCH_TARGET_FAILED 146 +#define VARIOUS_TRY_REVIVAL_BLESSING 147 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d91f1d35d2..28d75cd161 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9585,20 +9585,6 @@ static void Cmd_various(void) gLastUsedAbility = gBattleMons[battler].ability; break; } - case VARIOUS_TRY_QUASH: - { - VARIOUS_ARGS(const u8 *failInstr); - if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) // It's true if foe is faster, has a bigger priority, or switches - { - gBattlescriptCurrInstr = cmd->failInstr; // This replaces the current battlescript with the "fail" script. - } - else // If the condition is not true, it means we are faster than the foe, so we can set the quash bit - { - gProtectStructs[gBattlerTarget].quash = TRUE; - gBattlescriptCurrInstr = cmd->nextInstr; // and then we proceed with the rest of our battlescript - } - return; - } case VARIOUS_INVERT_STAT_STAGES: { VARIOUS_ARGS(); @@ -16839,3 +16825,36 @@ void BS_TryGulpMissile(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + + +void BS_TryQuash(void) +{ + NATIVE_ARGS(const u8 *failInstr); + u32 i; + + // It's true if foe is faster, has a bigger priority, or switches + if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) + { + gBattlescriptCurrInstr = cmd->failInstr; + return; + } + + // If the above condition is not true, it means we are faster than the foe, so we can set the quash bit + gProtectStructs[gBattlerTarget].quash = TRUE; + for (i = 0; i < gBattlersCount; i++) + { + gBattlerByTurnOrder[i] = i; + } + for (i = 0; i < gBattlersCount - 1; i++) + { + s32 j; + for (j = i + 1; j < gBattlersCount; j++) + { + if (!gProtectStructs[i].quash + && !gProtectStructs[j].quash + && GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1) + SwapTurnOrder(i, j); + } + } + gBattlescriptCurrInstr = cmd->nextInstr; +} diff --git a/test/battle/move_effect/quash.c b/test/battle/move_effect/quash.c new file mode 100644 index 0000000000..fd2bd9d877 --- /dev/null +++ b/test/battle/move_effect/quash.c @@ -0,0 +1,45 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_QUASH].effect == EFFECT_QUASH); +} + +DOUBLE_BATTLE_TEST("Quash-affected target will move last in the priority bracket") +{ + GIVEN { + PLAYER(SPECIES_VOLBEAT) { Speed(10); Ability(ABILITY_PRANKSTER); } + PLAYER(SPECIES_WOBBUFFET) { Speed(30); } + OPPONENT(SPECIES_TORCHIC) { Speed(20); } + OPPONENT(SPECIES_TREECKO) { Speed(40); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_QUASH, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Quash is not affected by dynamic speed") +{ + GIVEN { + ASSUME(B_RECALC_TURN_AFTER_ACTIONS >= GEN_8); + ASSUME(gMovesInfo[MOVE_TAILWIND].effect == EFFECT_TAILWIND); + PLAYER(SPECIES_VOLBEAT) { Speed(10); Ability(ABILITY_PRANKSTER); } + PLAYER(SPECIES_WOBBUFFET) { Speed(30); } + OPPONENT(SPECIES_TORCHIC) { Speed(50); } + OPPONENT(SPECIES_TREECKO) { Speed(40); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_QUASH, target: opponentRight); + MOVE(opponentLeft, MOVE_TAILWIND); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} From 9097f438f12340791d706e3fe7d9842d52a6aa22 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 22 Apr 2024 04:58:17 -0300 Subject: [PATCH 04/71] Fixes Will-O-Wisp's capitalization in gMovesInfo (#4425) --- src/data/moves_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 5ae99761ff..6fbe504a77 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -6358,7 +6358,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = [MOVE_WILL_O_WISP] = { - .name = COMPOUND_STRING("Will-o-Wisp"), + .name = COMPOUND_STRING("Will-O-Wisp"), .description = COMPOUND_STRING( "Inflicts a burn on the foe\n" "with intense fire."), From 6bde095ae883cd4ee0d78608839cea22878376c2 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Mon, 22 Apr 2024 10:20:37 +0200 Subject: [PATCH 05/71] Partner fixes (#4422) * Partner fixes * Generalise TRAINER_PARTNER(PARTNER_NONE) --- include/data.h | 4 ++-- src/battle_tower.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/data.h b/include/data.h index feae0d87b2..652f396c03 100644 --- a/include/data.h +++ b/include/data.h @@ -168,14 +168,14 @@ static inline const u8 GetTrainerClassFromId(u16 trainerId) static inline const u8 *GetTrainerClassNameFromId(u16 trainerId) { if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) - return gTrainerClasses[gBattlePartners[trainerId].trainerClass].name; + return gTrainerClasses[gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass].name; return gTrainerClasses[GetTrainerClassFromId(trainerId)].name; } static inline const u8 *GetTrainerNameFromId(u16 trainerId) { if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) - return gBattlePartners[trainerId].trainerName; + return gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName; return gTrainers[SanitizeTrainerId(trainerId)].trainerName; } diff --git a/src/battle_tower.c b/src/battle_tower.c index 477a08742b..cd94e7919b 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1435,7 +1435,7 @@ u8 GetFrontierOpponentClass(u16 trainerId) } else if (trainerId > TRAINER_PARTNER(PARTNER_NONE)) { - trainerClass = gBattlePartners[GetTrainerClassFromId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].trainerClass; + trainerClass = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass; } else if (trainerId < FRONTIER_TRAINERS_COUNT) { From fa53e97f0c1a5e6117637262416c10d409d05afe Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:42:46 +0300 Subject: [PATCH 06/71] Add thunder cage to trapping move array (#4426) * Add thunder cage to trapping move array * align sTrappingMoves vertically --- src/battle_script_commands.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 28d75cd161..f65ed39037 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -317,7 +317,16 @@ static const s32 sExperienceScalingFactors[] = static const u16 sTrappingMoves[NUM_TRAPPING_MOVES] = { - MOVE_BIND, MOVE_WRAP, MOVE_FIRE_SPIN, MOVE_CLAMP, MOVE_WHIRLPOOL, MOVE_SAND_TOMB, MOVE_MAGMA_STORM, MOVE_INFESTATION, MOVE_SNAP_TRAP, + MOVE_BIND, + MOVE_WRAP, + MOVE_FIRE_SPIN, + MOVE_CLAMP, + MOVE_WHIRLPOOL, + MOVE_SAND_TOMB, + MOVE_MAGMA_STORM, + MOVE_INFESTATION, + MOVE_SNAP_TRAP, + MOVE_THUNDER_CAGE }; static const u16 sBadgeFlags[8] = { From d398b9e8e7f0a4ef851395cd55bccd92b1319ba0 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Mon, 22 Apr 2024 03:49:53 -0500 Subject: [PATCH 07/71] Fix ABILITY_POPUP not respecting abilityPopupOverwrite in tests (#4423) --- src/battle_interface.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index d46c95dd3c..59f89ae30c 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3243,6 +3243,9 @@ void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle) if (B_ABILITY_POP_UP == FALSE) return; + if (gBattleScripting.abilityPopupOverwrite != 0) + ability = gBattleScripting.abilityPopupOverwrite; + if (gTestRunnerEnabled) { TestRunner_Battle_RecordAbilityPopUp(battlerId, ability); @@ -3250,9 +3253,6 @@ void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle) return; } - if (gBattleScripting.abilityPopupOverwrite != 0) - ability = gBattleScripting.abilityPopupOverwrite; - if (!gBattleStruct->activeAbilityPopUps) { LoadSpriteSheet(&sSpriteSheet_AbilityPopUp); From b24714a5973d04ee0fce7457854d82ac93c25c91 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Mon, 22 Apr 2024 14:03:55 +0100 Subject: [PATCH 08/71] Fix GiveBoxMonInitialMoveset_Fast --- src/pokemon.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pokemon.c b/src/pokemon.c index 0e145c2e4b..5b1a10a908 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1689,7 +1689,7 @@ void GiveBoxMonInitialMoveset_Fast(struct BoxPokemon *boxMon) //Credit: Asparagu u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL); s32 level = GetLevelFromBoxMonExp(boxMon); s32 i; - u16 moves[MAX_MON_MOVES] = {0}; + u16 moves[MAX_MON_MOVES] = {MOVE_NONE}; u8 addedMoves = 0; const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); @@ -1703,13 +1703,15 @@ void GiveBoxMonInitialMoveset_Fast(struct BoxPokemon *boxMon) //Credit: Asparagu if (learnset[i].level == 0) continue; - for (j = 0; j < addedMoves + 1; j++) + for (j = 0; j < addedMoves; j++) + { if (moves[j] == learnset[i].move) { alreadyKnown = TRUE; break; } - + } + if (!alreadyKnown) { if (addedMoves < MAX_MON_MOVES) From 308475a163413535e63ba5b2d9dcc518a086b2bb Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Mon, 22 Apr 2024 17:03:36 +0200 Subject: [PATCH 09/71] Make trainerproc compatible with partners (#4421) * Prepare stuff for MGriffin to fix * Remove doubleBattle from migration script * Support PARTNER_NONE in trainerproc * Let script apply to battle_partners.h file * TRAINER_BACK_PIC * Let script fix issues --------- Co-authored-by: Martin Griffin --- migration_scripts/convert_partner_parties.py | 319 ++++++++++++++++++ ..._parties.py => convert_trainer_parties.py} | 0 src/battle_tower.c | 3 + src/data/battle_partners.h | 109 +++++- src/data/battle_partners.party | 43 +++ src/data/partner_parties.h | 27 +- tools/trainerproc/main.c | 13 +- 7 files changed, 474 insertions(+), 40 deletions(-) create mode 100644 migration_scripts/convert_partner_parties.py rename migration_scripts/{convert_parties.py => convert_trainer_parties.py} (100%) create mode 100644 src/data/battle_partners.party diff --git a/migration_scripts/convert_partner_parties.py b/migration_scripts/convert_partner_parties.py new file mode 100644 index 0000000000..e726dcc723 --- /dev/null +++ b/migration_scripts/convert_partner_parties.py @@ -0,0 +1,319 @@ +# If you have extra members in 'TrainerMon': +# 1. Add a regular expression which matches that member (e.g. 'shadow_definition'). +# 2. Match that regular expression in 'convert' and write into 'attributes' with the key that 'trainerproc' should parse. +# 3. Add the key used in 'attributes' to 'pokemon_attribute_order'. +# 4. Update 'trainerproc.c' to parse the new key. + +import re +import sys + +is_blank = re.compile(r'^[ \t]*(//.*)?$') + +begin_party_definition = re.compile(r'struct TrainerMon (\w+)\[\] =') +end_party_definition = re.compile(r'^};') +begin_pokemon_definition = re.compile(r'^ { *$') +end_pokemon_definition = re.compile(r'^ },? *$') +level_definition = re.compile(r'\.lvl = (\d+)') +species_definition = re.compile(r'\.species = SPECIES_(\w+)') +gender_definition = re.compile(r'\.gender = TRAINER_MON_(\w+)') +nickname_definition = re.compile(r'\.nickname = COMPOUND_STRING\("([^"]+)"\)') +item_definition = re.compile(r'\.heldItem = ITEM_(\w+)') +ball_definition = re.compile(r'\.ball = ITEM_(\w+)') +ability_definition = re.compile(r'\.ability = ABILITY_(\w+)') +friendship_definition = re.compile(r'\.friendship = (\d+)') +shiny_definition = re.compile(r'\.isShiny = (\w+)') +ivs_definition = re.compile(r'\.iv = TRAINER_PARTY_IVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)') +evs_definition = re.compile(r'\.ev = TRAINER_PARTY_EVS\(([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+),([0-9 ]+)\)') +moves_definition = re.compile(r'\.moves = \{([^}]+)\}') +move_definition = re.compile(r'MOVE_(\w+)') +nature_definition = re.compile(r'\.nature = NATURE_(\w+)') + +# NOTE: These are just for aesthetics, the Pokemon would still compile +# without them. +species_replacements = { + "CHIEN_PAO": "Chien-Pao", + "CHI_YU": "Chi-Yu", + "HAKAMO_O": "Hakamo-o", + "HO_OH": "Ho-Oh", + "JANGMO_O": "Jangmo-o", + "KOMMO_O": "Kommo-o", + "PORYGON_Z": "Porygon-Z", + "ROTOM_": "Rotom-", + "TING_LU": "Ting-Lu", + "TYPE_NULL": "Type: Null", + "WO_CHIEN": "Wo-Chien", + + "_ALOLAN": "-Alola", + "_AQUA_BREED": "-Aqua", + "_BATTLE_BOND": "-Bond", + "_BLAZE_BREED": "-Blaze", + "_CAP": "", + "_CLOAK": "", + "_COMBAT_BREED": "-Combat", + "_CROWED_SHIELD": "-Crowned", + "_CROWED_SWORD": "-Crowned", + "_DRIVE": "", + "_EAST_SEA": "-East", + "_FAMILY_OF_FOUR": "-Four", + "_FEMALE": "-F", + "_FLOWER": "", + "_GALARIAN": "-Galar", + "_GIGANTAMAX": "-Gmax", + "_HISUIAN": "-Hisui", + "_ICE_RIDER": "-Ice", + "_NOICE_FACE": "-Noice", + "_ORIGIN": "-Origin", + "_ORIGINAL_COLOR": "-Original", + "_PALDEAN": "-Paldea", + "_PLUMAGE": "", + "_POKE_BALL": "-Pokeball", + "_SHADOW_RIDER": "-Shadow", + "_STRIKE_STYLE": "-Style", + "_TOTEM": "-Totem", + "_ZEN_MODE": "-Zen", +} + +pokemon_attribute_order = ['Level', 'Ability', 'IVs', 'EVs', 'Happiness', 'Shiny', 'Ball'] + +class Pokemon: + def __init__(self): + self.nickname = None + self.species = None + self.gender = None + self.item = None + self.nature = None + self.attributes = {} + self.attributes['IVs'] = "0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe" + self.moves = [] + +def convert_parties(in_path, in_h): + party_identifier = None + party = None + pokemon = None + parties = {} + + for line_no, line in enumerate(in_h, 1): + try: + line = line[:-1] + if m := begin_party_definition.search(line): + if party: + raise Exception(f"unexpected start of party") + [identifier] = m.groups() + party_identifier = identifier + party = [] + elif end_party_definition.search(line): + if not party: + raise Exception(f"unexpected end of party") + parties[party_identifier] = party + party = None + elif begin_pokemon_definition.search(line): + if pokemon: + raise Exception(f"unexpected start of Pokemon") + pokemon = Pokemon() + elif end_pokemon_definition.search(line): + if not pokemon: + raise Exception(f"unexpected end of Pokemon") + else: + party.append(pokemon) + pokemon = None + elif m := level_definition.search(line): + [level] = m.groups() + pokemon.attributes['Level'] = level + elif m := species_definition.search(line): + [species_] = m.groups() + for match, replacement in species_replacements.items(): + species_ = species_.replace(match, replacement) + pokemon.species = species_.replace("_", " ").title() + elif m := gender_definition.search(line): + [gender_] = m.groups() + if gender_ == 'MALE': + pokemon.gender = 'M' + elif gender_ == 'FEMALE': + pokemon.gender = 'F' + else: + raise Exception(f"unknown gender: '{gender_}'") + elif m := nickname_definition.search(line): + [nickname] = m.groups() + pokemon.nickname = nickname + elif m := item_definition.search(line): + [item_] = m.groups() + pokemon.item = item_.replace("_", " ").title() + elif m := ball_definition.search(line): + [ball] = m.groups() + pokemon.attributes['Ball'] = ball.replace("_", " ").title() + elif m := ability_definition.search(line): + [ability] = m.groups() + pokemon.attributes['Ability'] = ability.replace("_", " ").title() + elif m := friendship_definition.search(line): + [friendship] = m.groups() + pokemon.attributes['Happiness'] = friendship + elif m := shiny_definition.search(line): + [shiny] = m.groups() + if shiny == 'TRUE': + pokemon.attributes['Shiny'] = 'Yes' + elif shiny == 'FALSE': + pokemon.attributes['Shiny'] = 'No' + else: + raise Exception(f"unknown isShiny: '{shiny}'") + elif m := ivs_definition.search(line): + [hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()] + stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed} + pokemon.attributes['IVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items()) + elif m := evs_definition.search(line): + [hp, attack, defense, speed, special_attack, special_defense] = [stat.strip() for stat in m.groups()] + stats = {"HP": hp, "Atk": attack, "Def": defense, "SpA": special_attack, "SpD": special_defense, "Spe": speed} + pokemon.attributes['EVs'] = ' / '.join(f"{value} {key}" for key, value in stats.items() if value != '0') + elif m := moves_definition.search(line): + [moves_] = m.groups() + pokemon.moves = [move.replace("_", " ").title() for move in move_definition.findall(moves_) if move != "NONE"] + elif m := nature_definition.search(line): + [nature_] = m.groups() + pokemon.nature = nature_.replace("_", " ").title() + elif is_blank.search(line): + pass + else: + raise Exception(f"could not parse '{line.strip()}'") + except Exception as e: + print(f"{in_path}:{line_no}: {e}") + return parties + +is_trainer_skip = re.compile(r'(const struct Trainer gBattlePartners\[\] = \{)|(^ \{$)|(\.partySize =)|(\.party = NULL)|(\.mugshotEnabled = TRUE)|(\};)') + +begin_trainer_definition = re.compile(r' \[(PARTNER_\w+)\] =') +end_trainer_definition = re.compile(r' }') +trainer_class_definition = re.compile(r'\.trainerClass = TRAINER_CLASS_(\w+)') +encounter_music_gender_definition = re.compile(r'\.encounterMusic_gender = (F_TRAINER_FEMALE \| )?TRAINER_ENCOUNTER_MUSIC_(\w+)') +trainer_pic_definition = re.compile(r'\.trainerPic = TRAINER_BACK_PIC_(\w+)') +trainer_name_definition = re.compile(r'\.trainerName = _\("([^"]*)"\)') +trainer_items_definition = re.compile(r'\.items = \{([^}]*)\}') +trainer_item_definition = re.compile(r'ITEM_(\w+)') +trainer_ai_flags_definition = re.compile(r'\.aiFlags = (.*)') +trainer_ai_flag_definition = re.compile(r'AI_FLAG_(\w+)') +trainer_party_definition = re.compile(r'\.party = TRAINER_PARTY\((\w+)\)') +trainer_mugshot_definition = re.compile(r'\.mugshotColor = MUGSHOT_COLOR_(\w+)') +trainer_starting_status_definition = re.compile(r'\.startingStatus = STARTING_STATUS_(\w+)') + +class_fixups = { + "Rs": "RS", +} + +pic_fixups = { + "Rs": "RS", +} + +class Trainer: + def __init__(self, id_): + self.id = id_ + self.class_ = None + self.encounter_music = None + self.gender = None + self.pic = None + self.name = None + self.items = [] + self.ai_flags = None + self.mugshot = None + self.starting_status = None + self.party = None + +def convert_trainers(in_path, in_h, parties, out_party): + newlines = 0 + trainer = None + for line_no, line in enumerate(in_h, 1): + try: + line = line[:-1] + if m := begin_trainer_definition.search(line): + if trainer: + raise Exception(f"unexpected start of trainer") + [id_] = m.groups() + trainer = Trainer(id_) + elif m := trainer_class_definition.search(line): + [class_] = m.groups() + class_ = class_.replace("_", " ").title() + for match, replacement in class_fixups.items(): + class_ = class_.replace(match, replacement) + trainer.class_ = class_ + elif m := encounter_music_gender_definition.search(line): + [is_female, music] = m.groups() + trainer.gender = 'Female' if is_female else 'Male' + trainer.encounter_music = music.replace("_", " ").title() + elif m := trainer_pic_definition.search(line): + [pic] = m.groups() + pic = pic.replace("_", " ").title() + for match, replacement in pic_fixups.items(): + pic = pic.replace(match, replacement) + trainer.pic = pic + elif m := trainer_name_definition.search(line): + [name] = m.groups() + trainer.name = name + elif m := trainer_items_definition.search(line): + [items] = m.groups() + trainer.items = " / ".join(item.replace("_", " ").title() for item in trainer_item_definition.findall(items) if item != "NONE") + elif m := trainer_ai_flags_definition.search(line): + [ai_flags] = m.groups() + trainer.ai_flags = " / ".join(ai_flag.replace("_", " ").title() for ai_flag in trainer_ai_flag_definition.findall(ai_flags)) + elif m := trainer_mugshot_definition.search(line): + [color] = m.groups() + trainer.mugshot = color.title() + elif m := trainer_starting_status_definition.search(line): + [starting_status] = m.groups() + trainer.starting_status = starting_status.replace("_", " ").title() + elif m := trainer_party_definition.search(line): + [party] = m.groups() + trainer.party = parties[party] + elif end_trainer_definition.search(line): + if not trainer: + raise Exception(f"unexpected end of trainer") + while newlines > 0: + out_party.write(f"\n") + newlines -= 1 + newlines = 1 + out_party.write(f"=== {trainer.id} ===\n") + out_party.write(f"Name: {trainer.name}\n") + out_party.write(f"Class: {trainer.class_}\n") + out_party.write(f"Pic: {trainer.pic}\n") + out_party.write(f"Gender: {trainer.gender}\n") + out_party.write(f"Music: {trainer.encounter_music}\n") + if trainer.items: + out_party.write(f"Items: {trainer.items}\n") + if trainer.ai_flags: + out_party.write(f"AI: {trainer.ai_flags}\n") + if trainer.mugshot: + out_party.write(f"Mugshot: {trainer.mugshot}\n") + if trainer.starting_status: + out_party.write(f"Starting Status: {trainer.starting_status}\n") + if trainer.party: + for i, pokemon in enumerate(trainer.party): + out_party.write(f"\n") + if pokemon.nickname: + out_party.write(f"{pokemon.nickname} ({pokemon.species})") + else: + out_party.write(f"{pokemon.species}") + if pokemon.gender: + out_party.write(f" ({pokemon.gender})") + if pokemon.item and pokemon.item != 'None': + out_party.write(f" @ {pokemon.item}") + out_party.write(f"\n") + if pokemon.nature: + out_party.write(f"{pokemon.nature} Nature\n") + for key in pokemon_attribute_order: + if key in pokemon.attributes: + out_party.write(f"{key}: {pokemon.attributes[key]}\n") + for move in pokemon.moves: + out_party.write(f"- {move}\n") + trainer = None + elif is_blank.search(line) or is_trainer_skip.search(line): + pass + else: + raise Exception(f"could not parse '{line.strip()}'") + except Exception as e: + print(f"{in_path}:{line_no}: {e}") + +if __name__ == '__main__': + try: + [argv0, trainers_in_path, parties_in_path, out_path] = sys.argv + except: + print(f"usage: python3 {sys.argv[0]} ") + else: + with open(trainers_in_path, "r") as trainers_in_h, open(parties_in_path, "r") as parties_in_h, open(out_path, "w") as out_party: + parties = convert_parties(parties_in_path, parties_in_h) + trainers = convert_trainers(trainers_in_path, trainers_in_h, parties, out_party) diff --git a/migration_scripts/convert_parties.py b/migration_scripts/convert_trainer_parties.py similarity index 100% rename from migration_scripts/convert_parties.py rename to migration_scripts/convert_trainer_parties.py diff --git a/src/battle_tower.c b/src/battle_tower.c index eea9473778..59e9a8aeaf 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -699,7 +699,10 @@ static const u8 *const *const sPartnerApprenticeTextTables[NUM_APPRENTICES] = #include "data/battle_frontier/battle_tent.h" #include "data/partner_parties.h" +const struct Trainer gBattlePartners[] = +{ #include "data/battle_partners.h" +}; static void (* const sBattleTowerFuncs[])(void) = { diff --git a/src/data/battle_partners.h b/src/data/battle_partners.h index 39bb91132f..ae0b753cb3 100644 --- a/src/data/battle_partners.h +++ b/src/data/battle_partners.h @@ -1,20 +1,105 @@ -const struct Trainer gBattlePartners[] = { +// +// DO NOT MODIFY THIS FILE! It is auto-generated from src/data/battle_partners.party +// +// If you want to modify this file set COMPETITIVE_PARTY_SYNTAX to FALSE +// in include/config.h and remove this notice. +// Use sed -i '/^#line/d' 'src/data/battle_partners.h' to remove #line markers. +// + +#line 1 "src/data/battle_partners.party" + +#line 1 [PARTNER_NONE] = { - .party = NULL, +#line 3 .trainerClass = TRAINER_CLASS_PKMN_TRAINER_1, - .encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE, - .trainerPic = TRAINER_PIC_HIKER, - .trainerName = _(""), - .items = {}, +#line 4 + .trainerPic = TRAINER_BACK_PIC_BRENDAN, + .encounterMusic_gender = +#line 6 + TRAINER_ENCOUNTER_MUSIC_MALE, + .partySize = 0, + .party = (const struct TrainerMon[]) + { + }, }, - +#line 8 [PARTNER_STEVEN] = { - .party = TRAINER_PARTY(sParty_StevenPartner), - .trainerClass = TRAINER_CLASS_RIVAL, - .encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE, - .trainerPic = TRAINER_BACK_PIC_STEVEN, +#line 9 .trainerName = _("STEVEN"), +#line 10 + .trainerClass = TRAINER_CLASS_RIVAL, +#line 11 + .trainerPic = TRAINER_BACK_PIC_STEVEN, + .encounterMusic_gender = +#line 13 + TRAINER_ENCOUNTER_MUSIC_MALE, + .partySize = 3, + .party = (const struct TrainerMon[]) + { + { +#line 15 + .species = SPECIES_METANG, + .gender = TRAINER_MON_RANDOM_GENDER, +#line 19 + .ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0), +#line 18 + .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), +#line 17 + .lvl = 42, +#line 16 + .nature = NATURE_BRAVE, + .dynamaxLevel = MAX_DYNAMAX_LEVEL, + .moves = { +#line 20 + MOVE_LIGHT_SCREEN, + MOVE_PSYCHIC, + MOVE_REFLECT, + MOVE_METAL_CLAW, + }, + }, + { +#line 25 + .species = SPECIES_SKARMORY, + .gender = TRAINER_MON_RANDOM_GENDER, +#line 29 + .ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252), +#line 28 + .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), +#line 27 + .lvl = 43, +#line 26 + .nature = NATURE_IMPISH, + .dynamaxLevel = MAX_DYNAMAX_LEVEL, + .moves = { +#line 30 + MOVE_TOXIC, + MOVE_AERIAL_ACE, + MOVE_PROTECT, + MOVE_STEEL_WING, + }, + }, + { +#line 35 + .species = SPECIES_AGGRON, + .gender = TRAINER_MON_RANDOM_GENDER, +#line 39 + .ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6), +#line 38 + .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), +#line 37 + .lvl = 44, +#line 36 + .nature = NATURE_ADAMANT, + .dynamaxLevel = MAX_DYNAMAX_LEVEL, + .moves = { +#line 40 + MOVE_THUNDER, + MOVE_PROTECT, + MOVE_SOLAR_BEAM, + MOVE_DRAGON_CLAW, + }, + }, + }, }, -}; diff --git a/src/data/battle_partners.party b/src/data/battle_partners.party new file mode 100644 index 0000000000..e1ecfe35fa --- /dev/null +++ b/src/data/battle_partners.party @@ -0,0 +1,43 @@ +=== PARTNER_NONE === +Name: +Class: Pkmn Trainer 1 +Pic: Brendan +Gender: Male +Music: Male + +=== PARTNER_STEVEN === +Name: STEVEN +Class: Rival +Pic: Steven +Gender: Male +Music: Male + +Metang +Brave Nature +Level: 42 +IVs: 31 HP / 31 Atk / 31 Def / 31 SpA / 31 SpD / 31 Spe +EVs: 252 Atk / 252 Def / 6 SpA +- Light Screen +- Psychic +- Reflect +- Metal Claw + +Skarmory +Impish Nature +Level: 43 +IVs: 31 HP / 31 Atk / 31 Def / 31 SpA / 31 SpD / 31 Spe +EVs: 252 HP / 6 SpA / 252 SpD +- Toxic +- Aerial Ace +- Protect +- Steel Wing + +Aggron +Adamant Nature +Level: 44 +IVs: 31 HP / 31 Atk / 31 Def / 31 SpA / 31 SpD / 31 Spe +EVs: 252 Atk / 252 SpA / 6 SpD +- Thunder +- Protect +- Solar Beam +- Dragon Claw diff --git a/src/data/partner_parties.h b/src/data/partner_parties.h index 1b071ec28e..8b13789179 100644 --- a/src/data/partner_parties.h +++ b/src/data/partner_parties.h @@ -1,26 +1 @@ -static const struct TrainerMon sParty_StevenPartner[] = { - { - .species = SPECIES_METANG, - .lvl = 42, - .nature = NATURE_BRAVE, - .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), - .ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0), - .moves = {MOVE_LIGHT_SCREEN, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW}, - }, - { - .species = SPECIES_SKARMORY, - .lvl = 43, - .nature = NATURE_IMPISH, - .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), - .ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252), - .moves = {MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING}, - }, - { - .species = SPECIES_AGGRON, - .lvl = 44, - .nature = NATURE_ADAMANT, - .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), - .ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6), - .moves = {MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLAR_BEAM, MOVE_DRAGON_CLAW}, - } -}; + diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index 5b0fbfc3f4..51a2df8968 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -143,6 +143,12 @@ static bool is_literal_string(struct String s1, const char *s2) } } +static bool starts_with(struct String s, const char *prefix) +{ + int n = strlen(prefix); + return strncmp((const char *)s.string, prefix, n) == 0; +} + static bool ends_with(struct String s, const char *suffix) { int n = strlen(suffix); @@ -1194,7 +1200,7 @@ static bool parse_trainer(struct Parser *p, const struct Parsed *parsed, struct while (match_empty_line(p)) {} if (!parse_pokemon_header(p, &nickname, &species, &gender, &item)) { - if (i > 0 || is_literal_string(trainer->id, "TRAINER_NONE")) + if (i > 0 || ends_with(trainer->id, "_NONE")) break; if (!p->error) set_parse_error(p, p->location, "expected nickname or species"); @@ -1614,7 +1620,10 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par { fprintf(f, "#line %d\n", trainer->pic_line); fprintf(f, " .trainerPic = "); - fprint_constant(f, "TRAINER_PIC", trainer->pic); + if (starts_with(trainer->id, "PARTNER_")) + fprint_constant(f, "TRAINER_BACK_PIC", trainer->pic); + else + fprint_constant(f, "TRAINER_PIC", trainer->pic); fprintf(f, ",\n"); } From dbbe39709573e535cfede92144e8db3387dc3d3b Mon Sep 17 00:00:00 2001 From: Eemeliri Date: Tue, 23 Apr 2024 15:16:32 +0300 Subject: [PATCH 10/71] Fix Lycanroc dusk and midnight form backsprites and adjust back offset (#4430) --- src/data/pokemon/species_info/gen_7_families.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/pokemon/species_info/gen_7_families.h b/src/data/pokemon/species_info/gen_7_families.h index da640ca852..13bba7cd01 100644 --- a/src/data/pokemon/species_info/gen_7_families.h +++ b/src/data/pokemon/species_info/gen_7_families.h @@ -1761,7 +1761,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .frontPicYOffset = 0, .frontAnimFrames = sAnims_LycanrocMidnight, .frontAnimId = ANIM_SHRINK_GROW_VIBRATE_SLOW, - .backPic = gMonBackPic_LycanrocMidday, + .backPic = gMonBackPic_LycanrocMidnight, .backPicSize = MON_COORDS_SIZE(64, 56), .backPicYOffset = 7, .backAnimId = BACK_ANIM_SHRINK_GROW_VIBRATE, @@ -1814,9 +1814,9 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .frontPicYOffset = 6, .frontAnimFrames = sAnims_LycanrocDusk, .frontAnimId = ANIM_V_SHAKE, - .backPic = gMonBackPic_LycanrocMidday, + .backPic = gMonBackPic_LycanrocDusk, .backPicSize = MON_COORDS_SIZE(64, 56), - .backPicYOffset = 5, + .backPicYOffset = 6, .backAnimId = BACK_ANIM_V_SHAKE, .palette = gMonPalette_LycanrocDusk, .shinyPalette = gMonShinyPalette_LycanrocDusk, From edc98d664f5d4ecd604ab9d2347d4abec495a3f6 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Tue, 23 Apr 2024 19:51:03 +0300 Subject: [PATCH 11/71] pokedex expYield matches SpeciesInfo type (#4432) --- src/pokedex_plus_hgss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index b65f60dc65..debf4d7fe0 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -385,7 +385,7 @@ struct PokemonStats u8 eggGroup1; u8 eggGroup2; u8 eggCycles; - u8 expYield; + u16 expYield; u8 friendship; u16 ability0; u16 ability1; From aaaccbfe6ce893b0e85acf371546bb67e7be05a1 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:54:46 +0200 Subject: [PATCH 12/71] Adds support for shouldDynamax and shouldTerastal in trainer parties (#4431) --- tools/trainerproc/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index 51a2df8968..de8b7786c4 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -1822,12 +1822,18 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par fprintf(f, ",\n"); } + if (pokemon->dynamax_level_line || pokemon->gigantamax_factor_line) + { + fprintf(f, " .shouldDynamax = TRUE,\n"); + } + if (pokemon->tera_type_line) { fprintf(f, "#line %d\n", pokemon->tera_type_line); fprintf(f, " .teraType = "); fprint_constant(f, "TYPE", pokemon->tera_type); fprintf(f, ",\n"); + fprintf(f, " .shouldTerastal = TRUE,\n"); } if (pokemon->moves_n > 0) From 87dca8eb1cf68e6f05a044b666e51a1ccd35d25d Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Tue, 23 Apr 2024 20:49:22 +0100 Subject: [PATCH 13/71] Fix typo in Rotom Catalog (#4433) https://discord.com/channels/419213663107416084/774393519569502268/1232372639562072084 --- src/data/items.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/items.h b/src/data/items.h index 3a65f8758c..04e578c3ca 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -10771,7 +10771,7 @@ const struct Item gItemsInfo[] = .importance = 1, .description = COMPOUND_STRING( "A catalog full of\n" - "deviced liked by\n" + "devices liked by\n" "Rotom."), .pocket = POCKET_KEY_ITEMS, .type = ITEM_USE_PARTY_MENU, From 84a9d4ffcfb5ac78803830dc7d25805f433fc415 Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Wed, 24 Apr 2024 02:17:46 -0700 Subject: [PATCH 14/71] Terastallization (#4110) * wrote foundational terastal tests * implemented baseline test-only Tera functionality; modified GetBattlerType + STAB calculations, misc. changes to some moves * added tests and func. for Stellar type, more tests for Tera Blast * more tests for Stellar type, Conversion fixes, Color Change + Conversion2 future proof * implemented tera blast, expanded stellar type func., fixed tests * last set of Tera/Tera Blast tests for checklist, protean fix * implemented in-battle Terastallization, WIP stellar indicator and tera animation * fixed bad merge * expanded NUMBER_OF_MON_TYPES, cut down on TYPE_STELLAR hackiness, added Stellar type to summary * fixed type indicators * added tera logic to AI * implemented code review changes, added B_TERA_ORB_NO_COST * updated AI to calc damage with Tera when applicable; minor rework to AI gimmick handling * fixed Tera Blast split choice occuring when not Terastallized * fixed Tera Blast using Last Respects BP calcs * added tera type to TrainerMon, code review tweaks --- data/battle_scripts_1.s | 28 + graphics/battle_interface/bug_indicator.png | Bin 0 -> 4942 bytes graphics/battle_interface/dark_indicator.png | Bin 0 -> 4980 bytes .../battle_interface/dragon_indicator.png | Bin 0 -> 5015 bytes .../battle_interface/electric_indicator.png | Bin 0 -> 4876 bytes graphics/battle_interface/fairy_indicator.png | Bin 0 -> 5006 bytes .../battle_interface/fighting_indicator.png | Bin 0 -> 5009 bytes graphics/battle_interface/fire_indicator.png | Bin 0 -> 4928 bytes .../battle_interface/flying_indicator.png | Bin 0 -> 4929 bytes graphics/battle_interface/ghost_indicator.png | Bin 0 -> 4941 bytes graphics/battle_interface/grass_indicator.png | Bin 0 -> 4887 bytes .../battle_interface/ground_indicator.png | Bin 0 -> 4873 bytes graphics/battle_interface/ice_indicator.png | Bin 0 -> 4905 bytes .../battle_interface/normal_indicator.png | Bin 0 -> 4885 bytes .../battle_interface/poison_indicator.png | Bin 0 -> 4959 bytes .../battle_interface/psychic_indicator.png | Bin 0 -> 4937 bytes graphics/battle_interface/rock_indicator.png | Bin 0 -> 4933 bytes graphics/battle_interface/steel_indicator.png | Bin 0 -> 4904 bytes .../battle_interface/stellar_indicator.png | Bin 0 -> 5418 bytes graphics/battle_interface/tera_indicator.pal | 19 + graphics/battle_interface/tera_trigger.png | Bin 0 -> 7449 bytes graphics/battle_interface/water_indicator.png | Bin 0 -> 4896 bytes graphics/types/stellar.png | Bin 0 -> 5861 bytes graphics_file_rules.mk | 2 +- include/battle.h | 21 +- include/battle_controllers.h | 1 + include/battle_interface.h | 24 + include/battle_scripts.h | 2 + include/battle_terastal.h | 30 + include/battle_util.h | 2 +- include/config/battle.h | 2 + include/constants/battle.h | 3 +- include/constants/battle_move_effects.h | 1 + include/constants/battle_string_ids.h | 11 +- include/constants/pokemon.h | 3 +- include/data.h | 1 + include/test/battle.h | 2 + src/battle_ai_main.c | 38 +- src/battle_ai_util.c | 20 +- src/battle_controller_opponent.c | 12 +- src/battle_controller_player.c | 21 + src/battle_gfx_sfx_util.c | 8 + src/battle_interface.c | 19 + src/battle_main.c | 43 +- src/battle_message.c | 2 + src/battle_script_commands.c | 60 +- src/battle_terastal.c | 786 +++++++++++++++++ src/battle_util.c | 118 ++- src/data/battle_move_effects.h | 6 + src/data/moves_info.h | 6 +- src/pokemon_summary_screen.c | 5 + src/script_pokemon_util.c | 5 + test/{ => battle/gimmick}/dynamax.c | 0 test/battle/gimmick/terastal.c | 802 ++++++++++++++++++ test/battle/move_effect/tera_blast.c | 137 +++ test/test_runner_battle.c | 3 + 56 files changed, 2151 insertions(+), 92 deletions(-) create mode 100644 graphics/battle_interface/bug_indicator.png create mode 100644 graphics/battle_interface/dark_indicator.png create mode 100644 graphics/battle_interface/dragon_indicator.png create mode 100644 graphics/battle_interface/electric_indicator.png create mode 100644 graphics/battle_interface/fairy_indicator.png create mode 100644 graphics/battle_interface/fighting_indicator.png create mode 100644 graphics/battle_interface/fire_indicator.png create mode 100644 graphics/battle_interface/flying_indicator.png create mode 100644 graphics/battle_interface/ghost_indicator.png create mode 100644 graphics/battle_interface/grass_indicator.png create mode 100644 graphics/battle_interface/ground_indicator.png create mode 100644 graphics/battle_interface/ice_indicator.png create mode 100644 graphics/battle_interface/normal_indicator.png create mode 100644 graphics/battle_interface/poison_indicator.png create mode 100644 graphics/battle_interface/psychic_indicator.png create mode 100644 graphics/battle_interface/rock_indicator.png create mode 100644 graphics/battle_interface/steel_indicator.png create mode 100644 graphics/battle_interface/stellar_indicator.png create mode 100644 graphics/battle_interface/tera_indicator.pal create mode 100644 graphics/battle_interface/tera_trigger.png create mode 100644 graphics/battle_interface/water_indicator.png create mode 100644 graphics/types/stellar.png create mode 100644 include/battle_terastal.h create mode 100644 src/battle_terastal.c rename test/{ => battle/gimmick}/dynamax.c (100%) create mode 100644 test/battle/gimmick/terastal.c create mode 100644 test/battle/move_effect/tera_blast.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e66f4c2a8e..5e36241c9a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -20,6 +20,34 @@ .section script_data, "aw", %progbits +BattleScript_Terastallization:: + @ TODO: no string prints in S/V, but right now this helps with clarity + printstring STRINGID_PKMNTERASTALLIZEDINTO + @ TODO: replace this animation + playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE + waitanimation + end3 + +BattleScript_LowerAtkSpAtk:: + jumpifstat BS_EFFECT_BATTLER, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkDoAnim + jumpifstat BS_EFFECT_BATTLER, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkEnd +BattleScript_LowerAtkSpAtkDoAnim:: + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_EFFECT_BATTLER, BIT_ATK | BIT_SPATK, STAT_CHANGE_NEGATIVE + setstatchanger STAT_ATK, 1, TRUE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_LowerAtkSpAtkTrySpAtk + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerAtkSpAtkTrySpAtk + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_LowerAtkSpAtkTrySpAtk:: + setstatchanger STAT_SPATK, 1, TRUE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_LowerAtkSpAtkEnd + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerAtkSpAtkEnd + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_LowerAtkSpAtkEnd: + return + BattleScript_EffectTidyUp:: attackcanceler attackstring diff --git a/graphics/battle_interface/bug_indicator.png b/graphics/battle_interface/bug_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..7ab7dc6dfa37f207ea32e8655499176ff9db3b21 GIT binary patch literal 4942 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 aB@jvnC+s3S|0=@G2m*Isw?nQGDgOmy+lEd6 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/fire_indicator.png b/graphics/battle_interface/fire_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..4d3e59d543ac9e0f2b73933f1b74573a4e1b4411 GIT binary patch literal 4928 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 L?O!@`QTl%Y7XWa~ literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/grass_indicator.png b/graphics/battle_interface/grass_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..81fa2589d0dab5e7e803560121a84f5b761aab16 GIT binary patch literal 4887 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 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/ice_indicator.png b/graphics/battle_interface/ice_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..a40d96f01831d9941c476babde6cbf4f7c8d7bac GIT binary patch literal 4905 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 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/normal_indicator.png b/graphics/battle_interface/normal_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..029827e2962bc93f03b093045253db9155718dfc GIT binary patch literal 4885 zcmeHKXH*ku7Y-t#NKp}pl@LVHbx5xg5CfQ?QJMt>WHOn+2q|QO1nfawMa6T@ z$dP(i%l7Ak@!q2judX}fTVeINb^Ef{dtdw9Eq+-Ud2t!)VhI-ui$K`KyE-TT)AodJkbNMMVF7oA>aX z=*^)*n^ig+`oQZ66D)UK+Ss(j>-XiwMX0b!cV=bP$9p%nH=e!zD*N%JC5b7k!&{o1 zE5bzM6YYykt`=Vwm6hg?32@y!s^Zb+L)mkECWc=wf1FF-us@XXdD7R3$txhvfQROc z(7GwSPv)QCoBIdU=(Z&$oK1~C8~V^J;W5HwA@54gyy`V+Xo_U}+z^JV{ff{#N0!;g zF081V-%nz7q)6W^-v>o)9Yl*7QeP0T+iUUqt>Z}iH^XW4S=(HK)?}YdW<4pt?wjOH zjLwVLrw?Dgg);tF`|f84H(!i(j-p>6!e&|bzpVeTYX?n!%{HV&Y8L8lV-_*BL2d&L zm{eEce6H7`6IY^}$3@&Mr2fWESKJ?+yvMmnKC+xVC%2LI=BV%C5q$=bTR#wlz52wE z-VVO%+aOxO9oJe;-N|K!PBclVv~s<_ruW7qThp4^w%Cg!&mrIIbAERf^=5GcpUk&s z*H70b55E>(-16AmvE*|-zVV6P#;KILc3#S$uy?dlB5Qa0jN5zf<_1(pRb_MgCFf6f zy1aAjuI#7GOPaEmmMgRSJB&TpI>>MQoWOFs7eQg~S+{3b-wwv>Mq9<@t+dZNaa*$| zCHUpRwvmai9t2gI?q$nPKAlXAZ4^1r+rR1lF^}UPL-JEu;Zr4-b%$5&qUDT^+Ggv? zGeutYU3aCNFer0O~rku^wlQS_tw{X>T5ijIu4gSt$9J*<#C|3S6;%! zR!LN!9Hmpq3N_T`H;{OJSnQ%`W?GTP?w(h|u+o}Ri)|=HSG_-c{arN8sx%X z<1=Yn+2(h*n&lp2kF`$lYv6>LHlH{Um))O0Fj{3_%i?W+UD_po%P%|t31oHX-z3Ul{3E9sy0J1<$3Y+;c^$TlgF5moJM`= zhS0RIC3T_N*-tWH=hVzG*0yUeTW_0?zSVN8E|Z&AF!WjfkTkm+bF-`c)*pXUK96`U z`Zw(0t)X7Sr-(@X2GZxd=J)C!@PHLKb!Ve{!&Aov?P5)N{kWt}h{=qCjl(@#9ousb zgrsL`Qzy|#P0~Ld^CzkuqV6c#+U}jm8ndip$jPb)U+0^qe;HNs_zrpU#2U6b_R*bs z!4r)+F|o=;s2Sx6?eHf&u58TGoIelUJlb&MKwQc#bCU4roUF$AReHH+au7Lj00}lpo97-UH ztKu{W>aze;>Nrh|F&!B~L1Rz_FvY>Dw4auoESC6x_b^Zpi7C`ZFF^KB zmRd~ygRGxoGiZ$IbPoi$f5-jF`djWsV_+qb@V!+?oFP21w}5O|pD$A(n2c|{mCIl% zl__OGC>N$fOgbBZcvKz@qD#3f9>PVqOb*%&O02}Suo6KHPypNw12}RHiZVGY1d>ZR z42a3#!Vr(kL?AksA!pE8ECz~nhnTLxKv%*s-J>!<$p92f#$!_%Oew_WaS({frb;0x zhf9a(JhmK0>2juw3LB%5A$(tzMgfCzVhT7CrKpvWMumZJzL&pPK&HD zA(&31LI@RQ069a-rFE9;J36kCYvW-J>JX5u&2Le>rVL_AMQqjZbJb0b;`hS0kewoeOPtzWuWk1Ji6-e zFRlQfdlva6etYQZq3f3z_$A|>?&_iIml*gZggMGkN8nmd=QxSv}`ft2<@8;33c*2&fe5818>Ul~>_KYhh*2Uf^i@S0DeHEtz z9OlhJ;L}en3c(44{aeH&I3f0mok`Yph<&DcADk4S{~zq! BQG@^h literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/poison_indicator.png b/graphics/battle_interface/poison_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..84806865ccef15dc1acefaf8cfbfae06892db8d0 GIT binary patch literal 4959 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 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/psychic_indicator.png b/graphics/battle_interface/psychic_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..c2646d0a71b53c8785459ce1c5cb68c507e66180 GIT binary patch literal 4937 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 BWw8JN literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/rock_indicator.png b/graphics/battle_interface/rock_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..0329ec678cd1f66fd5d73625a319ccbd6dbcad8f GIT binary patch literal 4933 zcmeHKc~leU77r+85v@z9fG8nYktO?NEs-Szh#H9m6~!u($pi+nkqIPVp)J@`5v)=b zWvR+TU+F7VTbGJrt5vEj0#X)B1r;o!xS$WMmP-01pyGM`$8%o)bIzH}%>C~D-Ftuc zes?mt(j^g-o!p%W1j1x-WLPw~PPLp9CxG8OT|GcB;c!MwtRWgU5_Nj55>uf>L%I$n zq9#m9AecJu6viFxo$o9%yNTYk5eJ^tnfA9aE3S2%zI3;QD}E<);J?M_DTYG|JHBLO z;0io)x<>JJ%7Uawuh`X;sYxfOx^Lsu*~U-j7x~vKWx1ut zHnOflZajC7kjh+JrQMp3wl~El&f=)gEQ@x&>_9HMn%|srpChKcOt8()$O;_xBDb>s_V}l{9R0RK^wN^Et_4;`>{E2s1EYs4;O1evMS)vNkkVw8*bY{v>8a=494#7OU;@G4UDR z#n3!STvCo!Lz5X)Xv~@MXcuSnAEpB!jefQ>)4~DyiSS z_gZ#eN{~7~p4S_~e&?Jo%DwAY<^P?1Yv-SxG5$(#=(}rgwK*O2bT?e6OwVfVrd;@9c9zFe z?jI>bSv-AxT?pb^>C!l9?PmyHNjp`^5wY-}h|RK!EgnISJ^$SG^~#;ahOMD= z;-LIj&+=g1%*1x}@bIOhB|aaT-Hx?%jwDUk`E>5d?sif{@D;W$rK|nA;HuuO-LST?d2P4U-fhb24FpYq8@5B!z zBBl_KR@gTCC1Ihu> zSv)e2&S8?dD1!#U5X9qf>Eocp8r%SD5Yz$%z$qBOVe;s7Cc=fukb=u5Lns1~c}g0c ztUwVamxn4?T!=XiB1(^eu7p+Nqq0CL02IWc(-<@cLgugm0EEDDGLMG9WG9@a?zCxSWaWn$P79gAww)Sj|`(6HkrdkX3j&%CEcY|3;V7i_aZY1O5RS!N<&lPrT*eLx?6_93ui(x$OO> zAhirTAl-85QuCn^Jbhb9IZ_?B^%T`yy6oK1{FKI@QX5Y_Y~h%}VgEn~Y#eowD{%s0 z(hSRKLntnF1wwm+SR%5&VK>7zVBS8s;om^S6o-YxICLQKdz^Lmx@QxVw@1wD{C9q1 zHfK5Xn$1twdgWg+m*qc|etE^A=7d*%Vg-*-q4eObw|(O?U``N=mW1sOjnDoIKd@|u literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/steel_indicator.png b/graphics/battle_interface/steel_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..e84d9fa1b0c5cdd1fb95488d342a2c79b21ab748 GIT binary patch literal 4904 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 NczgH?_I|r4^>5NoF(&{3 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/tera_indicator.pal b/graphics/battle_interface/tera_indicator.pal new file mode 100644 index 0000000000..d4ab140472 --- /dev/null +++ b/graphics/battle_interface/tera_indicator.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +98 83 124 +207 63 109 +83 105 175 +80 144 216 +216 208 179 +254 115 121 +148 155 163 +99 188 91 +147 192 47 +255 156 85 +199 182 140 +113 206 198 +117 206 192 +245 210 55 +255 255 255 diff --git a/graphics/battle_interface/tera_trigger.png b/graphics/battle_interface/tera_trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..5498ea8806cfa40dc664e5f0c8b311e84c349ce2 GIT binary patch literal 7449 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

-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 VuKLOdzo1vkpslX2mZ#zn{2zN*k%<5R literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/water_indicator.png b/graphics/battle_interface/water_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..bdeb401f4abebf6b78b170c334e10aab6996efe5 GIT binary patch literal 4896 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 * 8 >= MAX_BATTLER #define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[gBattlerTarget])) #define BATTLER_TURN_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[battler])) -#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type))) - -#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY) +#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, FALSE) == type || GetBattlerType(battlerId, 1, FALSE) == type || (GetBattlerType(battlerId, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == type))) +#define IS_BATTLER_OF_BASE_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, TRUE) == type || GetBattlerType(battlerId, 1, TRUE) == type || (GetBattlerType(battlerId, 2, TRUE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, TRUE) == type))) +#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 1, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == TYPE_MYSTERY) #define SET_BATTLER_TYPE(battlerId, type) \ { \ diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 5f3bd8316e..523434d57a 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -101,6 +101,7 @@ enum { #define RET_MEGA_EVOLUTION (1 << 7) #define RET_ULTRA_BURST (1 << 6) #define RET_DYNAMAX (1 << 5) +#define RET_TERASTAL (1 << 4) struct UnusedControllerStruct { diff --git a/include/battle_interface.h b/include/battle_interface.h index b26205d810..f32017745f 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -57,12 +57,36 @@ enum #define TAG_DYNAMAX_TRIGGER_TILE 0xD77D #define TAG_DYNAMAX_INDICATOR_TILE 0xD77E +#define TAG_NORMAL_INDICATOR_TILE 0xD77F +#define TAG_FIGHTING_INDICATOR_TILE 0xD780 +#define TAG_FLYING_INDICATOR_TILE 0xD781 +#define TAG_POISON_INDICATOR_TILE 0xD782 +#define TAG_GROUND_INDICATOR_TILE 0xD783 +#define TAG_ROCK_INDICATOR_TILE 0xD784 +#define TAG_BUG_INDICATOR_TILE 0xD785 +#define TAG_GHOST_INDICATOR_TILE 0xD786 +#define TAG_STEEL_INDICATOR_TILE 0xD787 +// empty spot for TYPE_MYSTERY +#define TAG_FIRE_INDICATOR_TILE 0xD789 +#define TAG_WATER_INDICATOR_TILE 0xD78A +#define TAG_GRASS_INDICATOR_TILE 0xD78B +#define TAG_ELECTRIC_INDICATOR_TILE 0xD78C +#define TAG_PSYCHIC_INDICATOR_TILE 0xD78D +#define TAG_ICE_INDICATOR_TILE 0xD78E +#define TAG_DRAGON_INDICATOR_TILE 0xD78F +#define TAG_DARK_INDICATOR_TILE 0xD790 +#define TAG_FAIRY_INDICATOR_TILE 0xD791 +#define TAG_STELLAR_INDICATOR_TILE 0xD792 +#define TAG_TERA_TRIGGER_TILE 0xD793 + #define TAG_MEGA_TRIGGER_PAL 0xD777 #define TAG_MEGA_INDICATOR_PAL 0xD778 #define TAG_MISC_INDICATOR_PAL 0xD779 // Alpha, Omega, and Dynamax indicators use the same palette as each of them only uses 4 different colors. #define TAG_ZMOVE_TRIGGER_PAL 0xD77B #define TAG_BURST_TRIGGER_PAL 0xD77C #define TAG_DYNAMAX_TRIGGER_PAL 0xD77D +#define TAG_TERA_INDICATOR_PAL 0xD77E +#define TAG_TERA_TRIGGER_PAL 0xD77F enum { diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 1a7654d37b..e4e9ca4307 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -499,6 +499,8 @@ extern const u8 BattleScript_TheSwampDisappeared[]; extern const u8 BattleScript_ItemRestoreHP_Party[]; extern const u8 BattleScript_EffectPsychicNoise[]; extern const u8 BattleScript_AromaVeilProtectsRet[]; +extern const u8 BattleScript_LowerAtkSpAtk[]; +extern const u8 BattleScript_Terastallization[]; extern const u8 BattleScript_BoosterEnergyEnd2[]; // zmoves diff --git a/include/battle_terastal.h b/include/battle_terastal.h new file mode 100644 index 0000000000..078bf39079 --- /dev/null +++ b/include/battle_terastal.h @@ -0,0 +1,30 @@ +#ifndef GUARD_BATTLE_TERASTAL_H +#define GUARD_BATTLE_TERASTAL_H + +void PrepareBattlerForTera(u32 battler); +bool32 CanTerastallize(u32 battler); +u32 GetBattlerTeraType(u32 battler); +bool32 IsTerastallized(u32 battler); +void ExpendTypeStellarBoost(u32 battler, u32 type); +bool32 IsTypeStellarBoosted(u32 battler, u32 type); +uq4_12_t GetTeraMultiplier(u32 battler, u32 type); + +u16 GetTeraTypeRGB(u32 type); + +void ChangeTeraTriggerSprite(u8 spriteId, u8 animId); +void CreateTeraTriggerSprite(u8 battler, u8 palId); +bool32 IsTeraTriggerSpriteActive(void); +void HideTeraTriggerSprite(void); +void DestroyTeraTriggerSprite(void); + +void TeraIndicator_LoadSpriteGfx(void); +bool32 TeraIndicator_ShouldBeInvisible(u32 battler); +u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId); +void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible); +void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority); +void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level); +void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId); +void TeraIndicator_DestroySprite(u32 healthboxSpriteId); +void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId); + +#endif diff --git a/include/battle_util.h b/include/battle_util.h index 1fb6d6a8f7..27ecb26e82 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -250,7 +250,7 @@ bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2); bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2); u32 CalcSecondaryEffectChance(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect); bool32 MoveEffectIsGuaranteed(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect); -u8 GetBattlerType(u32 battler, u8 typeIndex); +u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera); bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon); bool8 IsMonBannedFromSkyBattles(u16 species); void RemoveBattlerType(u32 battler, u8 type); diff --git a/include/config/battle.h b/include/config/battle.h index 40d7bd1147..c47f4fedcf 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -179,6 +179,8 @@ #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. #define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles. #define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers. +#define B_FLAG_TERA_ORB_CHARGED 0 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. +#define B_FLAG_TERA_ORB_NO_COST 0 // If this flag is set, the Tera Orb does not use up its charge upon Terastallization. In S/V, this occurs after an event with Terapagos. // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. diff --git a/include/constants/battle.h b/include/constants/battle.h index 0acb857ad0..30f304144f 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -402,8 +402,9 @@ #define MOVE_EFFECT_FLORAL_HEALING 77 #define MOVE_EFFECT_SECRET_POWER 78 #define MOVE_EFFECT_PSYCHIC_NOISE 79 +#define MOVE_EFFECT_TERA_BLAST 80 -#define NUM_MOVE_EFFECTS 80 +#define NUM_MOVE_EFFECTS 81 #define MOVE_EFFECT_AFFECTS_USER 0x2000 #define MOVE_EFFECT_CERTAIN 0x4000 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 0cf0862f15..b01685fbb7 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -350,6 +350,7 @@ enum { EFFECT_DRAGON_CHEER, EFFECT_LAST_RESPECTS, EFFECT_TIDY_UP, + EFFECT_TERA_BLAST, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 86b48f9d4a..dfb5c5e2d4 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -707,12 +707,13 @@ #define STRINGID_BIZARREARENACREATED 705 #define STRINGID_BIZARREAREACREATED 706 #define STRINGID_TIDYINGUPCOMPLETE 707 -#define STRINGID_BOOSTERENERGYACTIVATES 708 -#define STRINGID_FOGCREPTUP 709 -#define STRINGID_FOGISDEEP 710 -#define STRINGID_FOGLIFTED 711 +#define STRINGID_PKMNTERASTALLIZEDINTO 708 +#define STRINGID_BOOSTERENERGYACTIVATES 709 +#define STRINGID_FOGCREPTUP 710 +#define STRINGID_FOGISDEEP 711 +#define STRINGID_FOGLIFTED 712 -#define BATTLESTRINGS_COUNT 712 +#define BATTLESTRINGS_COUNT 713 // 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/pokemon.h b/include/constants/pokemon.h index 1adefd4938..343bf1a049 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -22,7 +22,8 @@ #define TYPE_DRAGON 16 #define TYPE_DARK 17 #define TYPE_FAIRY 18 -#define NUMBER_OF_MON_TYPES 19 +#define TYPE_STELLAR 19 +#define NUMBER_OF_MON_TYPES 20 // Pokémon egg groups #define EGG_GROUP_NONE 0 diff --git a/include/data.h b/include/data.h index b4490e8cc7..0549f29c4c 100644 --- a/include/data.h +++ b/include/data.h @@ -71,6 +71,7 @@ struct TrainerMon bool8 gender:2; bool8 isShiny:1; u8 dynamaxLevel:4; + u8 teraType:5; bool8 gigantamaxFactor:1; bool8 shouldDynamax:1; bool8 shouldTerastal:1; diff --git a/include/test/battle.h b/include/test/battle.h index 62b0680486..c4bd89f91b 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -935,6 +935,8 @@ struct MoveContext // TODO: u8 zMove:1; u16 dynamax:1; u16 explicitDynamax:1; + u16 tera:1; + u16 explicitTera:1; u16 allowed:1; u16 explicitAllowed:1; u16 notExpected:1; // Has effect only with EXPECT_MOVE diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 3d1c7e934b..da8b3764a3 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -9,6 +9,7 @@ #include "battle_factory.h" #include "battle_setup.h" #include "battle_z_move.h" +#include "battle_terastal.h" #include "data.h" #include "debug.h" #include "event_data.h" @@ -398,6 +399,23 @@ static void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) aiData->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect); } +static void SetBattlerAiGimmickData(u32 battler, struct AiLogicData *aiData) +{ + bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT; + u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A; + const struct TrainerMon *party = GetTrainerPartyFromId(trainerId); + if (party != NULL) + { + aiData->shouldDynamax[battler] = CanDynamax(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax); + aiData->shouldTerastal[battler] = CanTerastallize(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldTerastal); + } + else + { + aiData->shouldDynamax[battler] = FALSE; + aiData->shouldTerastal[battler] = FALSE; + } +} + static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 move) { u32 accuracy; @@ -467,6 +485,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) continue; SetBattlerAiData(battlerAtk, aiData); + SetBattlerAiGimmickData(battlerAtk, aiData); SetBattlerAiMovesData(aiData, battlerAtk, battlersCount); } } @@ -2367,20 +2386,20 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_SOAK: if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) - || (GetBattlerType(battlerDef, 0) == TYPE_WATER - && GetBattlerType(battlerDef, 1) == TYPE_WATER - && GetBattlerType(battlerDef, 2) == TYPE_MYSTERY)) + || (GetBattlerType(battlerDef, 0, FALSE) == TYPE_WATER + && GetBattlerType(battlerDef, 1, FALSE) == TYPE_WATER + && GetBattlerType(battlerDef, 2, FALSE) == TYPE_MYSTERY)) ADJUST_SCORE(-10); // target is already water-only break; case EFFECT_THIRD_TYPE: switch (move) { case MOVE_TRICK_OR_TREAT: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef)) ADJUST_SCORE(-10); break; case MOVE_FORESTS_CURSE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef)) ADJUST_SCORE(-10); break; } @@ -2514,9 +2533,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_SYNCHRONOISE: //Check holding ring target or is of same type if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET - || IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 0)) - || IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 1)) - || IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 2))) + || DoBattlersShareType(battlerAtk, battlerDef)) break; else ADJUST_SCORE(-10); @@ -2940,9 +2957,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_SOAK: if (atkPartnerAbility == ABILITY_WONDER_GUARD - && (GetBattlerType(battlerAtkPartner, 0) != TYPE_WATER - || GetBattlerType(battlerAtkPartner, 1) != TYPE_WATER - || GetBattlerType(battlerAtkPartner, 2) != TYPE_WATER)) + && IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER) + && !IsTerastallized(battlerAtkPartner)) { RETURN_SCORE_PLUS(WEAK_EFFECT); } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 40f3da2b5a..3458830a12 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -448,14 +448,16 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes s32 dmg, moveType; uq4_12_t effectivenessMultiplier; bool32 isDamageMoveUnusable = FALSE; + bool32 toggledDynamax = FALSE; + bool32 toggledTera = FALSE; struct AiLogicData *aiData = AI_DATA; SetBattlerData(battlerAtk); SetBattlerData(battlerDef); + // Temporarily enable Z-Moves for damage calcs if (considerZPower && IsViableZMove(battlerAtk, move)) { - //temporarily enable z moves for damage calcs gBattleStruct->zmove.baseMoves[battlerAtk] = move; gBattleStruct->zmove.active = TRUE; } @@ -465,6 +467,18 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes if (gMovesInfo[move].effect == EFFECT_NATURE_POWER) move = GetNaturePowerMove(); + // Temporarily enable other gimmicks for damage calcs if planned + if (AI_DATA->shouldDynamax[battlerAtk]) + { + toggledDynamax = TRUE; + gBattleStruct->dynamax.dynamaxed[battlerAtk] = TRUE; + } + if (AI_DATA->shouldTerastal[battlerAtk]) + { + toggledTera = TRUE; + gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] |= gBitTable[gBattlerPartyIndexes[battlerAtk]]; + } + gBattleStruct->dynamicMoveType = 0; @@ -582,6 +596,10 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->zmove.active = FALSE; gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE; + if (toggledDynamax) + gBattleStruct->dynamax.dynamaxed[battlerAtk] = FALSE; + if (toggledTera) + gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] &= ~(gBitTable[gBattlerPartyIndexes[battlerAtk]]); return dmg; } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 597d0b7c5f..1622b94e9d 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -544,13 +544,6 @@ static void OpponentHandleChooseMove(u32 battler) default: { u16 chosenMove = moveInfo->moves[chosenMoveId]; - bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT; - u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A; - const struct TrainerMon *party = GetTrainerPartyFromId(trainerId); - bool32 shouldDynamax = FALSE; - if (party != NULL) - shouldDynamax = party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax; - if (GetBattlerMoveTargetType(battler, chosenMove) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) gBattlerTarget = battler; if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH) @@ -568,8 +561,11 @@ static void OpponentHandleChooseMove(u32 battler) else if (CanUltraBurst(battler)) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8)); // If opponent can Dynamax and is allowed in the partydata, do it. - else if (CanDynamax(battler) && shouldDynamax) + else if (CanDynamax(battler) && AI_DATA->shouldDynamax[battler]) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_DYNAMAX) | (gBattlerTarget << 8)); + // If opponent can Terastal and is allowed in the partydata, do it. + else if (CanTerastallize(battler) && AI_DATA->shouldTerastal[battler]) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_TERASTAL) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index eeca1899b1..10a91a1485 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -454,6 +454,8 @@ static void HandleInputChooseTarget(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); @@ -616,6 +618,8 @@ static void HandleInputShowEntireFieldTargets(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -648,6 +652,8 @@ static void HandleInputShowTargets(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -772,6 +778,8 @@ static void HandleInputChooseMove(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -810,6 +818,7 @@ static void HandleInputChooseMove(u32 battler) gBattleStruct->mega.playerSelect = FALSE; gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->dynamax.playerSelect = FALSE; + gBattleStruct->tera.playerSelect = FALSE; gBattleStruct->zmove.viable = FALSE; BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF); HideTriggerSprites(); @@ -918,6 +927,12 @@ static void HandleInputChooseMove(u32 battler) ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, gBattleStruct->dynamax.playerSelect); PlaySE(SE_SELECT); } + else if (CanTerastallize(battler)) + { + gBattleStruct->tera.playerSelect ^= 1; + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, gBattleStruct->tera.playerSelect); + PlaySE(SE_SELECT); + } } } @@ -926,6 +941,7 @@ static void ReloadMoveNames(u32 battler) gBattleStruct->mega.playerSelect = FALSE; gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->dynamax.playerSelect = FALSE; + gBattleStruct->tera.playerSelect = FALSE; gBattleStruct->zmove.viewing = FALSE; MoveSelectionDestroyCursorAt(battler); MoveSelectionDisplayMoveNames(battler); @@ -2059,6 +2075,7 @@ static void PlayerHandleChooseMove(u32 battler) gBattleStruct->mega.playerSelect = FALSE; gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->dynamax.playerSelect = FALSE; + gBattleStruct->tera.playerSelect = FALSE; if (!IsMegaTriggerSpriteActive()) gBattleStruct->mega.triggerSpriteId = 0xFF; if (CanMegaEvolve(battler)) @@ -2073,6 +2090,10 @@ static void PlayerHandleChooseMove(u32 battler) CreateDynamaxTriggerSprite(battler, 0); if (!IsZMoveTriggerSpriteActive()) gBattleStruct->zmove.triggerSpriteId = 0xFF; + if (!IsTeraTriggerSpriteActive()) + gBattleStruct->tera.triggerSpriteId = 0xFF; + if (CanTerastallize(battler)) + CreateTeraTriggerSprite(battler, 0); GetUsableZMoves(battler, moveInfo->moves); gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]); diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 09b85e7768..0a4eaa07dd 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -645,6 +645,13 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) BlendPalette(paletteOffset, 16, 4, RGB(31, 0, 12)); CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); } + + // Terastallization's tint + if (IsTerastallized(battler)) + { + BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler))); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); + } } void BattleGfxSfxDummy2(u16 species) @@ -710,6 +717,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state) LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]); LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]); MegaIndicator_LoadSpritesGfx(); + TeraIndicator_LoadSpriteGfx(); } else if (!IsDoubleBattle()) { diff --git a/src/battle_interface.c b/src/battle_interface.c index 34cdec6e5c..6637fa4c4e 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -854,6 +854,9 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId) // Create mega indicator sprite. MegaIndicator_CreateSprite(battlerId, healthboxLeftSpriteId); + // Create tera indicator sprites. + TeraIndicator_CreateSprite(battlerId, healthboxLeftSpriteId); + gBattleStruct->ballSpriteIds[0] = MAX_SPRITES; gBattleStruct->ballSpriteIds[1] = MAX_SPRITES; @@ -937,6 +940,7 @@ void SetHealthboxSpriteInvisible(u8 healthboxSpriteId) gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = TRUE; gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = TRUE; MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE); + TeraIndicator_SetVisibilities(healthboxSpriteId, TRUE); } void SetHealthboxSpriteVisible(u8 healthboxSpriteId) @@ -945,6 +949,7 @@ void SetHealthboxSpriteVisible(u8 healthboxSpriteId) gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = FALSE; gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = FALSE; MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE); + TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE); } static void UpdateSpritePos(u8 spriteId, s16 x, s16 y) @@ -971,6 +976,7 @@ static void TryToggleHealboxVisibility(u32 priority, u32 healthboxLeftSpriteId, gSprites[healthboxRightSpriteId].invisible = invisible; gSprites[healthbarSpriteId].invisible = invisible; MegaIndicator_SetVisibilities(healthboxLeftSpriteId, invisible); + TeraIndicator_SetVisibilities(healthboxLeftSpriteId, invisible); } void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) @@ -988,6 +994,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) gSprites[healthbarSpriteId].oam.priority = priority; MegaIndicator_UpdateOamPriority(healthboxLeftSpriteId, priority); + TeraIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority); if (B_HIDE_HEALTHBOX_IN_ANIMS == TRUE && hideHPBoxes && IsBattlerAlive(i)) TryToggleHealboxVisibility(priority, healthboxLeftSpriteId, healthboxRightSpriteId, healthbarSpriteId); @@ -1050,6 +1057,13 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) MegaIndicator_UpdateLevel(healthboxSpriteId, lvl); MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE); } + else if (IsTerastallized(battler)) + { + objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); + xPos = 5 * (3 - (objVram - (text + 2))) - 1; + TeraIndicator_UpdateLevel(healthboxSpriteId, lvl); + TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE); + } else { text[0] = CHAR_EXTRA_SYMBOL; @@ -1473,6 +1487,7 @@ void HideTriggerSprites(void) HideBurstTriggerSprite(); HideZMoveTriggerSprite(); HideDynamaxTriggerSprite(); + HideTeraTriggerSprite(); } void DestroyMegaTriggerSprite(void) @@ -2532,6 +2547,10 @@ void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elem u32 battlerId = gSprites[healthboxSpriteId].hMain_Battler; s32 maxHp = GetMonData(mon, MON_DATA_MAX_HP); s32 currHp = GetMonData(mon, MON_DATA_HP); + + // This fixes a bug that should likely never happen involving switching between two Teras. + if (elementId == HEALTHBOX_ALL) + TeraIndicator_UpdateType(battlerId, healthboxSpriteId); if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) { diff --git a/src/battle_main.c b/src/battle_main.c index 6290d5305c..eba4916ef4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -590,13 +590,15 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = //.teraShard = ITEM_FAIRY_TERA_SHARD, //.arceusForm = SPECIES_ARCEUS_FAIRY, }, - /* [TYPE_STELLAR] = { - .name = _("Stellar"), - .teraShard = ITEM_STELLAR_TERA_SHARD, + .name = _("Stellr"), + .generic = _("a STELLAR move"), + .palette = 15, + .zMove = MOVE_BREAKNECK_BLITZ, + .maxMove = MOVE_MAX_STRIKE, + // .teraShard = ITEM_STELLAR_TERA_SHARD, }, - */ }; // extra args are money and ball @@ -2325,6 +2327,11 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer u32 data = partyData[i].gigantamaxFactor; SetMonData(&party[i], MON_DATA_GIGANTAMAX_FACTOR, &data); } + if (partyData[i].teraType > 0) + { + u32 data = partyData[i].teraType; + SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); + } CalculateMonStats(&party[i]); if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && ball == -1) @@ -4608,6 +4615,7 @@ static void HandleTurnActionSelectionState(void) gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->dynamax.toDynamax &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); + gBattleStruct->tera.toTera &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE; gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE; BtlController_EmitEndBounceEffect(battler, BUFFER_A); @@ -4697,7 +4705,7 @@ static void HandleTurnActionSelectionState(void) } // Get the chosen move position (and thus the chosen move) and target from the returned buffer. - gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX); + gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL); gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]]; gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3]; @@ -4708,6 +4716,8 @@ static void HandleTurnActionSelectionState(void) gBattleStruct->burst.toBurst |= gBitTable[battler]; else if (gBattleResources->bufferB[battler][2] & RET_DYNAMAX) gBattleStruct->dynamax.toDynamax |= gBitTable[battler]; + else if (gBattleResources->bufferB[battler][2] & RET_TERASTAL) + gBattleStruct->tera.toTera |= gBitTable[battler]; // Max Move check if (ShouldUseMaxMove(battler, gChosenMoveByBattler[battler])) @@ -5325,7 +5335,8 @@ static void PopulateArrayWithBattlers(u8 *battlers) static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) - && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst || gBattleStruct->dynamax.toDynamax)) + && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst + || gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera)) { u32 i, battler; u8 order[MAX_BATTLERS_COUNT]; @@ -5334,6 +5345,17 @@ static bool32 TryDoGimmicksBeforeMoves(void) SortBattlersBySpeed(order, FALSE); for (i = 0; i < gBattlersCount; i++) { + // Tera Check + if (gBattleStruct->tera.toTera & gBitTable[order[i]]) + { + gBattlerAttacker = order[i]; + gBattleScripting.battler = gBattlerAttacker; + gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]); + PrepareBattlerForTera(gBattlerAttacker); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker)); + BattleScriptExecute(BattleScript_Terastallization); + return TRUE; + } // Dynamax Check if (gBattleStruct->dynamax.toDynamax & gBitTable[order[i]]) { @@ -5996,7 +6018,9 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) } else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE) { - if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY) + if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR) + gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk); + else if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type1 | F_DYNAMIC_TYPE_SET; else if (gBattleMons[battlerAtk].type2 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type2 | F_DYNAMIC_TYPE_SET; @@ -6038,6 +6062,10 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET; } } + else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) + { + gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET; + } attackerAbility = GetBattlerAbility(battlerAtk); @@ -6046,6 +6074,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_WEATHER_BALL && gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT + && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) diff --git a/src/battle_message.c b/src/battle_message.c index 2efb99ff5f..5f7e6ca052 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -846,11 +846,13 @@ static const u8 sText_ElectroShotCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorb static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up..."); static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!"); static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!"); +static const u8 sText_PkmnTerastallizedInto[] = _("{B_ATK_NAME_WITH_PREFIX} terastallized\ninto the {B_BUFF1} type!"); static const u8 sText_SupersweetAromaWafts[] = _("A supersweet aroma is wafting from\nthe syrup covering {B_ATK_NAME_WITH_PREFIX}!"); static const u8 sText_TidyingUpComplete[] = _("Tidying up complete!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_PKMNTERASTALLIZEDINTO - BATTLESTRINGS_TABLE_START] = sText_PkmnTerastallizedInto, [STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete, [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, [STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index feb0149c68..ddcbeee78c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1215,7 +1215,8 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType) && !gDisableStructs[gBattlerAttacker].usedProteanLibero && (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType || (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY)) - && move != MOVE_STRUGGLE) + && move != MOVE_STRUGGLE + && !IsTerastallized(battler)) { SET_BATTLER_TYPE(battler, moveType); return TRUE; @@ -2085,12 +2086,12 @@ END: // of a move that is Super Effective against a Flying-type Pokémon. if (gBattleWeather & B_WEATHER_STRONG_WINDS) { - if ((GetBattlerType(gBattlerTarget, 0) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0)) >= UQ_4_12(2.0)) - || (GetBattlerType(gBattlerTarget, 1) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1)) >= UQ_4_12(2.0)) - || (GetBattlerType(gBattlerTarget, 2) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2)) >= UQ_4_12(2.0))) + if ((GetBattlerType(gBattlerTarget, 0, FALSE) == TYPE_FLYING + && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0, FALSE)) >= UQ_4_12(2.0)) + || (GetBattlerType(gBattlerTarget, 1, FALSE) == TYPE_FLYING + && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1, FALSE)) >= UQ_4_12(2.0)) + || (GetBattlerType(gBattlerTarget, 2, FALSE) == TYPE_FLYING + && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2, FALSE)) >= UQ_4_12(2.0))) { gBattlerAbility = gBattlerTarget; BattleScriptPushCursor(); @@ -3806,6 +3807,15 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise; } break; + case MOVE_EFFECT_TERA_BLAST: + if (IsTerastallized(gEffectBattler) + && GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR + && !NoAliveMonsForEitherParty()) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_LowerAtkSpAtk; + } + break; } } } @@ -9648,8 +9658,9 @@ static void Cmd_various(void) case VARIOUS_TRY_SOAK: { VARIOUS_ARGS(const u8 *failInstr); - if (GetBattlerType(gBattlerTarget, 0) == gMovesInfo[gCurrentMove].type - && GetBattlerType(gBattlerTarget, 1) == gMovesInfo[gCurrentMove].type) + if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type + && GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type) + || IsTerastallized(gBattlerTarget)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -9982,7 +9993,7 @@ static void Cmd_various(void) case VARIOUS_TRY_THIRD_TYPE: { VARIOUS_ARGS(const u8 *failInstr); - if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument)) + if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || IsTerastallized(battler)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -12049,6 +12060,12 @@ static void Cmd_tryconversiontypechange(void) u8 moveChecked = 0; u8 moveType = 0; + if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + return; + } + if (B_UPDATED_CONVERSION >= GEN_6) { // Changes user's type to its first move's type @@ -12825,6 +12842,14 @@ static void Cmd_settypetorandomresistance(void) { gBattlescriptCurrInstr = cmd->failInstr; } + else if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else { u32 i, resistTypes = 0; @@ -14812,7 +14837,7 @@ static void Cmd_settypetoterrain(void) break; } - if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType)) + if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && !IsTerastallized(gBattlerAttacker)) { SET_BATTLER_TYPE(gBattlerAttacker, terrainType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType); @@ -16378,14 +16403,18 @@ void BS_TryReflectType(void) { NATIVE_ARGS(const u8 *failInstr); u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species); - u8 targetType1 = GetBattlerType(gBattlerTarget, 0); - u8 targetType2 = GetBattlerType(gBattlerTarget, 1); - u8 targetType3 = GetBattlerType(gBattlerTarget, 2); + u8 targetType1 = GetBattlerType(gBattlerTarget, 0, FALSE); + u8 targetType2 = GetBattlerType(gBattlerTarget, 1, FALSE); + u8 targetType3 = GetBattlerType(gBattlerTarget, 2, FALSE); if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY) { gBattlescriptCurrInstr = cmd->failInstr; } + else if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else if (IS_BATTLER_TYPELESS(gBattlerTarget)) { gBattlescriptCurrInstr = cmd->failInstr; @@ -16746,7 +16775,8 @@ void BS_AllySwitchFailChance(void) void BS_SetPhotonGeyserCategory(void) { NATIVE_ARGS(); - gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); + if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))) + gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_terastal.c b/src/battle_terastal.c new file mode 100644 index 0000000000..8cc03aca57 --- /dev/null +++ b/src/battle_terastal.c @@ -0,0 +1,786 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_terastal.h" +#include "event_data.h" +#include "item.h" +#include "palette.h" +#include "pokemon.h" +#include "sprite.h" +#include "util.h" +#include "constants/abilities.h" +#include "constants/hold_effects.h" +#include "constants/rgb.h" + +// Sets flags and variables upon a battler's Terastallization. +void PrepareBattlerForTera(u32 battler) +{ + u32 side = GetBattlerSide(battler); + struct Pokemon *party = GetBattlerParty(battler); + u32 index = gBattlerPartyIndexes[battler]; + + // Update TeraData fields. + gBattleStruct->tera.isTerastallized[side] |= gBitTable[index]; + gBattleStruct->tera.alreadyTerastallized[battler] = TRUE; + + // Remove Tera Orb charge. + if (B_FLAG_TERA_ORB_CHARGED != 0 + && B_FLAG_TERA_ORB_NO_COST != 0 + && !FlagGet(B_FLAG_TERA_ORB_NO_COST) + && side == B_SIDE_PLAYER + && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !IsPartnerMonFromSameTrainer(battler))) + { + FlagClear(B_FLAG_TERA_ORB_CHARGED); + } + + // Show indicator and do palette blend. + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &party[index], HEALTHBOX_ALL); + BlendPalette(OBJ_PLTT_ID(battler), 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler))); + CpuCopy32(gPlttBufferFaded + OBJ_PLTT_ID(battler), gPlttBufferUnfaded + OBJ_PLTT_ID(battler), PLTT_SIZEOF(16)); +} + +// Returns whether a battler can Terastallize. +bool32 CanTerastallize(u32 battler) +{ + u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); + + // Check if Player has Tera Orb and has charge. + if (B_FLAG_TERA_ORB_CHARGED != 0 + && (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) + && !(CheckBagHasItem(ITEM_TERA_ORB, 1) + && FlagGet(B_FLAG_TERA_ORB_CHARGED))) + { + return FALSE; + } + + // Check if Trainer has already Terastallized. + if (gBattleStruct->tera.alreadyTerastallized[battler]) + { + return FALSE; + } + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && IsPartnerMonFromSameTrainer(battler) + && (gBattleStruct->tera.alreadyTerastallized[BATTLE_PARTNER(battler)] + || (gBattleStruct->tera.toTera & gBitTable[BATTLE_PARTNER(battler)]))) + { + return FALSE; + } + + // Check if battler is holding a Z-Crystal or Mega Stone. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) + { + return FALSE; + } + + // Every check passed! + return TRUE; +} + +// Returns a battler's Tera type. +u32 GetBattlerTeraType(u32 battler) +{ + struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]]; + return GetMonData(mon, MON_DATA_TERA_TYPE); +} + +// Returns whether a battler is terastallized. +bool32 IsTerastallized(u32 battler) +{ + return gBattleStruct->tera.isTerastallized[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]; +} + + +// Uses up a type's Stellar boost. +void ExpendTypeStellarBoost(u32 battler, u32 type) +{ + if (type < 32) // avoid OOB access + gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type]; +} + +// Checks whether a type's Stellar boost has been expended. +bool32 IsTypeStellarBoosted(u32 battler, u32 type) +{ + if (type < 32) // avoid OOB access + return !(gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] & gBitTable[type]); + else + return FALSE; +} + +// Returns the STAB power multiplier to use when Terastallized. +// Power multipliers from Smogon Research thread. +uq4_12_t GetTeraMultiplier(u32 battler, u32 type) +{ + u32 teraType = GetBattlerTeraType(battler); + bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY); + + // Safety check. + if (!IsTerastallized(battler)) + return UQ_4_12(1.0); + + // Stellar-type checks. + if (teraType == TYPE_STELLAR) + { + bool32 shouldBoost = IsTypeStellarBoosted(battler, type); + if (IS_BATTLER_OF_BASE_TYPE(battler, type)) + { + if (shouldBoost) + return UQ_4_12(2.0); + else + return UQ_4_12(1.5); + } + else if (shouldBoost) + return UQ_4_12(1.2); + else + return UQ_4_12(1.0); + } + // Base and Tera type. + if (type == teraType && IS_BATTLER_OF_BASE_TYPE(battler, type)) + { + if (hasAdaptability) + return UQ_4_12(2.25); + else + return UQ_4_12(2.0); + } + // Base or Tera type only. + else if ((type == teraType && !IS_BATTLER_OF_BASE_TYPE(battler, type)) + || (type != teraType && IS_BATTLER_OF_BASE_TYPE(battler, type))) + { + if (hasAdaptability) + return UQ_4_12(2.0); + else + return UQ_4_12(1.5); + } + // Neither base or Tera type. + else + { + return UQ_4_12(1.0); + } +} + +// Most values pulled from the Tera type icon palette. +const u16 sTeraTypeRGBValues[NUMBER_OF_MON_TYPES] = { + [TYPE_NORMAL] = RGB_WHITE, // custom + [TYPE_FIGHTING] = RGB(26, 8, 14), + [TYPE_FLYING] = RGB(31, 26, 7), + [TYPE_POISON] = RGB(26, 10, 25), // custom + [TYPE_GROUND] = RGB(25, 23, 18), + [TYPE_ROCK] = RGB(18, 16, 8), // custom + [TYPE_BUG] = RGB(18, 24, 6), + [TYPE_GHOST] = RGB(12, 10, 16), + [TYPE_STEEL] = RGB(19, 19, 20), + [TYPE_MYSTERY] = RGB_WHITE, + [TYPE_FIRE] = RGB(31, 20, 11), + [TYPE_WATER] = RGB(10, 18, 27), + [TYPE_GRASS] = RGB(12, 24, 11), + [TYPE_ELECTRIC] = RGB(30, 26, 7), + [TYPE_PSYCHIC] = RGB(31, 14, 15), + [TYPE_ICE] = RGB(14, 26, 25), + [TYPE_DRAGON] = RGB(10, 18, 27), + [TYPE_DARK] = RGB(6, 5, 8), + [TYPE_FAIRY] = RGB(31, 15, 21), + [TYPE_STELLAR] = RGB(10, 18, 27), +}; + +u16 GetTeraTypeRGB(u32 type) +{ + return sTeraTypeRGBValues[type]; +} + +// TERASTAL TRIGGER: +static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp"); +static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal"); + +static const struct SpriteSheet sSpriteSheet_TeraTrigger = +{ + sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_TERA_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_TeraTrigger = +{ + sTeraTriggerPal, TAG_TERA_TRIGGER_PAL +}; + +static const struct OamData sOamData_TeraTrigger = +{ + .y = 0, + .affineMode = 0, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = ST_OAM_SQUARE, + .x = 0, + .matrixNum = 0, + .size = 2, + .tileNum = 0, + .priority = 1, + .paletteNum = 0, + .affineParam = 0, +}; + +static const union AnimCmd sSpriteAnim_TeraTriggerOff[] = +{ + ANIMCMD_FRAME(0, 0), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_TeraTriggerOn[] = +{ + ANIMCMD_FRAME(16, 0), + ANIMCMD_END +}; + +static const union AnimCmd *const sSpriteAnimTable_TeraTrigger[] = +{ + sSpriteAnim_TeraTriggerOff, + sSpriteAnim_TeraTriggerOn, +}; + +static void SpriteCb_TeraTrigger(struct Sprite *sprite); +static const struct SpriteTemplate sSpriteTemplate_TeraTrigger = +{ + .tileTag = TAG_TERA_TRIGGER_TILE, + .paletteTag = TAG_TERA_TRIGGER_PAL, + .oam = &sOamData_TeraTrigger, + .anims = sSpriteAnimTable_TeraTrigger, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraTrigger +}; + +// Tera Evolution Trigger icon functions. +void ChangeTeraTriggerSprite(u8 spriteId, u8 animId) +{ + StartSpriteAnim(&gSprites[spriteId], animId); +} + +#define SINGLES_TERA_TRIGGER_POS_X_OPTIMAL (30) +#define SINGLES_TERA_TRIGGER_POS_X_PRIORITY (31) +#define SINGLES_TERA_TRIGGER_POS_X_SLIDE (15) +#define SINGLES_TERA_TRIGGER_POS_Y_DIFF (-11) + +#define DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL (30) +#define DOUBLES_TERA_TRIGGER_POS_X_PRIORITY (31) +#define DOUBLES_TERA_TRIGGER_POS_X_SLIDE (15) +#define DOUBLES_TERA_TRIGGER_POS_Y_DIFF (-4) + +#define tBattler data[0] +#define tHide data[1] + +void CreateTeraTriggerSprite(u8 battler, u8 palId) +{ + LoadSpritePalette(&sSpritePalette_TeraTrigger); + if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) + { + LoadSpriteSheet(&sSpriteSheet_TeraTrigger); + } + if (gBattleStruct->tera.triggerSpriteId == 0xFF) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, + gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_TERA_TRIGGER_POS_X_SLIDE, + gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_TERA_TRIGGER_POS_Y_DIFF, 0); + else + gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, + gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_TERA_TRIGGER_POS_X_SLIDE, + gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_TERA_TRIGGER_POS_Y_DIFF, 0); + } + gSprites[gBattleStruct->tera.triggerSpriteId].tBattler = battler; + gSprites[gBattleStruct->tera.triggerSpriteId].tHide = FALSE; + + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, palId); +} + +static void SpriteCb_TeraTrigger(struct Sprite *sprite) +{ + s32 xSlide, xPriority, xOptimal; + s32 yDiff; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + xSlide = DOUBLES_TERA_TRIGGER_POS_X_SLIDE; + xPriority = DOUBLES_TERA_TRIGGER_POS_X_PRIORITY; + xOptimal = DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL; + yDiff = DOUBLES_TERA_TRIGGER_POS_Y_DIFF; + } + else + { + xSlide = SINGLES_TERA_TRIGGER_POS_X_SLIDE; + xPriority = SINGLES_TERA_TRIGGER_POS_X_PRIORITY; + xOptimal = SINGLES_TERA_TRIGGER_POS_X_OPTIMAL; + yDiff = SINGLES_TERA_TRIGGER_POS_Y_DIFF; + } + + if (sprite->tHide) + { + if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) + sprite->x++; + + if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) + sprite->oam.priority = 2; + else + sprite->oam.priority = 1; + + sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; + sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; + if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) + DestroyTeraTriggerSprite(); + } + else + { + if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal) + sprite->x--; + + if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) + sprite->oam.priority = 2; + else + sprite->oam.priority = 1; + + sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; + sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; + } +} + +bool32 IsTeraTriggerSpriteActive(void) +{ + if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) + return FALSE; + else if (IndexOfSpritePaletteTag(TAG_TERA_TRIGGER_PAL) != 0xFF) + return TRUE; + else + return FALSE; +} + +void HideTeraTriggerSprite(void) +{ + if (gBattleStruct->tera.triggerSpriteId != 0xFF) + { + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, 0); + gSprites[gBattleStruct->tera.triggerSpriteId].tHide = TRUE; + } +} + +void DestroyTeraTriggerSprite(void) +{ + FreeSpritePaletteByTag(TAG_TERA_TRIGGER_PAL); + FreeSpriteTilesByTag(TAG_TERA_TRIGGER_TILE); + if (gBattleStruct->tera.triggerSpriteId != 0xFF) + DestroySprite(&gSprites[gBattleStruct->tera.triggerSpriteId]); + gBattleStruct->tera.triggerSpriteId = 0xFF; +} + +#undef tBattler +#undef tHide + +// TERA INDICATOR: +static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal"); +static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp"); +static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp"); +static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp"); +static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp"); +static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp"); +static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp"); +static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp"); +static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp"); +static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp"); +static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp"); +static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp"); +static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp"); +static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp"); +static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp"); +static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp"); +static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp"); +static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp"); +static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp"); +static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp"); + +static void SpriteCb_TeraIndicator(struct Sprite *sprite); +static const s8 sIndicatorPositions[][2] = +{ + [B_POSITION_PLAYER_LEFT] = {53, -9}, + [B_POSITION_OPPONENT_LEFT] = {44, -9}, + [B_POSITION_PLAYER_RIGHT] = {52, -9}, + [B_POSITION_OPPONENT_RIGHT] = {44, -9}, +}; + +static const struct SpritePalette sSpritePalette_TeraIndicator = +{ + sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL +}; + +static const struct OamData sOamData_TeraIndicator = +{ + .shape = SPRITE_SHAPE(16x16), + .size = SPRITE_SIZE(16x16), + .priority = 1, +}; + +static const struct SpriteTemplate sSpriteTemplate_NormalIndicator = +{ + .tileTag = TAG_NORMAL_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FightingIndicator = +{ + .tileTag = TAG_FIGHTING_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FlyingIndicator = +{ + .tileTag = TAG_FLYING_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_PoisonIndicator = +{ + .tileTag = TAG_POISON_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GroundIndicator = +{ + .tileTag = TAG_GROUND_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_RockIndicator = +{ + .tileTag = TAG_ROCK_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_BugIndicator = +{ + .tileTag = TAG_BUG_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GhostIndicator = +{ + .tileTag = TAG_GHOST_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_SteelIndicator = +{ + .tileTag = TAG_STEEL_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FireIndicator = +{ + .tileTag = TAG_FIRE_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_WaterIndicator = +{ + .tileTag = TAG_WATER_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GrassIndicator = +{ + .tileTag = TAG_GRASS_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_ElectricIndicator = +{ + .tileTag = TAG_ELECTRIC_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_PsychicIndicator = +{ + .tileTag = TAG_PSYCHIC_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_IceIndicator = +{ + .tileTag = TAG_ICE_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_DragonIndicator = +{ + .tileTag = TAG_DRAGON_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_DarkIndicator = +{ + .tileTag = TAG_DARK_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FairyIndicator = +{ + .tileTag = TAG_FAIRY_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_StellarIndicator = +{ + .tileTag = TAG_STELLAR_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] = +{ + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, + {sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE}, + {sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE}, + {sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE}, + {sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE}, + {sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE}, + {sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE}, + {sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE}, + {sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE}, + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY + {sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE}, + {sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE}, + {sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE}, + {sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE}, + {sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE}, + {sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE}, + {sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE}, + {sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE}, + {sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE}, + {sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE}, + {0} +}; + +static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] = +{ + [TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator, + [TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator, + [TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator, + [TYPE_POISON] = &sSpriteTemplate_PoisonIndicator, + [TYPE_GROUND] = &sSpriteTemplate_GroundIndicator, + [TYPE_ROCK] = &sSpriteTemplate_RockIndicator, + [TYPE_BUG] = &sSpriteTemplate_BugIndicator, + [TYPE_GHOST] = &sSpriteTemplate_GhostIndicator, + [TYPE_STEEL] = &sSpriteTemplate_SteelIndicator, + [TYPE_MYSTERY] = &sSpriteTemplate_NormalIndicator, // just in case + [TYPE_FIRE] = &sSpriteTemplate_FireIndicator, + [TYPE_WATER] = &sSpriteTemplate_WaterIndicator, + [TYPE_GRASS] = &sSpriteTemplate_GrassIndicator, + [TYPE_ELECTRIC] = &sSpriteTemplate_ElectricIndicator, + [TYPE_PSYCHIC] = &sSpriteTemplate_PsychicIndicator, + [TYPE_ICE] = &sSpriteTemplate_IceIndicator, + [TYPE_DRAGON] = &sSpriteTemplate_DragonIndicator, + [TYPE_DARK] = &sSpriteTemplate_DarkIndicator, + [TYPE_FAIRY] = &sSpriteTemplate_FairyIndicator, + [TYPE_STELLAR] = &sSpriteTemplate_StellarIndicator, +}; + +// for sprite data fields +#define tBattler data[0] +#define tType data[1] // Indicator type: tera +#define tPosX data[2] +#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit + +// data fields for healthboxMain +// oam.affineParam holds healthboxRight spriteId +#define hMain_TeraIndicatorId data[3] +#define hMain_HealthBarSpriteId data[5] +#define hMain_Battler data[6] +#define hMain_Data7 data[7] + +// data fields for healthboxRight +#define hOther_HealthBoxSpriteId data[5] + +// data fields for healthbar +#define hBar_HealthBoxSpriteId data[5] + +void TeraIndicator_LoadSpriteGfx(void) +{ + LoadSpriteSheets(sTeraIndicatorSpriteSheets); + LoadSpritePalette(&sSpritePalette_TeraIndicator); +} + +bool32 TeraIndicator_ShouldBeInvisible(u32 battler) +{ + return !IsTerastallized(battler); +} + +u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId) +{ + return gBattleStruct->tera.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler]; +} + +void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + u32 battler = gSprites[healthboxId].hMain_Battler; + + if (invisible == TRUE) + gSprites[spriteId].invisible = TRUE; + else // Try visible. + gSprites[spriteId].invisible = TeraIndicator_ShouldBeInvisible(battler); +} + +void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + gSprites[spriteId].oam.priority = oamPriority; +} + +void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level) +{ + s16 xDelta = 0; + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + + if (level >= 100) + xDelta -= 4; + else if (level < 10) + xDelta += 5; + + gSprites[spriteId].tLevelXDelta = xDelta; +} + +void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId) +{ + u32 position; + u8 spriteId; + s16 xHealthbox = 0, y = 0; + s32 x = 0; + u32 type = GetBattlerTeraType(battler); + + position = GetBattlerPosition(battler); + GetBattlerHealthboxCoords(battler, &xHealthbox, &y); + + x = sIndicatorPositions[position][0]; + y += sIndicatorPositions[position][1]; + + spriteId = gBattleStruct->tera.indicatorSpriteId[battler] = CreateSpriteAtEnd(sTeraIndicatorSpriteTemplates[type], 0, y, 0); + gSprites[spriteId].tBattler = battler; + gSprites[spriteId].tPosX = x; + gSprites[spriteId].invisible = TRUE; +} + +void TeraIndicator_DestroySprite(u32 healthboxSpriteId) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxSpriteId); + DestroySprite(&gSprites[spriteId]); +} + +void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId) +{ + TeraIndicator_DestroySprite(healthboxSpriteId); + TeraIndicator_CreateSprite(battler, healthboxSpriteId); +} + +static void SpriteCb_TeraIndicator(struct Sprite *sprite) +{ + u32 battler = sprite->tBattler; + + sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta; + sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2; + sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2; +} + +#undef tBattler +#undef tType +#undef tPosX +#undef tLevelXDelta diff --git a/src/battle_util.c b/src/battle_util.c index b6fb998376..e11ddc27ea 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -707,7 +707,7 @@ void HandleAction_NothingIsFainted(void) void HandleAction_ActionFinished(void) { - u32 i, j; + u32 i, j, moveType; bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou; *(gBattleStruct->monToSwitchIntoId + gBattlerByTurnOrder[gCurrentTurnActionNumber]) = gSelectedMonPartyId = PARTY_SIZE; gCurrentTurnActionNumber++; @@ -718,6 +718,16 @@ void HandleAction_ActionFinished(void) | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE); + // check if Stellar type boost should be used up + GET_MOVE_TYPE(gCurrentMove, moveType); + if (IsTerastallized(gBattlerAttacker) + && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR + && gMovesInfo[gCurrentMove].category != DAMAGE_CATEGORY_STATUS + && IsTypeStellarBoosted(gBattlerAttacker, moveType)) + { + ExpendTypeStellarBoost(gBattlerAttacker, moveType); + } + gCurrentMove = 0; gBattleMoveDamage = 0; gMoveResultFlags = 0; @@ -898,34 +908,35 @@ static const uq4_12_t sPercentToModifier[] = static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = {// Defender --> - // Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy - [TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, - [TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5)}, - [TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______}, - [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0)}, - [TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______}, - [TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______}, - [TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5)}, + // Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar + [TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______}, + [TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______}, + [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______}, + [TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______}, + [TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______}, + [TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______}, #if B_STEEL_RESISTANCES >= GEN_6 - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______}, + [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______}, #else - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______}, + [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______}, #endif - [TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0)}, - [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, - [TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______}, - [TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______}, - [TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______}, - [TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______}, - [TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______}, - [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______}, - [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0)}, + [TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______}, + [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______}, + [TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, + [TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, + [TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______}, + [TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______}, + [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______}, + [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______}, #if B_STEEL_RESISTANCES >= GEN_6 - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5)}, + [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, #else - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5)}, + [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, #endif - [TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______}, + [TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______}, + [TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, }; #undef ______ @@ -1276,7 +1287,7 @@ static bool32 IsBelchPreventingMove(u32 battler, u32 move) u32 TrySetCantSelectMoveBattleScript(u32 battler) { u32 limitations = 0; - u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX); + u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL); u32 move = gBattleMons[battler].moves[moveId]; u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[battler]; @@ -5349,6 +5360,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gMovesInfo[move].power != 0 && TARGET_TURN_DAMAGED && !IS_BATTLER_OF_TYPE(battler, moveType) + && moveType != TYPE_STELLAR && gBattleMons[battler].hp != 0) { SET_BATTLER_TYPE(battler, moveType); @@ -8842,6 +8854,10 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 if (RandomPercentage(RNG_FICKLE_BEAM, 30)) basePower *= 2; break; + case EFFECT_TERA_BLAST: + if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) == TYPE_STELLAR) + basePower = 100; + break; case EFFECT_LAST_RESPECTS: basePower += (basePower * min(100, GetBattlerSideFaintCounter(battlerAtk))); break; @@ -9210,6 +9226,19 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); break; } + + // Terastallization boosts weak, non-priority, non-multi hit moves after modifiers to 60 BP. + if (IsTerastallized(battlerAtk) + && (moveType == GetBattlerTeraType(battlerAtk) + || (GetBattlerTeraType(battlerAtk) == TYPE_STELLAR && IsTypeStellarBoosted(battlerAtk, moveType))) + && uq4_12_multiply_by_int_half_down(modifier, basePower) < 60 + && gMovesInfo[move].strikeCount < 2 + && gMovesInfo[move].effect != EFFECT_MULTI_HIT + && gMovesInfo[move].priority == 0) + { + return 60; + } + return uq4_12_multiply_by_int_half_down(modifier, basePower); } @@ -9912,7 +9941,10 @@ static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef, dmg /= 100; } - DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk)); + if (IsTerastallized(battlerAtk)) + DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, moveType)); + else + DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk)); DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(battlerAtk, move, abilityAtk)); DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(battlerDef, move)); @@ -10119,12 +10151,12 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov { u32 illusionSpecies; - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0), battlerAtk, recordAbilities); - if (GetBattlerType(battlerDef, 1) != GetBattlerType(battlerDef, 0)) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1), battlerAtk, recordAbilities); - if (GetBattlerType(battlerDef, 2) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 1) - && GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 0)) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2), battlerAtk, recordAbilities); + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0, FALSE), battlerAtk, recordAbilities); + if (GetBattlerType(battlerDef, 1, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1, FALSE), battlerAtk, recordAbilities); + if (GetBattlerType(battlerDef, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 1, FALSE) + && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2, FALSE), battlerAtk, recordAbilities); if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef))) TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies); @@ -10185,12 +10217,16 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, { uq4_12_t modifier = UQ_4_12(1.0); - if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) + if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY && moveType != TYPE_STELLAR) { modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); if (gMovesInfo[move].effect == EFFECT_TWO_TYPED_MOVE) modifier = CalcTypeEffectivenessMultiplierInternal(move, gMovesInfo[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); } + else if (moveType == TYPE_STELLAR) + { + modifier = IsTerastallized(battlerDef) ? UQ_4_12(2.0) : UQ_4_12(1.0); + } if (recordAbilities) UpdateMoveResultFlags(modifier); @@ -10640,8 +10676,8 @@ bool32 TryBattleFormChange(u32 battler, u16 method) bool32 DoBattlersShareType(u32 battler1, u32 battler2) { s32 i; - u8 types1[3] = {GetBattlerType(battler1, 0), GetBattlerType(battler1, 1), GetBattlerType(battler1, 2)}; - u8 types2[3] = {GetBattlerType(battler2, 0), GetBattlerType(battler2, 1), GetBattlerType(battler2, 2)}; + u8 types1[3] = {GetBattlerType(battler1, 0, FALSE), GetBattlerType(battler1, 1, FALSE), GetBattlerType(battler1, 2, FALSE)}; + u8 types2[3] = {GetBattlerType(battler2, 0, FALSE), GetBattlerType(battler2, 1, FALSE), GetBattlerType(battler2, 2, FALSE)}; if (types1[2] == TYPE_MYSTERY) types1[2] = types1[0]; @@ -10823,7 +10859,7 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) return FALSE; } -// Photon geyser & light that burns the sky +// Photon Geyser, Light That Burns the Sky, Tera Blast u8 GetCategoryBasedOnStats(u32 battler) { u32 attack = gBattleMons[battler].attack; @@ -11381,17 +11417,23 @@ bool8 IsMonBannedFromSkyBattles(u16 species) } } -u8 GetBattlerType(u32 battler, u8 typeIndex) +u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera) { + u32 teraType = GetBattlerTeraType(battler); u16 types[3] = {0}; types[0] = gBattleMons[battler].type1; types[1] = gBattleMons[battler].type2; types[2] = gBattleMons[battler].type3; + // Handle Terastallization + if (IsTerastallized(battler) && teraType != TYPE_STELLAR && !ignoreTera) + return GetBattlerTeraType(battler); + // Handle Roost's Flying-type suppression if (typeIndex == 0 || typeIndex == 1) { - if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST) + if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST + && !IsTerastallized(battler)) { if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING) return B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY; @@ -11406,6 +11448,8 @@ u8 GetBattlerType(u32 battler, u8 typeIndex) void RemoveBattlerType(u32 battler, u8 type) { u32 i; + if (IsTerastallized(battler)) // don't remove type if Terastallized + return; for (i = 0; i < 3; i++) { if (*(u8 *)(&gBattleMons[battler].type1 + i) == type) diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 52efcb1212..de8d3f9ec8 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2230,4 +2230,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points .encourageEncore = TRUE, }, + + [EFFECT_TERA_BLAST] = + { + .battleScript = BattleScript_EffectPhotonGeyser, + .battleTvScore = 0, // TODO: Assign points + }, }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 10e0c8ef28..911f2b6b82 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -18440,7 +18440,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "If the user's Terastallized,\n" "it hits with its Tera-type."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_TERA_BLAST, + .effect = EFFECT_TERA_BLAST, .power = 80, .type = TYPE_NORMAL, .accuracy = 100, @@ -18449,6 +18449,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .forcePressure = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_TERA_BLAST, + .self = TRUE, + }), }, [MOVE_SILK_TRAP] = diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index f5c2e7f6b7..6e63ce5eab 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -891,6 +891,10 @@ static const union AnimCmd sSpriteAnim_TypeFairy[] = { ANIMCMD_FRAME(TYPE_FAIRY * 8, 0, FALSE, FALSE), ANIMCMD_END }; +static const union AnimCmd sSpriteAnim_TypeStellar[] = { + ANIMCMD_FRAME(TYPE_STELLAR * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; static const union AnimCmd sSpriteAnim_CategoryCool[] = { ANIMCMD_FRAME((CONTEST_CATEGORY_COOL + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), ANIMCMD_END @@ -931,6 +935,7 @@ static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES sSpriteAnim_TypeDragon, sSpriteAnim_TypeDark, sSpriteAnim_TypeFairy, + sSpriteAnim_TypeStellar, sSpriteAnim_CategoryCool, sSpriteAnim_CategoryBeauty, sSpriteAnim_CategoryCute, diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 31e40fa31a..763bb3f9e6 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -7,6 +7,7 @@ #include "decompress.h" #include "event_data.h" #include "international_string_util.h" +#include "item.h" #include "link.h" #include "link_rfu.h" #include "main.h" @@ -38,6 +39,10 @@ void HealPlayerParty(void) HealPokemon(&gPlayerParty[i]); if (OW_PC_HEAL >= GEN_8) HealPlayerBoxes(); + + // Recharge Tera Orb, if possible. + if (B_FLAG_TERA_ORB_CHARGED != 0 && CheckBagHasItem(ITEM_TERA_ORB, 1)) + FlagSet(B_FLAG_TERA_ORB_CHARGED); } static void HealPlayerBoxes(void) diff --git a/test/dynamax.c b/test/battle/gimmick/dynamax.c similarity index 100% rename from test/dynamax.c rename to test/battle/gimmick/dynamax.c diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c new file mode 100644 index 0000000000..3166e2d67c --- /dev/null +++ b/test/battle/gimmick/terastal.c @@ -0,0 +1,802 @@ +#include "global.h" +#include "test/battle.h" + +// Base Power and STAB Checks + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type preserves other STAB boosts", s16 damage1, s16 damage2) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_BULBASAUR) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_VINE_WHIP, tera: tera); } + TURN { MOVE(player, MOVE_SLUDGE_BOMB); } + } SCENE { + MESSAGE("Bulbasaur used Vine Whip!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_VINE_WHIP, player); + HP_BAR(opponent, captureDamage: &results[i].damage1); + MESSAGE("Bulbasaur used Sludge Bomb!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLUDGE_BOMB, player); + HP_BAR(opponent, captureDamage: &results[i].damage2); + } FINALLY { + EXPECT_EQ(results[0].damage1, results[1].damage1); + EXPECT_EQ(results[0].damage2, results[1].damage2); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing does not affect the power of non-STAB moves", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type gives that type 1.5x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from no STAB to 1.5x STAB is a 1.5x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PSYCHIC, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Psychic!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 1.5x STAB to 2.0x STAB is a 1.33x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.33), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); } + } SCENE { + MESSAGE("Crawdaunt used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from no STAB to 2.0x STAB is a 2.0x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WATER_PULSE, tera: tera); } + } SCENE { + MESSAGE("Crawdaunt used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 2x STAB to 2.25x STAB is a 1.125x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_ABSORB].power == 20); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ABSORB, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Absorb!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 20 BP to 90 BP (60 * 1.5x) is a 4.5x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_MEGA_DRAIN].power == 40); + PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: tera); } + } SCENE { + MESSAGE("Mr. Mime used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // This should be the same as a normal Tera boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STORED_POWER, tera: tera); } + } SCENE { + MESSAGE("Mr. Mime used Stored Power!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STORED_POWER, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 45 BP (20 * 1.5x * 1.5x) to 120 BP (60 * 2.0x) is a 2.667x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.667), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to multi-hit moves", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FURY_SWIPES, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Fury Swipes!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to priority moves", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_QUICK_ATTACK, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Quick Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +// Defensive Type Checks + +SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_WATER_GUN); } + } SCENE { + MESSAGE("Foe Wobbuffet used Water Gun!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); } + } SCENE { + MESSAGE("Foe Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect Wobbuffet…"); + NOT { HP_BAR(player); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization persists across switches") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); } + TURN { SWITCH(player, 1); } + TURN { SWITCH(player, 0); } + TURN { MOVE(opponent, MOVE_EARTHQUAKE); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect Wobbuffet…"); + NOT { HP_BAR(player); } + // turn 4 + MESSAGE("Foe Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect Wobbuffet…"); + NOT { HP_BAR(player); } + } +} + +// Other Type Checks + +SINGLE_BATTLE_TEST("(TERA) Terastallization changes the effect of Curse") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CURSE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Curse!"); + HP_BAR(player); + MESSAGE("Wobbuffet cut its own HP and laid a CURSE on Foe Wobbuffet!"); + NOT { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Roost does not remove the user's Flying type while Terastallized") +{ + GIVEN { + PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_FLYING); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); } + } SCENE { + MESSAGE("Zapdos used Roost!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player); + MESSAGE("Foe Wobbuffet used Ice Beam!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_BEAM, opponent); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Type-changing moves fail against a Terastallized Pokemon") +{ + u16 move; + PARAMETRIZE { move = MOVE_SOAK; } + PARAMETRIZE { move = MOVE_FORESTS_CURSE; } + PARAMETRIZE { move = MOVE_TRICK_OR_TREAT; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, move); } + } SCENE { + if (move != MOVE_SOAK) + NOT { ANIMATION(ANIM_TYPE_MOVE, move, opponent); } + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Reflect Type fails if used by a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Conversion fails if used by a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CONVERSION, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Conversion!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if used by a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_CONVERSION_2, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Terastallized Pokemon's Tera Type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + TURN { MOVE(opponent, MOVE_REFLECT_TYPE); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + // turn 2 + MESSAGE("Foe Wobbuffet used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, opponent); + // turn 3 + MESSAGE("Wobbuffet used Tackle!"); + MESSAGE("It doesn't affect Foe Wobbuffet…"); + NOT { HP_BAR(opponent); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Synchronoise uses a Terastallized Pokemon's Tera Type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + } WHEN { + TURN { MOVE(opponent, MOVE_SYNCHRONOISE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + TURN { MOVE(opponent, MOVE_SYNCHRONOISE, tera: TRUE); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Synchronoise!"); + MESSAGE("It had no effect on Wobbuffet!"); + // turn 2 + MESSAGE("Foe Wobbuffet used Synchronoise!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, opponent); + } +} + +SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Terastallized Pokemon's Tera Type") +{ + GIVEN { + ASSUME(P_GEN_7_POKEMON); + PLAYER(SPECIES_ORICORIO) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_GENGAR); + } WHEN { + TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); } + } SCENE { + #if B_EXPANDED_MOVE_NAMES == TRUE + MESSAGE("Oricorio used Revelation Dance!"); + #else + MESSAGE("Oricorio used RvlationDnce!"); + #endif + MESSAGE("It doesn't affect Foe Gengar…"); + NOT { HP_BAR(opponent); } + } +} + +// This tests that Tera STAB modifiers depend on the user's original types, too. +SINGLE_BATTLE_TEST("(TERA) Double Shock does not remove the user's Electric type while Terastallized, and changes STAB modifier depending on when it is used") +{ + s16 damage[4]; + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + PLAYER(SPECIES_PICHU) { TeraType(TYPE_ELECTRIC); } + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK, tera: TRUE); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_RECOVER); } + TURN { SWITCH(player, 0); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } + } SCENE { + // turn 1 - regular STAB + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 - lost Electric type, gained back from Tera + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 - retained Electric type + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + // turn 6 - original type reset, regular STAB + Tera boost + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[2]); + // turn 7 - regular STAB + Tera boost stays + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[3]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + EXPECT_MUL_EQ(damage[0], Q_4_12(1.333), damage[2]); + EXPECT_EQ(damage[2], damage[3]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type") +{ + KNOWN_FAILING; // Transform seems to be bugged in tests. + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TACKLE, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_TRANSFORM); } + TURN { MOVE(player, MOVE_EARTHQUAKE); } + // TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE, target: player, tera: TRUE); } + } SCENE { + // turn 2 + MESSAGE("Wobbuffet used Earthquake!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player); + HP_BAR(opponent); + // turn 3 + MESSAGE("Wobbuffet used Tackle!"); + MESSAGE("It doesn't affect Ditto…"); + NOT { HP_BAR(opponent); } + } +} + +// Stellar Type checks +SINGLE_BATTLE_TEST("(TERA) Stellar type does not change the user's defensive profile", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_PSYCHIC); } + } SCENE { + MESSAGE("Foe Wobbuffet used Psychic!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Stellar-type Pokemon's base type") +{ + GIVEN { + PLAYER(SPECIES_BANETTE) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + TURN { MOVE(opponent, MOVE_REFLECT_TYPE); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + // turn 2 + MESSAGE("Foe Wobbuffet used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, opponent); + // turn 3 + MESSAGE("Banette used Tackle!"); + MESSAGE("It doesn't affect Foe Wobbuffet…"); + NOT { HP_BAR(opponent); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base type") +{ + GIVEN { + ASSUME(P_GEN_7_POKEMON); + PLAYER(SPECIES_ORICORIO_SENSU) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_GUMSHOOS); + } WHEN { + TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); } + } SCENE { + #if B_EXPANDED_MOVE_NAMES == TRUE + MESSAGE("Oricorio used Revelation Dance!"); + #else + MESSAGE("Oricorio used RvlationDnce!"); + #endif + MESSAGE("It doesn't affect Foe Gumshoos…"); + NOT { HP_BAR(opponent); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + TURN { MOVE(opponent, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + // turn 2 + MESSAGE("Foe Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Roost does not remove Flying-type ground immunity when Terastallized into the Stellar type") +{ + GIVEN { + PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); } + } SCENE { + MESSAGE("Zapdos used Roost!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player); + MESSAGE("Foe Wobbuffet used Ice Beam!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_BEAM, opponent); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-time 2.0x boost to STAB moves") +{ + s16 damage[3]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_EXTRASENSORY); } + TURN { MOVE(player, MOVE_EXTRASENSORY, tera: TRUE); } + TURN { MOVE(player, MOVE_EXTRASENSORY); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Extrasensory!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Extrasensory!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 + MESSAGE("Wobbuffet used Extrasensory!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + // Extrasensory goes from a 50% boost to a 100% boost for a 1.33x total multiplier + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]); + EXPECT_EQ(damage[0], damage[2]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-time 1.2x boost to non-STAB moves") +{ + s16 damage[3]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TAKE_DOWN); } + TURN { MOVE(player, MOVE_TAKE_DOWN, tera: TRUE); } + TURN { MOVE(player, MOVE_TAKE_DOWN); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.2), damage[1]); + EXPECT_EQ(damage[0], damage[2]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves up to 60 BP once per type") +{ + s16 damage[4]; + GIVEN { + ASSUME(gMovesInfo[MOVE_MEGA_DRAIN].power == 40); + ASSUME(gMovesInfo[MOVE_BUBBLE].power == 40); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MEGA_DRAIN); } + TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: TRUE); } + TURN { MOVE(player, MOVE_MEGA_DRAIN); } + TURN { MOVE(player, MOVE_BUBBLE); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 + MESSAGE("Wobbuffet used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &damage[2]); + // turn 4 + MESSAGE("Wobbuffet used Bubble!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player); + HP_BAR(opponent, captureDamage: &damage[3]); + } THEN { + // The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost. + EXPECT_MUL_EQ(damage[0], Q_4_12(1.8), damage[1]); + EXPECT_EQ(damage[0], damage[2]); + EXPECT_EQ(damage[1], damage[3]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BUBBLE, tera: TRUE); + MOVE(opponent, MOVE_EMBER); } + } SCENE { + MESSAGE("Greninja used Bubble!"); + MESSAGE("Foe Wobbuffet used Ember!"); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Status moves don't expend Stellar's one-time type boost") +{ + s16 damage[2]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GROWL, tera: TRUE); } + TURN { MOVE(player, MOVE_TAKE_DOWN); } + TURN { MOVE(player, MOVE_TAKE_DOWN); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Growl!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player); + // turn 2 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 3 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.20), damage[0]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically-typed moves") +{ + s16 damage[4]; + GIVEN { + ASSUME(gMovesInfo[MOVE_WEATHER_BALL].type == TYPE_NORMAL); + PLAYER(SPECIES_PELIPPER) { Ability(ABILITY_DRIZZLE); TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WEATHER_BALL, tera: TRUE); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_TAKE_DOWN); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_TAKE_DOWN); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_WATER_PULSE); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_WATER_PULSE); MOVE(opponent, MOVE_RECOVER); } + } SCENE { + MESSAGE("Pelipper used Weather Ball!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WEATHER_BALL, player); + // turn 2 + MESSAGE("Pelipper used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 3 + MESSAGE("Pelipper used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 4 + MESSAGE("Pelipper used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + // turn 5 + MESSAGE("Pelipper used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &damage[3]); + } THEN { + // Take Down should have a Normal type boost applied + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.20), damage[0]); + // Water Pulse should not have a Water type boost applied + EXPECT_EQ(damage[3], damage[2]); + } +} + +SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly") +{ + u32 type; + PARAMETRIZE { type = TYPE_NORMAL; } + PARAMETRIZE { type = TYPE_FIGHTING; } + PARAMETRIZE { type = TYPE_FLYING; } + PARAMETRIZE { type = TYPE_POISON; } + PARAMETRIZE { type = TYPE_GROUND; } + PARAMETRIZE { type = TYPE_ROCK; } + PARAMETRIZE { type = TYPE_BUG; } + PARAMETRIZE { type = TYPE_GHOST; } + PARAMETRIZE { type = TYPE_STEEL; } + PARAMETRIZE { type = TYPE_MYSTERY; } + PARAMETRIZE { type = TYPE_FIRE; } + PARAMETRIZE { type = TYPE_WATER; } + PARAMETRIZE { type = TYPE_GRASS; } + PARAMETRIZE { type = TYPE_ELECTRIC; } + PARAMETRIZE { type = TYPE_PSYCHIC; } + PARAMETRIZE { type = TYPE_ICE; } + PARAMETRIZE { type = TYPE_DRAGON; } + PARAMETRIZE { type = TYPE_DARK; } + PARAMETRIZE { type = TYPE_FAIRY; } + PARAMETRIZE { type = TYPE_STELLAR; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(type); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + } +} diff --git a/test/battle/move_effect/tera_blast.c b/test/battle/move_effect/tera_blast.c new file mode 100644 index 0000000000..4592cf32a5 --- /dev/null +++ b/test/battle/move_effect/tera_blast.c @@ -0,0 +1,137 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TERA_BLAST].effect == EFFECT_TERA_BLAST); +} + +SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's Tera Type") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TERA_BLAST].type == TYPE_NORMAL); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_DARK); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Tera Blast becomes a physical move if the user is Terastallized and has a higher Attack stat", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); Attack(100); SpAttack(50); } + OPPONENT(SPECIES_WOBBUFFET) { Defense(200); SpDefense(200); } + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // Since Wobbuffett has equal defenses, Tera Blast should do 1.5x more damage + // from gaining STAB and an additional 2.0x damage from using its highest + // attacking stat. + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(3.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast lowers both offensive stats") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack fell!"); + MESSAGE("Wobbuffet's Sp. Atk fell!"); + } +} + + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast has 100 BP and a one-time 1.2x boost") +{ + s16 damage[3]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + TURN { MOVE(player, MOVE_WORK_UP); } + TURN { MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 4 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + // 80 BP to 120 BP (100 * 1.2) boost upon Terastallizing + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.50), damage[1]); + // 120 BP to 100 BP after Stellar boost expended + EXPECT_MUL_EQ(damage[2], UQ_4_12(1.20), damage[1]); + } +} + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast is super-effective on Stellar-type Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_CELEBRATE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast activates a Stellar-type Pokemon's Weakness Policy") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_WEAKNESS_POLICY); TeraType(TYPE_NORMAL); } + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_CELEBRATE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } +} + +SINGLE_BATTLE_TEST("Flying-type Tera Blast does not have its priority boosted by Gale Wings") +{ + GIVEN { + PLAYER(SPECIES_TALONFLAME) { Ability(ABILITY_GALE_WINGS); TeraType(TYPE_FLYING); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_QUICK_ATTACK); } + } SCENE { + MESSAGE("Foe Wobbuffet used Quick Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent); + MESSAGE("Talonflame used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + } +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index b800f9099f..aaeba97479 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2075,6 +2075,9 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * if (ctx->explicitDynamax && ctx->dynamax) *moveSlot |= RET_DYNAMAX; + + if (ctx->explicitTera && ctx->tera) + *moveSlot |= RET_TERASTAL; } void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) From 536e4a8fb9b3d0008e5806f713e135c9b559b89f Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:08:20 -0700 Subject: [PATCH 15/71] fixed psychic type tera icon (#4435) --- .../battle_interface/poison_indicator.png | Bin 4959 -> 4937 bytes .../battle_interface/psychic_indicator.png | Bin 4937 -> 5168 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/graphics/battle_interface/poison_indicator.png b/graphics/battle_interface/poison_indicator.png index 84806865ccef15dc1acefaf8cfbfae06892db8d0..c2646d0a71b53c8785459ce1c5cb68c507e66180 100644 GIT binary patch delta 1259 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 delta 1635 zcmV-p2Auh@D9I*}BmujTB_V$tD-8VCDSQNkSj2HyBYcB*d?(a0)76W6{N{6tC=eoxSlflgpts^S05V@8$q+nx7KI^8g6uHnX|g**Kpw@(d_qN6R!)hZ z=r5#5*z^R&Z7{<*KGd_n=y!*-Li}lGsprl&dQK)@k8`|ZG^H{6cDn2WLK^l%oza;B z3=2XoYgs^xcs=B}T0(zQLiScr-b^(Gt#V=uS_V6AoOy6xW915n&UPCyT3f9wol3OH zRxt#A+-y8-1Y=DUY*o-J(ApCVuYJ?nuCij|#;~*uXN(n=X7?TKsiQGw$}<~1V+CAx zUJAgN%*`HFAcV(}M}7tPXxC@*DqzsBFe?t2P1#%&&wFmoJd%G!w7}3~-0m93wmU$G zu(t;o7BFA}i|4>5S{s=WM+H9_jue;17Er+6+Xj@@i3E^?8-u;ECi1ei-fn8tED+%( zNCY*wQsBy=upxh$q@hl< z&|=e;TJGGFZvq*Arj6FS_C9NTux1xi(=eW_VQGwq3!de~PtL%YbqvPiWB>_mCuc_4 zIZKne$(czgbK)&f`bkdX$ru=Povj~ya`(a94R7S!|KiOa%+aLoJD8(M-4o{Sd3(TG zo4+>I2rY)@HJm{;g6ju^uGwmyKWcA}^wkAFj(!~dIC_779Ob;0^wb7rV~Hb+s2Rb} zi7j1pcr&zah8_O_P2LchCH%uEeT-A2koTRm_7_Xi0VtY2q~ENPmV(kkuN_5Ax@-$e z(W+k!7d?pEH9XmeZJMf-`Xq>SG5cza&-2q4V%uH;r3-`;_)DOt)dyhu3*xs&x*~o8 z=}Cw`xT8CNjQAE$6^me@ zv=v%)FnQ@8G-*guTpR`0f`dPcRRZ7c=mzuY2mFx{LBG@4i2)SIL_U z@QK8Kb4)iZ;tk@NO-tvzPaI|?Ng+Nb9yjQM#E)E;U4G+SbXee-VIz~8Ck_*f#SWG` zn3W8bc$zq(s2b%9S(g>gTb$KOjkWH{Ul_`3D`~FN8bKUONFWIjGHNKJ0t->vHBwBZ z=sf1(A9nmna>?W>gOOtaRj800KlmT~o~>Dbn3{BxLNOrlV%s01KyVjm)@}Ry*tVM| zfd3h|(%SxN1DO3Jz24R$M?l{;aBIl^&Gi7EuH#s$8 zEjKebIW06XW-%>dVKp@^HeoR}G-NnqVq!8llj8|f1Ti`?F|!#9Xb1!;=eoC(g%es* z=LHi2DHUhNS@i$_05C~JK~xCWV_={R@c;iD0A_qPwl{?^{zDn|#-AY! Date: Wed, 24 Apr 2024 20:10:25 +0200 Subject: [PATCH 16/71] Combine weather set commands (#4434) * Combine Weather set commands * further tests * minor error * update test names --- asm/macros/battle_script.inc | 13 ++-- data/battle_scripts_1.s | 12 ++-- src/battle_script_commands.c | 107 +++++++++++---------------------- test/battle/weather/hail.c | 24 ++++++++ test/battle/weather/rain.c | 22 +++++++ test/battle/weather/snow.c | 24 ++++++++ test/battle/weather/sunlight.c | 22 +++++++ 7 files changed, 139 insertions(+), 85 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 25e0110a51..69e4dd8d97 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -710,8 +710,9 @@ .byte 0x7c .endm - .macro setrain + .macro setfieldweather weather:req .byte 0x7d + .byte \weather .endm .macro setreflect @@ -833,7 +834,7 @@ .byte 0x94 .endm - .macro setsandstorm + .macro unused_95 .byte 0x95 .endm @@ -1005,7 +1006,7 @@ .4byte \jumpInstr .endm - .macro setsunny + .macro unused_bb .byte 0xbb .endm @@ -1080,7 +1081,7 @@ .byte 0xc7 .endm - .macro sethail + .macro unused_c8 .byte 0xc8 .endm @@ -1409,10 +1410,6 @@ callnative BS_TryRevertWeatherForm .endm - .macro setsnow - callnative BS_SetSnow - .endm - .macro applysaltcure battler:req callnative BS_ApplySaltCure .byte \battler diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 5e36241c9a..9da79c38d8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -187,7 +187,7 @@ BattleScript_EffectChillyReception:: jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_EffectChillyReceptionBlockedByPrimalRain jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_EffectChillyReceptionBlockedByStrongWinds call BattleScript_EffectChillyReceptionPlayAnimation - setsnow + setfieldweather ENUM_WEATHER_SNOW call BattleScript_MoveWeatherChangeRet goto BattleScript_MoveSwitch BattleScript_EffectChillyReceptionPlayAnimation: @@ -4335,7 +4335,7 @@ BattleScript_EffectSandstorm:: attackstring ppreduce call BattleScript_CheckPrimalWeather - setsandstorm + setfieldweather ENUM_WEATHER_SANDSTORM goto BattleScript_MoveWeatherChange BattleScript_EffectRollout:: @@ -4501,7 +4501,7 @@ BattleScript_EffectRainDance:: attackstring ppreduce call BattleScript_CheckPrimalWeather - setrain + setfieldweather ENUM_WEATHER_RAIN BattleScript_MoveWeatherChange:: attackanimation waitanimation @@ -4519,7 +4519,7 @@ BattleScript_EffectSunnyDay:: attackstring ppreduce call BattleScript_CheckPrimalWeather - setsunny + setfieldweather ENUM_WEATHER_SUN goto BattleScript_MoveWeatherChange BattleScript_ExtremelyHarshSunlightWasNotLessened: @@ -4877,7 +4877,7 @@ BattleScript_EffectHail:: attackstring ppreduce call BattleScript_CheckPrimalWeather - sethail + setfieldweather ENUM_WEATHER_HAIL goto BattleScript_MoveWeatherChange BattleScript_EffectTorment:: @@ -9996,5 +9996,5 @@ BattleScript_EffectSnow:: attackstring ppreduce call BattleScript_CheckPrimalWeather - setsnow + setfieldweather ENUM_WEATHER_SNOW goto BattleScript_MoveWeatherChange diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ddcbeee78c..21dcf57f6b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -452,7 +452,7 @@ static void Cmd_setatkhptozero(void); static void Cmd_jumpifnexttargetvalid(void); static void Cmd_tryhealhalfhealth(void); static void Cmd_trymirrormove(void); -static void Cmd_setrain(void); +static void Cmd_setfieldweather(void); static void Cmd_setreflect(void); static void Cmd_setseeded(void); static void Cmd_manipulatedamage(void); @@ -476,7 +476,7 @@ static void Cmd_givepaydaymoney(void); static void Cmd_setlightscreen(void); static void Cmd_tryKO(void); static void Cmd_damagetohalftargethp(void); -static void Cmd_setsandstorm(void); +static void Cmd_unused_95(void); static void Cmd_weatherdamage(void); static void Cmd_tryinfatuating(void); static void Cmd_updatestatusicon(void); @@ -514,7 +514,7 @@ static void Cmd_presentdamagecalculation(void); static void Cmd_setsafeguard(void); static void Cmd_magnitudedamagecalculation(void); static void Cmd_jumpifnopursuitswitchdmg(void); -static void Cmd_setsunny(void); +static void Cmd_unused_bb(void); static void Cmd_halvehp(void); static void Cmd_copyfoestats(void); static void Cmd_rapidspinfree(void); @@ -527,7 +527,7 @@ static void Cmd_trydobeatup(void); static void Cmd_setsemiinvulnerablebit(void); static void Cmd_tryfiretwoturnmovenowbyeffect(void); static void Cmd_setminimize(void); -static void Cmd_sethail(void); +static void Cmd_unused_c8(void); static void Cmd_trymemento(void); static void Cmd_setforcedtarget(void); static void Cmd_setcharge(void); @@ -650,7 +650,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_end, //0x3D Cmd_end2, //0x3E Cmd_end3, //0x3F - Cmd_unused5, //0x40 + Cmd_unused5, //0x40 Cmd_call, //0x41 Cmd_setroost, //0x42 Cmd_jumpifabilitypresent, //0x43 @@ -711,7 +711,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_jumpifnexttargetvalid, //0x7A Cmd_tryhealhalfhealth, //0x7B Cmd_trymirrormove, //0x7C - Cmd_setrain, //0x7D + Cmd_setfieldweather, //0x7D Cmd_setreflect, //0x7E Cmd_setseeded, //0x7F Cmd_manipulatedamage, //0x80 @@ -735,7 +735,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_setlightscreen, //0x92 Cmd_tryKO, //0x93 Cmd_damagetohalftargethp, //0x94 - Cmd_setsandstorm, //0x95 + Cmd_unused_95, //0x95 Cmd_weatherdamage, //0x96 Cmd_tryinfatuating, //0x97 Cmd_updatestatusicon, //0x98 @@ -773,7 +773,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_setsafeguard, //0xB8 Cmd_magnitudedamagecalculation, //0xB9 Cmd_jumpifnopursuitswitchdmg, //0xBA - Cmd_setsunny, //0xBB + Cmd_unused_bb, //0xBB Cmd_halvehp, //0xBC Cmd_copyfoestats, //0xBD Cmd_rapidspinfree, //0xBE @@ -786,7 +786,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_setsemiinvulnerablebit, //0xC5 Cmd_tryfiretwoturnmovenowbyeffect, //0xC6 Cmd_setminimize, //0xC7 - Cmd_sethail, //0xC8 + Cmd_unused_c8, //0xC8 Cmd_trymemento, //0xC9 Cmd_setforcedtarget, //0xCA Cmd_setcharge, //0xCB @@ -11085,19 +11085,39 @@ static void Cmd_trymirrormove(void) } } -static void Cmd_setrain(void) +static void Cmd_setfieldweather(void) { - CMD_ARGS(); + CMD_ARGS(u8 weather); - if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_RAIN, FALSE)) + u8 weather = cmd->weather; + + if (!TryChangeBattleWeather(gBattlerAttacker, weather, FALSE)) { gMoveResultFlags |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; + gBattlescriptCurrInstr = cmd->nextInstr; + return; } - else + + switch (weather) { + case ENUM_WEATHER_RAIN: gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_RAIN; + break; + case ENUM_WEATHER_SUN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SUNLIGHT; + break; + case ENUM_WEATHER_SANDSTORM: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SANDSTORM; + break; + case ENUM_WEATHER_HAIL: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_HAIL; + break; + case ENUM_WEATHER_SNOW: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW; + break; } + gBattlescriptCurrInstr = cmd->nextInstr; } @@ -12300,20 +12320,8 @@ static void Cmd_damagetohalftargethp(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_setsandstorm(void) +static void Cmd_unused_95(void) { - CMD_ARGS(); - - if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SANDSTORM, FALSE)) - { - gMoveResultFlags |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SANDSTORM; - } - gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_weatherdamage(void) @@ -13519,21 +13527,8 @@ static void Cmd_jumpifnopursuitswitchdmg(void) } } -static void Cmd_setsunny(void) +static void Cmd_unused_bb(void) { - CMD_ARGS(); - - if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SUN, FALSE)) - { - gMoveResultFlags |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SUNLIGHT; - } - - gBattlescriptCurrInstr = cmd->nextInstr; } // Belly Drum, Fillet Away @@ -13850,21 +13845,8 @@ static void Cmd_setminimize(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_sethail(void) +static void Cmd_unused_c8(void) { - CMD_ARGS(); - - if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_HAIL, FALSE)) - { - gMoveResultFlags |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_HAIL; - } - - gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_trymemento(void) @@ -16212,23 +16194,6 @@ void BS_TryRevertWeatherForm(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_SetSnow(void) -{ - NATIVE_ARGS(); - - if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SNOW, FALSE)) - { - gMoveResultFlags |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; - - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW; - } - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_HandleMegaEvolution(void) { NATIVE_ARGS(u8 battler, u8 caseId); diff --git a/test/battle/weather/hail.c b/test/battle/weather/hail.c index 8908e17a79..567037ccaa 100644 --- a/test/battle/weather/hail.c +++ b/test/battle/weather/hail.c @@ -29,3 +29,27 @@ SINGLE_BATTLE_TEST("Hail damage does not affect Ice-type Pokémon") NOT MESSAGE("Foe Glalie is pelted by HAIL!"); } } + +SINGLE_BATTLE_TEST("Hail fails if Desolate Land or Primordial Sea are active") +{ + u32 species; + u32 item; + + PARAMETRIZE { species = SPECIES_WOBBUFFET; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_GROUDON; item = ITEM_RED_ORB; } + PARAMETRIZE { species = SPECIES_KYOGRE; item = ITEM_BLUE_ORB; } + + GIVEN { + PLAYER(species) { Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_HAIL); } + } SCENE { + if (item == ITEM_RED_ORB || item == ITEM_BLUE_ORB) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HAIL, opponent); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HAIL, opponent); + } + } +} diff --git a/test/battle/weather/rain.c b/test/battle/weather/rain.c index ce05870290..3359d25a81 100644 --- a/test/battle/weather/rain.c +++ b/test/battle/weather/rain.c @@ -45,3 +45,25 @@ SINGLE_BATTLE_TEST("Rain multiplies the power of Water-type moves by 1.5x", s16 EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); } } + +SINGLE_BATTLE_TEST("Drizzle fails if Desolate Land is active") +{ + u32 item; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_RED_ORB; } + + GIVEN { + PLAYER(SPECIES_GROUDON) { Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_RAIN_DANCE); } + } SCENE { + if (item == ITEM_RED_ORB) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent); + } + } +} diff --git a/test/battle/weather/snow.c b/test/battle/weather/snow.c index c617f651c4..3aceaf2f85 100644 --- a/test/battle/weather/snow.c +++ b/test/battle/weather/snow.c @@ -27,3 +27,27 @@ SINGLE_BATTLE_TEST("Snow multiplies the defense of Ice-types by 1.5x", s16 damag EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); } } + +SINGLE_BATTLE_TEST("Snowscape fails if Desolate Land or Primordial Sea are active") +{ + u32 species; + u32 item; + + PARAMETRIZE { species = SPECIES_WOBBUFFET; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_GROUDON; item = ITEM_RED_ORB; } + PARAMETRIZE { species = SPECIES_KYOGRE; item = ITEM_BLUE_ORB; } + + GIVEN { + PLAYER(species) { Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); } + } SCENE { + if (item == ITEM_RED_ORB || item == ITEM_BLUE_ORB) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, opponent); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, opponent); + } + } +} diff --git a/test/battle/weather/sunlight.c b/test/battle/weather/sunlight.c index 4c8ea5cb19..6cf6348987 100644 --- a/test/battle/weather/sunlight.c +++ b/test/battle/weather/sunlight.c @@ -45,3 +45,25 @@ SINGLE_BATTLE_TEST("Sunlight multiplies the power of Water-type moves by 0.5x", EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); } } + +SINGLE_BATTLE_TEST("Sunny Day fails if Primordial Sea is active") +{ + u32 item; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_BLUE_ORB; } + + GIVEN { + PLAYER(SPECIES_KYOGRE) { Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUNNY_DAY); } + } SCENE { + if (item == ITEM_BLUE_ORB) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); + } + } +} From baaefb4d7255cf295a413b0e92cf2f5aa8cd40c6 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 25 Apr 2024 09:10:36 +0100 Subject: [PATCH 17/71] Add missing shouldDynamax (#4442) --- test/battle/trainer_control.h | 1 + 1 file changed, 1 insertion(+) diff --git a/test/battle/trainer_control.h b/test/battle/trainer_control.h index b0f417bb4d..125a19d46f 100644 --- a/test/battle/trainer_control.h +++ b/test/battle/trainer_control.h @@ -52,6 +52,7 @@ .isShiny = TRUE, #line 18 .dynamaxLevel = 5, + .shouldDynamax = TRUE, .moves = { #line 19 MOVE_AIR_SLASH, From 143d6fb2892bc845b065708dbc880cacef409f12 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 25 Apr 2024 09:11:01 +0100 Subject: [PATCH 18/71] Minor cleanup of Frisk test (#4441) --- test/battle/ability/frisk.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/battle/ability/frisk.c b/test/battle/ability/frisk.c index dd6d0f691f..385ab7e1f9 100644 --- a/test/battle/ability/frisk.c +++ b/test/battle/ability/frisk.c @@ -37,9 +37,9 @@ SINGLE_BATTLE_TEST("Frisk triggers in a Single Battle") DOUBLE_BATTLE_TEST("Frisk triggers for player in a Double Battle after switching-in after fainting") { - bool32 targetLeft; - PARAMETRIZE { targetLeft = TRUE; } - PARAMETRIZE { targetLeft = FALSE; } + struct BattlePokemon *target = NULL; + PARAMETRIZE { target = playerLeft; } + PARAMETRIZE { target = playerRight; } GIVEN { ASSUME(gMovesInfo[MOVE_POUND].power != 0); @@ -49,20 +49,20 @@ DOUBLE_BATTLE_TEST("Frisk triggers for player in a Double Battle after switching OPPONENT(SPECIES_WYNAUT) { Item(ITEM_POTION); } OPPONENT(SPECIES_WYNAUT); } WHEN { - TURN { MOVE(opponentLeft, MOVE_POUND, target: targetLeft ? playerLeft : playerRight); SEND_OUT(targetLeft ? playerLeft : playerRight, 2); } + TURN { MOVE(opponentLeft, MOVE_POUND, target: target); SEND_OUT(target, 2); } } SCENE { MESSAGE("Foe Wynaut used Pound!"); MESSAGE("Wobbuffet fainted!"); - ABILITY_POPUP(targetLeft ? playerLeft : playerRight, ABILITY_FRISK); + ABILITY_POPUP(target, ABILITY_FRISK); MESSAGE("Furret frisked Foe Wynaut and found its Potion!"); } } DOUBLE_BATTLE_TEST("Frisk triggers for opponent in a Double Battle after switching-in after fainting") { - bool32 targetLeft; - PARAMETRIZE { targetLeft = TRUE; } - PARAMETRIZE { targetLeft = FALSE; } + struct BattlePokemon *target = NULL; + PARAMETRIZE { target = opponentLeft; } + PARAMETRIZE { target = opponentRight; } GIVEN { ASSUME(gMovesInfo[MOVE_POUND].power != 0); @@ -72,11 +72,11 @@ DOUBLE_BATTLE_TEST("Frisk triggers for opponent in a Double Battle after switchi OPPONENT(SPECIES_WOBBUFFET) { HP(1); } OPPONENT(SPECIES_FURRET) { Ability(ABILITY_FRISK); }; } WHEN { - TURN { MOVE(playerLeft, MOVE_POUND, target: targetLeft ? opponentLeft : opponentRight); SEND_OUT(targetLeft ? opponentLeft : opponentRight, 2); } + TURN { MOVE(playerLeft, MOVE_POUND, target: target); SEND_OUT(target, 2); } } SCENE { MESSAGE("Wynaut used Pound!"); MESSAGE("Foe Wobbuffet fainted!"); - ABILITY_POPUP(targetLeft ? opponentLeft : opponentRight, ABILITY_FRISK); + ABILITY_POPUP(target, ABILITY_FRISK); MESSAGE("Foe Furret frisked Wynaut and found its Potion!"); } } From 94cb2133c6c2dd92f2e37e7de4d17216fb5cde77 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:13:57 +0200 Subject: [PATCH 19/71] Fixes ability dancer not targeting multiply targets (#4437) * Fixes ability dancer not targeting multiply targets * remove file --- src/battle_util.c | 3 +++ test/battle/ability/dancer.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 65a4d79089..c68959a8ff 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5751,6 +5751,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 // Set the target to the original target of the mon that first used a Dance move gBattlerTarget = gBattleScripting.savedBattler & 0x3; + // Edge case for dance moves that hit multiply targets + gHitMarker &= ~HITMARKER_NO_ATTACKSTRING; + // Make sure that the target isn't an ally - if it is, target the original user if (GetBattlerSide(gBattlerTarget) == GetBattlerSide(gBattlerAttacker)) gBattlerTarget = (gBattleScripting.savedBattler & 0xF0) >> 4; diff --git a/test/battle/ability/dancer.c b/test/battle/ability/dancer.c index 7e520f440b..34af26e75e 100644 --- a/test/battle/ability/dancer.c +++ b/test/battle/ability/dancer.c @@ -36,11 +36,11 @@ SINGLE_BATTLE_TEST("Dancer can copy Teeter Dance") DOUBLE_BATTLE_TEST("Dancer can copy Teeter Dance and confuse both opposing targets") { - KNOWN_FAILING; // Fails because copied move that targets both opposing mons, targets only one when copied by Dancer GIVEN { ASSUME(gMovesInfo[MOVE_TEETER_DANCE].danceMove == TRUE); + ASSUME(gItemsInfo[ITEM_LUM_BERRY].holdEffect == HOLD_EFFECT_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) - PLAYER(SPECIES_WYNAUT) + PLAYER(SPECIES_WYNAUT) { Item(ITEM_LUM_BERRY); } OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Item(ITEM_LUM_BERRY); } OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); } } WHEN { @@ -50,6 +50,6 @@ DOUBLE_BATTLE_TEST("Dancer can copy Teeter Dance and confuse both opposing targe ABILITY_POPUP(opponentLeft, ABILITY_DANCER); ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, opponentLeft); MESSAGE("Wobbuffet became confused!"); - MESSAGE("Wynaut became confusef!"); + MESSAGE("Wynaut became confused!"); } } From 62ce29d0e98467cf0235caff1bfb72a5b6ac0e04 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 20 Apr 2024 16:31:04 +0100 Subject: [PATCH 20/71] Debug Menu: Re-render 'Flags & Vars' rather than re-create --- src/debug.c | 54 +++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/debug.c b/src/debug.c index ea3ffbabb8..76fd85fdcd 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1344,36 +1344,16 @@ static void Debug_InitDebugBattleData(void) sDebugBattleData->aiFlags[i] = FALSE; } -static void Debug_RefreshListMenu(u8 taskId) +static void Debug_GenerateListMenuNames(u32 totalItems) { - u16 i; const u8 sColor_Red[] = _("{COLOR RED}"); const u8 sColor_Green[] = _("{COLOR GREEN}"); - u8 totalItems = 0, flagResult = 0; + u32 i, flagResult = 0; u8 const *name = NULL; - if (sDebugMenuListData->listId == 0) - { - gMultiuseListMenuTemplate = sDebugMenu_ListTemplate_FlagsVars; - totalItems = gMultiuseListMenuTemplate.totalItems; - } - else if (sDebugMenuListData->listId == 1 && sDebugBattleData->submenu <= 1) - { - gMultiuseListMenuTemplate = sDebugMenu_ListTemplate_Battle_1; - totalItems = gMultiuseListMenuTemplate.totalItems; - } - else if (sDebugMenuListData->listId == 1 && sDebugBattleData->submenu > 1) - { - gMultiuseListMenuTemplate = sDebugMenu_ListTemplate_Battle_2; - totalItems = 7; - } - - // Failsafe to prevent memory corruption - totalItems = min(totalItems, DEBUG_MAX_MENU_ITEMS); // Copy item names for all entries but the last (which is Cancel) - for(i = 0; i < totalItems; i++) + for (i = 0; i < totalItems; i++) { - if (sDebugMenuListData->listId == 1 && sDebugBattleData->submenu > 1) { u16 species; @@ -1429,6 +1409,31 @@ static void Debug_RefreshListMenu(u8 taskId) sDebugMenuListData->listItems[i].name = &sDebugMenuListData->itemNames[i][0]; sDebugMenuListData->listItems[i].id = i; } +} + +static void Debug_RefreshListMenu(u8 taskId) +{ + u8 totalItems = 0; + + if (sDebugMenuListData->listId == 0) + { + gMultiuseListMenuTemplate = sDebugMenu_ListTemplate_FlagsVars; + totalItems = gMultiuseListMenuTemplate.totalItems; + } + else if (sDebugMenuListData->listId == 1 && sDebugBattleData->submenu <= 1) + { + gMultiuseListMenuTemplate = sDebugMenu_ListTemplate_Battle_1; + totalItems = gMultiuseListMenuTemplate.totalItems; + } + else if (sDebugMenuListData->listId == 1 && sDebugBattleData->submenu > 1) + { + gMultiuseListMenuTemplate = sDebugMenu_ListTemplate_Battle_2; + totalItems = 7; + } + + // Failsafe to prevent memory corruption + totalItems = min(totalItems, DEBUG_MAX_MENU_ITEMS); + Debug_GenerateListMenuNames(totalItems); // Set list menu data gMultiuseListMenuTemplate.items = sDebugMenuListData->listItems; @@ -1595,7 +1600,8 @@ static void DebugTask_HandleMenuInput_FlagsVars(u8 taskId) else { func(taskId); - Debug_RedrawListMenu(taskId); + Debug_GenerateListMenuNames(gMultiuseListMenuTemplate.totalItems); + RedrawListMenu(gTasks[taskId].tMenuTaskId); } // Remove TRUE/FALSE window for functions that haven't been assigned flags From 1fea6b83cc3d65c039b1f6d452e41243fd3eb935 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Tue, 5 Mar 2024 13:57:39 +0000 Subject: [PATCH 21/71] PARAMETRIZE_LABEL in test/species.c PARAMETRIZE_LABEL is like PARAMETRIZE, except that it allows the user to provide a label which will be displayed in the test name line. This is useful for tracking _which_ PARAMETRIZE case failed in the situation where the numbers are unwieldy. --- include/test/test.h | 2 ++ test/species.c | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/include/test/test.h b/include/test/test.h index 790563e772..dbd547ba9f 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -215,6 +215,8 @@ static inline struct Benchmark BenchmarkStop(void) #define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter) +#define PARAMETRIZE_LABEL(f, label) if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter && (MgbaPrintf_(":N%s: " f " (%d/%d)", gTestRunnerState.test->name, label, gFunctionTestRunnerState->runParameter + 1, gFunctionTestRunnerState->parameters), 1)) + #define TO_DO \ do { \ Test_ExpectedResult(TEST_RESULT_TODO); \ diff --git a/test/species.c b/test/species.c index d412dd005a..986fdbb14f 100644 --- a/test/species.c +++ b/test/species.c @@ -6,12 +6,16 @@ TEST("Form species ID tables are shared between all forms") { u32 i; u32 species = SPECIES_NONE; + const u16 *formSpeciesIdTable; for (i = 0; i < NUM_SPECIES; i++) { - if (gSpeciesInfo[i].formSpeciesIdTable) PARAMETRIZE { species = i; } + if (gSpeciesInfo[i].formSpeciesIdTable) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } } - const u16 *formSpeciesIdTable = gSpeciesInfo[species].formSpeciesIdTable; + formSpeciesIdTable = gSpeciesInfo[species].formSpeciesIdTable; for (i = 0; formSpeciesIdTable[i] != FORM_SPECIES_END; i++) { u32 formSpeciesId = formSpeciesIdTable[i]; @@ -23,13 +27,18 @@ TEST("Form change tables contain only forms in the form species ID table") { u32 i, j; u32 species = SPECIES_NONE; + const struct FormChange *formChangeTable; + const u16 *formSpeciesIdTable; for (i = 0; i < NUM_SPECIES; i++) { - if (gSpeciesInfo[i].formChangeTable) PARAMETRIZE { species = i; } + if (gSpeciesInfo[i].formChangeTable) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } } - const struct FormChange *formChangeTable = gSpeciesInfo[species].formChangeTable; - const u16 *formSpeciesIdTable = gSpeciesInfo[species].formSpeciesIdTable; + formChangeTable = gSpeciesInfo[species].formChangeTable; + formSpeciesIdTable = gSpeciesInfo[species].formSpeciesIdTable; EXPECT(formSpeciesIdTable); for (i = 0; formChangeTable[i].method != FORM_CHANGE_TERMINATOR; i++) @@ -51,12 +60,16 @@ TEST("Form change targets have the appropriate species flags") { u32 i; u32 species = SPECIES_NONE; + const struct FormChange *formChangeTable; for (i = 0; i < NUM_SPECIES; i++) { - if (gSpeciesInfo[i].formChangeTable) PARAMETRIZE { species = i; } + if (gSpeciesInfo[i].formChangeTable) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } } - const struct FormChange *formChangeTable = gSpeciesInfo[species].formChangeTable; + formChangeTable = gSpeciesInfo[species].formChangeTable; for (i = 0; formChangeTable[i].method != FORM_CHANGE_TERMINATOR; i++) { const struct SpeciesInfo *targetSpeciesInfo = &gSpeciesInfo[formChangeTable[i].targetSpecies]; From e42a0a6dbeb1b0db15ed14dd862ee7ed9c2f5e4f Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 6 Apr 2024 13:00:10 +0100 Subject: [PATCH 22/71] Narrower fonts --- charmap.txt | 3 + gflib/text.c | 331 ++++++++++++++++++++++-- gflib/text.h | 7 + gflib/window.c | 10 + gflib/window.h | 2 + graphics/fonts/latin_narrower.png | Bin 0 -> 7346 bytes graphics/fonts/latin_short_narrow.png | Bin 0 -> 7508 bytes graphics/fonts/latin_small_narrower.png | Bin 0 -> 7037 bytes graphics_file_rules.mk | 9 + include/fonts.h | 6 + include/list_menu.h | 5 +- src/fonts.c | 108 ++++++++ src/list_menu.c | 10 +- 13 files changed, 467 insertions(+), 24 deletions(-) create mode 100644 graphics/fonts/latin_narrower.png create mode 100644 graphics/fonts/latin_short_narrow.png create mode 100644 graphics/fonts/latin_small_narrower.png diff --git a/charmap.txt b/charmap.txt index 7271c621ca..872e9f76e4 100644 --- a/charmap.txt +++ b/charmap.txt @@ -464,6 +464,9 @@ FONT_NORMAL = FC 06 01 FONT_SHORT = FC 06 02 FONT_NARROW = FC 06 07 FONT_SMALL_NARROW = FC 06 08 +FONT_NARROWER = FC 06 0A +FONT_SMALL_NARROWER = FC 06 0B +FONT_SHORT_NARROW = FC 06 0C @ colors diff --git a/gflib/text.c b/gflib/text.c index cc6a6e90f2..c4ed5f4dbe 100644 --- a/gflib/text.c +++ b/gflib/text.c @@ -23,17 +23,26 @@ static u16 FontFunc_ShortCopy2(struct TextPrinter *); static u16 FontFunc_ShortCopy3(struct TextPrinter *); static u16 FontFunc_Narrow(struct TextPrinter *); static u16 FontFunc_SmallNarrow(struct TextPrinter *); +static u16 FontFunc_Narrower(struct TextPrinter *); +static u16 FontFunc_SmallNarrower(struct TextPrinter *); +static u16 FontFunc_ShortNarrow(struct TextPrinter *); static void DecompressGlyph_Small(u16, bool32); static void DecompressGlyph_Normal(u16, bool32); static void DecompressGlyph_Short(u16, bool32); static void DecompressGlyph_Narrow(u16, bool32); static void DecompressGlyph_SmallNarrow(u16, bool32); static void DecompressGlyph_Bold(u16); +static void DecompressGlyph_Narrower(u16, bool32); +static void DecompressGlyph_SmallNarrower(u16, bool32); +static void DecompressGlyph_ShortNarrow(u16, bool32); static u32 GetGlyphWidth_Small(u16, bool32); static u32 GetGlyphWidth_Normal(u16, bool32); static u32 GetGlyphWidth_Short(u16, bool32); static u32 GetGlyphWidth_Narrow(u16, bool32); static u32 GetGlyphWidth_SmallNarrow(u16, bool32); +static u32 GetGlyphWidth_Narrower(u16, bool32); +static u32 GetGlyphWidth_SmallNarrower(u16, bool32); +static u32 GetGlyphWidth_ShortNarrow(u16, bool32); static EWRAM_DATA struct TextPrinter sTempTextPrinter = {0}; static EWRAM_DATA struct TextPrinter sTextPrinters[WINDOWS_MAX] = {0}; @@ -81,15 +90,18 @@ static const u8 sWindowVerticalScrollSpeeds[] = { static const struct GlyphWidthFunc sGlyphWidthFuncs[] = { - { FONT_SMALL, GetGlyphWidth_Small }, - { FONT_NORMAL, GetGlyphWidth_Normal }, - { FONT_SHORT, GetGlyphWidth_Short }, - { FONT_SHORT_COPY_1, GetGlyphWidth_Short }, - { FONT_SHORT_COPY_2, GetGlyphWidth_Short }, - { FONT_SHORT_COPY_3, GetGlyphWidth_Short }, - { FONT_BRAILLE, GetGlyphWidth_Braille }, - { FONT_NARROW, GetGlyphWidth_Narrow }, - { FONT_SMALL_NARROW, GetGlyphWidth_SmallNarrow } + { FONT_SMALL, GetGlyphWidth_Small }, + { FONT_NORMAL, GetGlyphWidth_Normal }, + { FONT_SHORT, GetGlyphWidth_Short }, + { FONT_SHORT_COPY_1, GetGlyphWidth_Short }, + { FONT_SHORT_COPY_2, GetGlyphWidth_Short }, + { FONT_SHORT_COPY_3, GetGlyphWidth_Short }, + { FONT_BRAILLE, GetGlyphWidth_Braille }, + { FONT_NARROW, GetGlyphWidth_Narrow }, + { FONT_SMALL_NARROW, GetGlyphWidth_SmallNarrow }, + { FONT_NARROWER, GetGlyphWidth_Narrower }, + { FONT_SMALL_NARROWER, GetGlyphWidth_SmallNarrower }, + { FONT_SHORT_NARROW, GetGlyphWidth_ShortNarrow }, }; struct @@ -217,21 +229,54 @@ static const struct FontInfo sFontInfos[] = .fgColor = 1, .bgColor = 2, .shadowColor = 15, - } + }, + [FONT_NARROWER] = { + .fontFunction = FontFunc_Narrower, + .maxLetterWidth = 5, + .maxLetterHeight = 16, + .letterSpacing = 0, + .lineSpacing = 0, + .fgColor = 2, + .bgColor = 1, + .shadowColor = 3, + }, + [FONT_SMALL_NARROWER] = { + .fontFunction = FontFunc_SmallNarrower, + .maxLetterWidth = 5, + .maxLetterHeight = 8, + .letterSpacing = 0, + .lineSpacing = 0, + .fgColor = 2, + .bgColor = 1, + .shadowColor = 3, + }, + [FONT_SHORT_NARROW] = { + .fontFunction = FontFunc_ShortNarrow, + .maxLetterWidth = 5, + .maxLetterHeight = 14, + .letterSpacing = 0, + .lineSpacing = 0, + .fgColor = 2, + .bgColor = 1, + .shadowColor = 3, + }, }; static const u8 sMenuCursorDimensions[][2] = { - [FONT_SMALL] = { 8, 12 }, - [FONT_NORMAL] = { 8, 15 }, - [FONT_SHORT] = { 8, 14 }, - [FONT_SHORT_COPY_1] = { 8, 14 }, - [FONT_SHORT_COPY_2] = { 8, 14 }, - [FONT_SHORT_COPY_3] = { 8, 14 }, - [FONT_BRAILLE] = { 8, 16 }, - [FONT_NARROW] = { 8, 15 }, - [FONT_SMALL_NARROW] = { 8, 8 }, - [FONT_BOLD] = {} + [FONT_SMALL] = { 8, 12 }, + [FONT_NORMAL] = { 8, 15 }, + [FONT_SHORT] = { 8, 14 }, + [FONT_SHORT_COPY_1] = { 8, 14 }, + [FONT_SHORT_COPY_2] = { 8, 14 }, + [FONT_SHORT_COPY_3] = { 8, 14 }, + [FONT_BRAILLE] = { 8, 16 }, + [FONT_NARROW] = { 8, 15 }, + [FONT_SMALL_NARROW] = { 8, 8 }, + [FONT_BOLD] = {}, + [FONT_NARROWER] = { 8, 15 }, + [FONT_SMALL_NARROWER] = { 8, 8 }, + [FONT_SHORT_NARROW] = { 8, 14 }, }; static const u16 sFontBoldJapaneseGlyphs[] = INCBIN_U16("graphics/fonts/bold.hwjpnfont"); @@ -813,6 +858,42 @@ static u16 FontFunc_SmallNarrow(struct TextPrinter *textPrinter) return RenderText(textPrinter); } +static u16 FontFunc_Narrower(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasFontIdBeenSet == FALSE) + { + subStruct->fontId = FONT_NARROWER; + subStruct->hasFontIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +static u16 FontFunc_SmallNarrower(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasFontIdBeenSet == FALSE) + { + subStruct->fontId = FONT_SMALL_NARROWER; + subStruct->hasFontIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + +static u16 FontFunc_ShortNarrow(struct TextPrinter *textPrinter) +{ + struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); + + if (subStruct->hasFontIdBeenSet == FALSE) + { + subStruct->fontId = FONT_SHORT_NARROW; + subStruct->hasFontIdBeenSet = TRUE; + } + return RenderText(textPrinter); +} + void TextPrinterInitDownArrowCounters(struct TextPrinter *textPrinter) { struct TextPrinterSubStruct *subStruct = (struct TextPrinterSubStruct *)(&textPrinter->subStructFields); @@ -1250,6 +1331,15 @@ static u16 RenderText(struct TextPrinter *textPrinter) case FONT_SMALL_NARROW: DecompressGlyph_SmallNarrow(currChar, textPrinter->japanese); break; + case FONT_NARROWER: + DecompressGlyph_Narrower(currChar, textPrinter->japanese); + break; + case FONT_SMALL_NARROWER: + DecompressGlyph_SmallNarrower(currChar, textPrinter->japanese); + break; + case FONT_SHORT_NARROW: + DecompressGlyph_ShortNarrow(currChar, textPrinter->japanese); + break; case FONT_BRAILLE: break; } @@ -2012,3 +2102,204 @@ static void DecompressGlyph_Bold(u16 glyphId) gCurGlyph.width = 8; gCurGlyph.height = 12; } + +static void DecompressGlyph_Narrower(u16 glyphId, bool32 isJapanese) +{ + const u16 *glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFontNormalJapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId % 0x10)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 15; + } + else + { + glyphs = gFontNarrowerLatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFontNarrowerLatinGlyphWidths[glyphId]; + + if (gCurGlyph.width <= 8) + { + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom); + } + else + { + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8); + DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom); + DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8); + } + + gCurGlyph.height = 15; + } +} + +static u32 GetGlyphWidth_Narrower(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFontNarrowerLatinGlyphWidths[glyphId]; +} + +static void DecompressGlyph_SmallNarrower(u16 glyphId, bool32 isJapanese) +{ + const u16 *glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFontSmallJapaneseGlyphs + (0x100 * (glyphId >> 0x4)) + (0x8 * (glyphId % 0x10)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); + gCurGlyph.width = 8; + gCurGlyph.height = 15; + } + else + { + glyphs = gFontSmallNarrowerLatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFontSmallNarrowerLatinGlyphWidths[glyphId]; + + if (gCurGlyph.width <= 8) + { + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom); + } + else + { + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8); + DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom); + DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8); + } + + gCurGlyph.height = 15; + } +} + +static u32 GetGlyphWidth_SmallNarrower(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return 8; + else + return gFontSmallNarrowerLatinGlyphWidths[glyphId]; +} + +static void DecompressGlyph_ShortNarrow(u16 glyphId, bool32 isJapanese) +{ + const u16 *glyphs; + + if (isJapanese == TRUE) + { + glyphs = gFontShortJapaneseGlyphs + (0x100 * (glyphId >> 0x3)) + (0x10 * (glyphId & 0x7)); + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8); + DecompressGlyphTile(glyphs + 0x80, gCurGlyph.gfxBufferBottom); // gCurGlyph + 0x20 + DecompressGlyphTile(glyphs + 0x88, gCurGlyph.gfxBufferBottom + 8); // gCurGlyph + 0x60 + gCurGlyph.width = gFontShortJapaneseGlyphWidths[glyphId]; + gCurGlyph.height = 14; + } + else + { + glyphs = gFontShortNarrowLatinGlyphs + (0x20 * glyphId); + gCurGlyph.width = gFontShortNarrowLatinGlyphWidths[glyphId]; + + if (gCurGlyph.width <= 8) + { + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom); + } + else + { + DecompressGlyphTile(glyphs, gCurGlyph.gfxBufferTop); + DecompressGlyphTile(glyphs + 0x8, gCurGlyph.gfxBufferTop + 8); + DecompressGlyphTile(glyphs + 0x10, gCurGlyph.gfxBufferBottom); + DecompressGlyphTile(glyphs + 0x18, gCurGlyph.gfxBufferBottom + 8); + } + + gCurGlyph.height = 14; + } +} + +static u32 GetGlyphWidth_ShortNarrow(u16 glyphId, bool32 isJapanese) +{ + if (isJapanese == TRUE) + return gFontShortJapaneseGlyphWidths[glyphId]; + else + return gFontShortNarrowLatinGlyphWidths[glyphId]; +} + +static const s8 sNarrowerFontIds[] = +{ + [FONT_SMALL] = FONT_SMALL_NARROW, + [FONT_NORMAL] = FONT_NARROW, + [FONT_SHORT] = FONT_SHORT_NARROW, + [FONT_SHORT_COPY_1] = FONT_SHORT_NARROW, + [FONT_SHORT_COPY_2] = FONT_SHORT_NARROW, + [FONT_SHORT_COPY_3] = FONT_SHORT_NARROW, + [FONT_BRAILLE] = -1, + [FONT_NARROW] = FONT_NARROWER, + [FONT_SMALL_NARROW] = FONT_SMALL_NARROWER, + [FONT_BOLD] = -1, + [FONT_NARROWER] = -1, + [FONT_SMALL_NARROWER] = -1, + [FONT_SHORT_NARROW] = -1, +}; + +// If the narrowest font ID doesn't fit the text, we still return that +// ID because clipping is better than crashing. +u32 GetFontIdToFit(const u8 *string, u32 fontId, u32 letterSpacing, u32 widthPx) +{ + for (;;) + { + s32 narrowerFontId = sNarrowerFontIds[fontId]; + if (narrowerFontId == -1) + return fontId; + if (GetStringWidth(fontId, string, letterSpacing) <= widthPx) + return fontId; + fontId = narrowerFontId; + } +} + +u8 *PrependFontIdToFit(u8 *start, u8 *end, u32 fontId, u32 width) +{ + + u32 fitFontId = GetFontIdToFit(start, fontId, 0, width); + if (fitFontId != fontId) + { + memmove(&start[3], &start[0], end - start); + start[0] = EXT_CTRL_CODE_BEGIN; + start[1] = EXT_CTRL_CODE_FONT; + start[2] = fitFontId; + end[3] = EOS; + return end + 3; + } + else + { + return end; + } +} + +u8 *WrapFontIdToFit(u8 *start, u8 *end, u32 fontId, u32 width) +{ + + u32 fitFontId = GetFontIdToFit(start, fontId, 0, width); + if (fitFontId != fontId) + { + memmove(&start[3], &start[0], end - start); + start[0] = EXT_CTRL_CODE_BEGIN; + start[1] = EXT_CTRL_CODE_FONT; + start[2] = fitFontId; + end[3] = EXT_CTRL_CODE_BEGIN; + end[4] = EXT_CTRL_CODE_FONT; + end[5] = fontId; + end[6] = EOS; + return end + 6; + } + else + { + return end; + } +} diff --git a/gflib/text.h b/gflib/text.h index 7e1d7bfb11..0b12edc363 100644 --- a/gflib/text.h +++ b/gflib/text.h @@ -26,6 +26,9 @@ enum { FONT_NARROW, FONT_SMALL_NARROW, // Very similar to FONT_SMALL, some glyphs are narrower FONT_BOLD, // JP glyph set only + FONT_NARROWER, + FONT_SMALL_NARROWER, + FONT_SHORT_NARROW, }; // Return values for font functions @@ -190,4 +193,8 @@ u8 GetMenuCursorDimensionByFont(u8 fontId, u8 whichDimension); u16 FontFunc_Braille(struct TextPrinter *textPrinter); u32 GetGlyphWidth_Braille(u16 glyphId, bool32 isJapanese); +u32 GetFontIdToFit(const u8 *string, u32 widestFontId, u32 letterSpacing, u32 widthPx); +u8 *PrependFontIdToFit(u8 *start, u8 *end, u32 fontId, u32 width); +u8 *WrapFontIdToFit(u8 *start, u8 *end, u32 fontId, u32 width); + #endif // GUARD_TEXT_H diff --git a/gflib/window.c b/gflib/window.c index 61264000ea..5e77283945 100644 --- a/gflib/window.c +++ b/gflib/window.c @@ -712,3 +712,13 @@ static u32 GetNumActiveWindowsOnBg8Bit(u32 bgId) } return windowsNum; } + +u32 WindowWidthPx(u32 windowId) +{ + return gWindows[windowId].window.width * TILE_WIDTH; +} + +u32 WindowTemplateWidthPx(const struct WindowTemplate *template) +{ + return template->width * TILE_WIDTH; +} diff --git a/gflib/window.h b/gflib/window.h index 85e385cb9f..2fd0bddb20 100644 --- a/gflib/window.h +++ b/gflib/window.h @@ -73,6 +73,8 @@ void FillWindowPixelBuffer8Bit(u32 windowId, u8 fillValue); void FillWindowPixelRect8Bit(u32 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height); void BlitBitmapRectToWindow4BitTo8Bit(u32 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight, u8 paletteNum); void CopyWindowToVram8Bit(u32 windowId, u8 mode); +u32 WindowWidthPx(u32 windowId); +u32 WindowTemplateWidthPx(const struct WindowTemplate *template); extern struct Window gWindows[]; extern void *gWindowBgTilemapBuffers[]; diff --git a/graphics/fonts/latin_narrower.png b/graphics/fonts/latin_narrower.png new file mode 100644 index 0000000000000000000000000000000000000000..22847ef09953dda2644214d2619277f4090324b2 GIT binary patch literal 7346 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 literal 0 HcmV?d00001 diff --git a/graphics/fonts/latin_short_narrow.png b/graphics/fonts/latin_short_narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..cf48712719ace38225213bdf1b896ac07e7f9e19 GIT binary patch literal 7508 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 literal 0 HcmV?d00001 diff --git a/graphics_file_rules.mk b/graphics_file_rules.mk index 92b8cb0929..9f5d9d6c79 100644 --- a/graphics_file_rules.mk +++ b/graphics_file_rules.mk @@ -238,6 +238,15 @@ $(FONTGFXDIR)/narrow.latfont: $(FONTGFXDIR)/latin_narrow.png $(FONTGFXDIR)/small_narrow.latfont: $(FONTGFXDIR)/latin_small_narrow.png $(GFX) $< $@ +$(FONTGFXDIR)/narrower.latfont: $(FONTGFXDIR)/latin_narrower.png + $(GFX) $< $@ + +$(FONTGFXDIR)/small_narrower.latfont: $(FONTGFXDIR)/latin_small_narrower.png + $(GFX) $< $@ + +$(FONTGFXDIR)/short_narrow.latfont: $(FONTGFXDIR)/latin_short_narrow.png + $(GFX) $< $@ + $(FONTGFXDIR)/small.hwjpnfont: $(FONTGFXDIR)/japanese_small.png $(GFX) $< $@ diff --git a/include/fonts.h b/include/fonts.h index c21c75942d..a6be35db98 100644 --- a/include/fonts.h +++ b/include/fonts.h @@ -15,5 +15,11 @@ extern const u16 gFontNarrowLatinGlyphs[]; extern const u8 gFontNarrowLatinGlyphWidths[]; extern const u16 gFontSmallNarrowLatinGlyphs[]; extern const u8 gFontSmallNarrowLatinGlyphWidths[]; +extern const u8 gFontNarrowerLatinGlyphWidths[]; +extern const u16 gFontNarrowerLatinGlyphs[]; +extern const u8 gFontSmallNarrowerLatinGlyphWidths[]; +extern const u16 gFontSmallNarrowerLatinGlyphs[]; +extern const u8 gFontShortNarrowLatinGlyphWidths[]; +extern const u16 gFontShortNarrowLatinGlyphs[]; #endif // GUARD_FONTS_H diff --git a/include/list_menu.h b/include/list_menu.h index 23caaf4b4c..1e6c0f95fa 100644 --- a/include/list_menu.h +++ b/include/list_menu.h @@ -40,8 +40,9 @@ struct ListMenuTemplate const struct ListMenuItem *items; void (* moveCursorFunc)(s32 itemIndex, bool8 onInit, struct ListMenu *list); void (* itemPrintFunc)(u8 windowId, u32 itemId, u8 y); - u16 totalItems; - u16 maxShowed; + u16 totalItems:12; + u16 maxShowed:12; + u16 textNarrowWidth:8; u8 windowId; u8 header_X; u8 item_X; diff --git a/src/fonts.c b/src/fonts.c index 72de054b73..92600fcf7e 100644 --- a/src/fonts.c +++ b/src/fonts.c @@ -180,6 +180,114 @@ ALIGNED(4) const u8 gFontNormalLatinGlyphWidths[] = { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 3, }; +ALIGNED(4) const u16 gFontNarrowerLatinGlyphs[] = INCBIN_U16("graphics/fonts/narrower.latfont"); +ALIGNED(4) const u8 gFontNarrowerLatinGlyphWidths[] = { + 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, + 8, 4, 4, 4, 5, 5, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, + 4, 4, 4, 4, 4, 6, 4, 4, 4, 5, 4, 5, 8, 6, 6, 3, + 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 5, 5, 4, 8, 8, 8, 7, 8, 8, 4, 4, 6, 4, 4, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, + 3, 3, 3, 3, 3, 3, 3, 5, 3, 7, 7, 7, 7, 0, 0, 3, + 4, 5, 6, 7, 4, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 3, 5, 3, + 5, 5, 5, 3, 3, 5, 5, 6, 3, 6, 6, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 4, 4, 4, 4, 4, + 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, + 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, + 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 10, 10, 10, 10, 8, 8, 10, 8, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 3, +}; + +ALIGNED(4) const u16 gFontSmallNarrowerLatinGlyphs[] = INCBIN_U16("graphics/fonts/small_narrower.latfont"); +ALIGNED(4) const u8 gFontSmallNarrowerLatinGlyphWidths[] = { + 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, + 5, 4, 4, 4, 5, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, + 4, 4, 4, 4, 4, 6, 4, 4, 4, 5, 4, 4, 7, 5, 6, 3, + 3, 3, 3, 3, 8, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 5, 4, 3, 7, 7, 7, 8, 8, 8, 8, 4, 5, 4, 4, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, + 3, 3, 3, 3, 3, 3, 3, 5, 3, 8, 8, 8, 8, 0, 0, 3, + 4, 5, 6, 7, 4, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 3, 4, 4, + 5, 5, 5, 3, 3, 5, 5, 5, 4, 5, 5, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 3, 4, + 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, + 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 8, 8, 8, 8, 8, 7, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 3, +}; + +ALIGNED(4) const u16 gFontShortNarrowLatinGlyphs[] = INCBIN_U16("graphics/fonts/short_narrow.latfont"); +ALIGNED(4) const u8 gFontShortNarrowLatinGlyphWidths[] = { + 3, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3, 4, 4, 5, 5, 5, + 8, 5, 5, 5, 5, 6, 5, 5, 3, 5, 5, 5, 5, 5, 4, 3, + 4, 4, 5, 5, 5, 8, 5, 5, 5, 5, 6, 6, 9, 6, 6, 3, + 3, 3, 3, 3, 10, 8, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 6, 6, 6, 8, 8, 8, 8, 8, 8, 4, 6, 8, 5, 5, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 6, + 3, 3, 3, 3, 3, 3, 3, 6, 3, 12, 12, 12, 12, 0, 0, 3, + 4, 5, 6, 7, 8, 8, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, + 6, 6, 6, 3, 3, 6, 6, 8, 5, 9, 6, 5, 5, 5, 5, 5, + 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, + 5, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, + 4, 6, 5, 5, 5, 5, 5, 5, 4, 5, 5, 6, 4, 5, 5, 8, + 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 12, 12, 12, 12, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 3, +}; + ALIGNED(4) const u16 gFontSmallJapaneseGlyphs[] = INCBIN_U16("graphics/fonts/small.hwjpnfont"); ALIGNED(4) const u16 gFontNormalJapaneseGlyphs[] = INCBIN_U16("graphics/fonts/normal.hwjpnfont"); diff --git a/src/list_menu.c b/src/list_menu.c index e83f325161..aa65683edd 100644 --- a/src/list_menu.c +++ b/src/list_menu.c @@ -600,11 +600,14 @@ static void ListMenuPrint(struct ListMenu *list, const u8 *str, u8 x, u8 y) u8 colors[3]; if (gListMenuOverride.enabled) { + u32 fontId = gListMenuOverride.fontId; + if (list->template.textNarrowWidth) + fontId = GetFontIdToFit(str, fontId, gListMenuOverride.lettersSpacing, list->template.textNarrowWidth); colors[0] = gListMenuOverride.fillValue; colors[1] = gListMenuOverride.cursorPal; colors[2] = gListMenuOverride.cursorShadowPal; AddTextPrinterParameterized4(list->template.windowId, - gListMenuOverride.fontId, + fontId, x, y, gListMenuOverride.lettersSpacing, 0, colors, TEXT_SKIP_DRAW, str); @@ -613,11 +616,14 @@ static void ListMenuPrint(struct ListMenu *list, const u8 *str, u8 x, u8 y) } else { + u32 fontId = list->template.fontId; + if (list->template.textNarrowWidth) + fontId = GetFontIdToFit(str, fontId, list->template.lettersSpacing, list->template.textNarrowWidth); colors[0] = list->template.fillValue; colors[1] = list->template.cursorPal; colors[2] = list->template.cursorShadowPal; AddTextPrinterParameterized4(list->template.windowId, - list->template.fontId, + fontId, x, y, list->template.lettersSpacing, 0, colors, TEXT_SKIP_DRAW, str); From 738fade50a3c33917d169cdd34eba722cd72724f Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 6 Apr 2024 13:38:52 +0100 Subject: [PATCH 23/71] Fix expanded move names --- src/data/moves_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 911f2b6b82..c569fb91f2 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -14160,7 +14160,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = [MOVE_HYPERSPACE_HOLE] = { - .name = HANDLE_EXPANDED_MOVE_NAME("HyprspceHole", "Hyprspace Hole"), + .name = HANDLE_EXPANDED_MOVE_NAME("HyprspceHole", "Hyperspace Hole"), .description = sHyperspaceHoleDescription, .effect = EFFECT_HIT, .power = 80, From a1d24603720d30d578260c34e11627acd68ac821 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 6 Apr 2024 13:00:30 +0100 Subject: [PATCH 24/71] Enable GF move names by default In contests, even FONT_NARROWER isn't sufficient to prevent clipping in all cases. e.g. Stomping Tantrum clips. We have decided to accept that cost to make the rest of the user experience better, but downstream projects that don't like that trade-off can either a) alter the contest UI, or b) set B_EXPANDED_MOVE_NAMES to FALSE. --- include/config/battle.h | 2 +- include/config/test.h | 7 ++ include/constants/global.h | 4 + src/battle_message.c | 8 ++ src/contest.c | 14 +++- src/item_menu.c | 11 ++- src/menu_specialized.c | 3 +- src/pokemon_summary_screen.c | 23 ++++-- test/battle/ability/wind_power.c | 4 +- test/battle/damage_formula.c | 2 +- test/battle/gimmick/dynamax.c | 4 +- test/battle/gimmick/terastal.c | 8 -- test/battle/item_effect/increase_stat.c | 8 +- test/battle/move_effect/corrosive_gas.c | 10 +-- test/battle/move_effect/fling.c | 2 +- test/battle/move_effect/metronome.c | 2 +- test/battle/move_effect/psychic_noise.c | 2 +- test/battle/move_effect/reflect_type.c | 2 +- test/battle/move_effect/revival_blessing.c | 18 ++--- .../move_effect/semi_invulnerable_moves.c | 8 +- test/battle/terrain/electric.c | 4 +- test/battle/terrain/misty.c | 2 +- test/battle/terrain/psychic.c | 12 +-- test/text.c | 77 +++++++++++++++++++ 24 files changed, 173 insertions(+), 64 deletions(-) create mode 100644 include/config/test.h create mode 100644 test/text.c diff --git a/include/config/battle.h b/include/config/battle.h index c47f4fedcf..f2c17f91f6 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -214,7 +214,7 @@ #define B_SHOW_TARGETS TRUE // If set to TRUE, all available targets, for moves hitting 2 or 3 Pokémon, will be shown before selecting a move. #define B_SHOW_CATEGORY_ICON TRUE // If set to TRUE, it will show an icon in the summary showing the move's category. #define B_HIDE_HEALTHBOX_IN_ANIMS TRUE // If set to TRUE, hides healthboxes during move animations. -#define B_EXPANDED_MOVE_NAMES FALSE // If set to TRUE, move names are increased from 12 characters to 16 characters. +#define B_EXPANDED_MOVE_NAMES TRUE // If set to FALSE, move names are decreased from 16 characters to 12 characters. #define B_WAIT_TIME_MULTIPLIER 16 // This determines how long text pauses in battle last. Vanilla is 16. Lower values result in faster battles. #define B_QUICK_MOVE_CURSOR_TO_RUN FALSE // If set to TRUE, pushing B in the battle options against a wild encounter will move the cursor to the run option diff --git a/include/config/test.h b/include/config/test.h new file mode 100644 index 0000000000..9d8066864a --- /dev/null +++ b/include/config/test.h @@ -0,0 +1,7 @@ +#ifndef GUARD_CONFIG_TEST_H +#define GUARD_CONFIG_TEST_H + +#undef B_EXPANDED_MOVE_NAMES +#define B_EXPANDED_MOVE_NAMES TRUE + +#endif // GUARD_CONFIG_TEST_H diff --git a/include/constants/global.h b/include/constants/global.h index b8ac7559a8..daf1c9edb4 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -170,4 +170,8 @@ #define CONNECTION_DIVE 5 #define CONNECTION_EMERGE 6 +#if TESTING +#include "config/test.h" +#endif + #endif // GUARD_CONSTANTS_GLOBAL_H diff --git a/src/battle_message.c b/src/battle_message.c index 5f7e6ca052..b713d846ad 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3949,6 +3949,14 @@ void BattlePutTextOnWindow(const u8 *text, u8 windowId) printerTemplate.bgColor = textInfo[windowId].bgColor; printerTemplate.shadowColor = textInfo[windowId].shadowColor; + if (B_WIN_MOVE_NAME_1 <= windowId && windowId <= B_WIN_MOVE_NAME_4) + { + // We cannot check the actual width of the window because + // B_WIN_MOVE_NAME_1 and B_WIN_MOVE_NAME_3 are 16 wide for + // Z-move details. + printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 8 * TILE_WIDTH); + } + if (printerTemplate.x == 0xFF) { u32 width = GetBattleWindowTemplatePixelWidth(gBattleScripting.windowsType, windowId); diff --git a/src/contest.c b/src/contest.c index f0d0d527ed..c500fda60d 100644 --- a/src/contest.c +++ b/src/contest.c @@ -1085,6 +1085,12 @@ static const s8 sContestExcitementTable[CONTEST_CATEGORIES_COUNT][CONTEST_CATEGO } }; +static void CopyMoveNameToFit(u8 *dest, u32 move) +{ + u8 *end = StringCopy(dest, GetMoveName(move)); + WrapFontIdToFit(dest, end, FONT_NORMAL, 84); +} + static void TaskDummy1(u8 taskId) { } @@ -1636,7 +1642,7 @@ static void Task_ShowMoveSelectScreen(u8 taskId) moveNameBuffer = StringCopy(moveNameBuffer, GetMoveName(move)); FillWindowPixelBuffer(i + MOVE_WINDOWS_START, PIXEL_FILL(0)); - Contest_PrintTextToBg0WindowAt(i + MOVE_WINDOWS_START, moveName, 5, 1, FONT_NARROW); + Contest_PrintTextToBg0WindowAt(i + MOVE_WINDOWS_START, moveName, 5, 1, GetFontIdToFit(moveName, FONT_NARROW, 0, WindowWidthPx(i + MOVE_WINDOWS_START) - 11)); } DrawMoveSelectArrow(eContest.playerMoveChoice); @@ -2295,7 +2301,7 @@ static void Task_DoAppeals(u8 taskId) if (eContestantStatus[contestant].overrideCategoryExcitementMod) { r3 = 1; - StringCopy(gStringVar3, GetMoveName(eContestantStatus[contestant].currMove)); + CopyMoveNameToFit(gStringVar3, eContestantStatus[contestant].currMove); } else { @@ -2430,7 +2436,7 @@ static void Task_DoAppeals(u8 taskId) ContestClearGeneralTextWindow(); StringCopy(gStringVar3, gContestMons[eContestExcitement.freezer].nickname); StringCopy(gStringVar1, gContestMons[contestant].nickname); - StringCopy(gStringVar2, GetMoveName(eContestantStatus[contestant].currMove)); + CopyMoveNameToFit(gStringVar2, eContestantStatus[contestant].currMove); StringExpandPlaceholders(gStringVar4, gText_CrowdContinuesToWatchMon); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_PRINT_MON_MOVE_IGNORED_MSG; @@ -2456,7 +2462,7 @@ static void Task_DoAppeals(u8 taskId) eContestantStatus[contestant].hasJudgesAttention = FALSE; StartStopFlashJudgeAttentionEye(contestant); StringCopy(gStringVar1, gContestMons[contestant].nickname); - StringCopy(gStringVar2, GetMoveName(eContestantStatus[contestant].currMove)); + CopyMoveNameToFit(gStringVar2, eContestantStatus[contestant].currMove); StringExpandPlaceholders(gStringVar4, gText_MonWasTooNervousToMove); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_WAIT_TOO_NERVOUS_MSG; diff --git a/src/item_menu.c b/src/item_menu.c index 569712cfe6..dde142d021 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -109,7 +109,7 @@ struct ListBuffer1 { }; struct ListBuffer2 { - u8 name[MAX_POCKET_ITEMS][ITEM_NAME_LENGTH + 10]; + u8 name[MAX_POCKET_ITEMS][max(ITEM_NAME_LENGTH, MOVE_NAME_LENGTH + 3) + 10]; }; struct TempWallyBag { @@ -905,10 +905,19 @@ static void LoadBagItemListBuffers(u8 pocketId) static void GetItemName(u8 *dest, u16 itemId) { + u32 fontId; switch (gBagPosition.pocket) { case TMHM_POCKET: StringCopy(gStringVar2, GetMoveName(ItemIdToBattleMoveId(itemId))); + fontId = GetFontIdToFit(gStringVar2, FONT_NARROW, 0, 73); + if (fontId != FONT_NARROW) + { + gStringVar2[0] = EXT_CTRL_CODE_BEGIN; + gStringVar2[1] = EXT_CTRL_CODE_FONT; + gStringVar2[2] = fontId; + StringCopy(&gStringVar2[3], GetMoveName(ItemIdToBattleMoveId(itemId))); + } if (itemId >= ITEM_HM01) { // Get HM number diff --git a/src/menu_specialized.c b/src/menu_specialized.c index fcb65d9ae7..a80a227a4b 100644 --- a/src/menu_specialized.c +++ b/src/menu_specialized.c @@ -186,7 +186,8 @@ static const struct ListMenuTemplate sMoveRelearnerMovesListTemplate = .itemVerticalPadding = 0, .scrollMultiple = LIST_NO_MULTIPLE_SCROLL, .fontId = FONT_NORMAL, - .cursorKind = CURSOR_BLACK_ARROW + .cursorKind = CURSOR_BLACK_ARROW, + .textNarrowWidth = 68, }; //-------------- diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 6e63ce5eab..ee2d0e9853 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2800,12 +2800,23 @@ static void ResetWindows(void) sMonSummaryScreen->windowIds[i] = WINDOW_NONE; } -static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) +static void PrintTextOnWindowWithFont(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId, u32 fontId) { if (DECAP_ENABLED && DECAP_MIRRORING && !DECAP_SUMMARY) - AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, MirrorPtr(string)); + AddTextPrinterParameterized4(windowId, fontId, x, y, 0, lineSpacing, sTextColors[colorId], 0, MirrorPtr(string)); else - AddTextPrinterParameterized4(windowId, FONT_NORMAL, x, y, 0, lineSpacing, sTextColors[colorId], 0, string); + AddTextPrinterParameterized4(windowId, fontId, x, y, 0, lineSpacing, sTextColors[colorId], 0, string); +} + +static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) +{ + PrintTextOnWindowWithFont(windowId, string, x, y, lineSpacing, colorId, FONT_NORMAL); +} + +static void PrintTextOnWindowToFit(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) +{ + u32 fontId = GetFontIdToFit(string, FONT_NORMAL, 0, WindowWidthPx(windowId)); + PrintTextOnWindowWithFont(windowId, string, x, y, lineSpacing, colorId, fontId); } static void PrintMonInfo(void) @@ -3626,7 +3637,7 @@ static void PrintMoveNameAndPP(u8 moveIndex) if (move != 0) { pp = CalculatePPWithBonus(move, summary->ppBonuses, moveIndex); - PrintTextOnWindow(moveNameWindowId, GetMoveName(move), 0, moveIndex * 16 + 1, 0, 1); + PrintTextOnWindowToFit(moveNameWindowId, GetMoveName(move), 0, moveIndex * 16 + 1, 0, 1); ConvertIntToDecimalStringN(gStringVar1, summary->pp[moveIndex], STR_CONV_MODE_RIGHT_ALIGN, 2); ConvertIntToDecimalStringN(gStringVar2, pp, STR_CONV_MODE_RIGHT_ALIGN, 2); DynamicPlaceholderTextUtil_Reset(); @@ -3794,9 +3805,9 @@ static void PrintNewMoveDetailsOrCancelText(void) u16 move = sMonSummaryScreen->newMove; if (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES) - PrintTextOnWindow(windowId1, GetMoveName(move), 0, 65, 0, 6); + PrintTextOnWindowToFit(windowId1, GetMoveName(move), 0, 65, 0, 6); else - PrintTextOnWindow(windowId1, GetMoveName(move), 0, 65, 0, 5); + PrintTextOnWindowToFit(windowId1, GetMoveName(move), 0, 65, 0, 5); ConvertIntToDecimalStringN(gStringVar1, gMovesInfo[move].pp, STR_CONV_MODE_RIGHT_ALIGN, 2); DynamicPlaceholderTextUtil_Reset(); diff --git a/test/battle/ability/wind_power.c b/test/battle/ability/wind_power.c index bbf76cd6fb..146c47f3dd 100644 --- a/test/battle/ability/wind_power.c +++ b/test/battle/ability/wind_power.c @@ -167,12 +167,12 @@ DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ab HP_BAR(playerLeft); if (abilityLeft == ABILITY_WIND_POWER) { ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER); - MESSAGE("Being hit by PetalBlizzrd charged Wattrel with power!"); + MESSAGE("Being hit by Petal Blizzard charged Wattrel with power!"); } HP_BAR(playerRight); if (abilityRight == ABILITY_WIND_POWER) { ABILITY_POPUP(playerRight, ABILITY_WIND_POWER); - MESSAGE("Being hit by PetalBlizzrd charged Wattrel with power!"); + MESSAGE("Being hit by Petal Blizzard charged Wattrel with power!"); } HP_BAR(opponentRight); NOT HP_BAR(opponentLeft); diff --git a/test/battle/damage_formula.c b/test/battle/damage_formula.c index 2fdb9bed0f..d89fb7e7fd 100644 --- a/test/battle/damage_formula.c +++ b/test/battle/damage_formula.c @@ -109,7 +109,7 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Marshadow vs Mawile)") } } SCENE{ - MESSAGE("Marshadow used SpectrlThief!"); + MESSAGE("Marshadow used Spectral Thief!"); HP_BAR(opponent, captureDamage: &dmg); } THEN{ diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 2db55d87b6..42e48c9e9c 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -834,9 +834,9 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Mindstorm sets up Psychic Terrain") TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_PSYCHIC, dynamax: TRUE); } TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_PSYCHIC); } } SCENE { - MESSAGE("Foe Wobbuffet used ExtremeSpeed!"); + MESSAGE("Foe Wobbuffet used Extreme Speed!"); MESSAGE("Wobbuffet used Max Mindstorm!"); - MESSAGE("Foe Wobbuffet cannot use ExtremeSpeed!"); + MESSAGE("Foe Wobbuffet cannot use Extreme Speed!"); MESSAGE("Wobbuffet used Max Mindstorm!"); } } diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 3166e2d67c..18eab992af 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -422,11 +422,7 @@ SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Terastallized Pokemon's Tera } WHEN { TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); } } SCENE { - #if B_EXPANDED_MOVE_NAMES == TRUE MESSAGE("Oricorio used Revelation Dance!"); - #else - MESSAGE("Oricorio used RvlationDnce!"); - #endif MESSAGE("It doesn't affect Foe Gengar…"); NOT { HP_BAR(opponent); } } @@ -547,11 +543,7 @@ SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base t } WHEN { TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); } } SCENE { - #if B_EXPANDED_MOVE_NAMES == TRUE MESSAGE("Oricorio used Revelation Dance!"); - #else - MESSAGE("Oricorio used RvlationDnce!"); - #endif MESSAGE("It doesn't affect Foe Gumshoos…"); NOT { HP_BAR(opponent); } } diff --git a/test/battle/item_effect/increase_stat.c b/test/battle/item_effect/increase_stat.c index df36b8ff33..4eba548961 100644 --- a/test/battle/item_effect/increase_stat.c +++ b/test/battle/item_effect/increase_stat.c @@ -63,7 +63,7 @@ SINGLE_BATTLE_TEST("X Sp. Atk sharply raises battler's Sp. Attack stat", s16 dam if (useItem) TURN { USE_ITEM(player, ITEM_X_SP_ATK); } TURN { MOVE(player, MOVE_DISARMING_VOICE); } } SCENE { - MESSAGE("Wobbuffet used DisrmngVoice!"); + MESSAGE("Wobbuffet used Disarming Voice!"); HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { if (B_X_ITEMS_BUFF >= GEN_7) @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("X Sp. Def sharply raises battler's Sp. Defense stat", s16 da if (useItem) TURN { USE_ITEM(player, ITEM_X_SP_DEF); } TURN { MOVE(opponent, MOVE_DISARMING_VOICE); } } SCENE { - MESSAGE("Foe Wobbuffet used DisrmngVoice!"); + MESSAGE("Foe Wobbuffet used Disarming Voice!"); HP_BAR(player, captureDamage: &results[i].damage); } FINALLY { if (B_X_ITEMS_BUFF >= GEN_7) @@ -205,7 +205,7 @@ SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Sp. Attack stat", s16 damage) if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } TURN { MOVE(player, MOVE_DISARMING_VOICE); } } SCENE { - MESSAGE("Wobbuffet used DisrmngVoice!"); + MESSAGE("Wobbuffet used Disarming Voice!"); HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); @@ -225,7 +225,7 @@ SINGLE_BATTLE_TEST("Max Mushrooms battler's Sp. Defense stat", s16 damage) if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } TURN { MOVE(opponent, MOVE_DISARMING_VOICE); } } SCENE { - MESSAGE("Foe Wobbuffet used DisrmngVoice!"); + MESSAGE("Foe Wobbuffet used Disarming Voice!"); HP_BAR(player, captureDamage: &results[i].damage); } FINALLY { EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage); diff --git a/test/battle/move_effect/corrosive_gas.c b/test/battle/move_effect/corrosive_gas.c index c2c921cf74..714702703a 100644 --- a/test/battle/move_effect/corrosive_gas.c +++ b/test/battle/move_effect/corrosive_gas.c @@ -19,7 +19,7 @@ SINGLE_BATTLE_TEST("Corrosive Gas destroys the target's item or fails if the tar } WHEN { TURN { MOVE(player, MOVE_CORROSIVE_GAS); } } SCENE { - MESSAGE("Wobbuffet used CorrosiveGas!"); + MESSAGE("Wobbuffet used Corrosive Gas!"); if (item == ITEM_POTION) { ANIMATION(ANIM_TYPE_MOVE, MOVE_CORROSIVE_GAS, player); MESSAGE("Wobbuffet corroded Foe Wobbuffet's Potion!"); @@ -40,11 +40,11 @@ SINGLE_BATTLE_TEST("Corrosive Gas doesn't destroy the item of a Pokemon with the } WHEN { TURN { MOVE(player, MOVE_CORROSIVE_GAS); } } SCENE { - MESSAGE("Wobbuffet used CorrosiveGas!"); + MESSAGE("Wobbuffet used Corrosive Gas!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_CORROSIVE_GAS, player); NOT MESSAGE("Wobbuffet corroded Foe Wobbuffet's Potion!"); ABILITY_POPUP(opponent, ABILITY_STICKY_HOLD); - MESSAGE("Foe Muk's Sticky Hold made CorrosiveGas ineffective!"); + MESSAGE("Foe Muk's Sticky Hold made Corrosive Gas ineffective!"); } THEN { EXPECT_EQ(opponent->item, ITEM_POISON_BARB); } @@ -59,7 +59,7 @@ SINGLE_BATTLE_TEST("Items lost to Corrosive Gas cannot be restored by Recycle") } WHEN { TURN { MOVE(player, MOVE_CORROSIVE_GAS); MOVE(opponent, MOVE_RECYCLE); } } SCENE { - MESSAGE("Wobbuffet used CorrosiveGas!"); + MESSAGE("Wobbuffet used Corrosive Gas!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_CORROSIVE_GAS, player); MESSAGE("Wobbuffet corroded Foe Wobbuffet's Oran Berry!"); MESSAGE("Foe Wobbuffet used Recycle!"); @@ -93,7 +93,7 @@ DOUBLE_BATTLE_TEST("Corrosive Gas destroys foes and ally's items if they have on } WHEN { TURN { MOVE(playerRight, MOVE_CORROSIVE_GAS); } } SCENE { - MESSAGE("Wynaut used CorrosiveGas!"); + MESSAGE("Wynaut used Corrosive Gas!"); if (itemPlayerLeft == ITEM_CHERI_BERRY) { MESSAGE("Wynaut corroded Wobbuffet's Cheri Berry!"); } else { diff --git a/test/battle/move_effect/fling.c b/test/battle/move_effect/fling.c index f6b83215a1..b4b25484b3 100644 --- a/test/battle/move_effect/fling.c +++ b/test/battle/move_effect/fling.c @@ -114,7 +114,7 @@ SINGLE_BATTLE_TEST("Fling - Item is lost even when there is no target") TURN { MOVE(opponent, MOVE_SELF_DESTRUCT); MOVE(player, MOVE_FLING); SEND_OUT(opponent, 1); } TURN { MOVE(player, MOVE_FLING); } } SCENE { - MESSAGE("Foe Wobbuffet used SelfDestruct!"); + MESSAGE("Foe Wobbuffet used Self-Destruct!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SELF_DESTRUCT, opponent); HP_BAR(player); MESSAGE("Foe Wobbuffet fainted!"); diff --git a/test/battle/move_effect/metronome.c b/test/battle/move_effect/metronome.c index d4efcaafff..22e3390cb2 100644 --- a/test/battle/move_effect/metronome.c +++ b/test/battle/move_effect/metronome.c @@ -35,7 +35,7 @@ SINGLE_BATTLE_TEST("Metronome's called powder move fails against Grass Types") } SCENE { MESSAGE("Wobbuffet used Metronome!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player); - MESSAGE("Wobbuffet used PoisonPowder!"); + MESSAGE("Wobbuffet used Poison Powder!"); NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_POWDER, player); MESSAGE("It doesn't affect Foe Tangela…"); NOT STATUS_ICON(opponent, poison: TRUE); diff --git a/test/battle/move_effect/psychic_noise.c b/test/battle/move_effect/psychic_noise.c index 7846157b77..e8d50dc961 100644 --- a/test/battle/move_effect/psychic_noise.c +++ b/test/battle/move_effect/psychic_noise.c @@ -35,7 +35,7 @@ SINGLE_BATTLE_TEST("Psychic Noise is blocked by Soundproof") TURN { MOVE(player, MOVE_PSYCHIC_NOISE); MOVE(opponent, MOVE_RECOVER); } } SCENE { ABILITY_POPUP(opponent, ABILITY_SOUNDPROOF); - MESSAGE("Foe Voltorb's Soundproof blocks PsychicNoise!"); + MESSAGE("Foe Voltorb's Soundproof blocks Psychic Noise!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_RECOVER, opponent); } } diff --git a/test/battle/move_effect/reflect_type.c b/test/battle/move_effect/reflect_type.c index e873516034..b75ffc0b0d 100644 --- a/test/battle/move_effect/reflect_type.c +++ b/test/battle/move_effect/reflect_type.c @@ -171,7 +171,7 @@ SINGLE_BATTLE_TEST("Reflect Type defaults to Normal type for the user's type1 an HP_BAR(player); MESSAGE("Foe Arcanine burned itself out!"); // Turn 2 - MESSAGE("Wobbuffet used Forest'sCurs!"); + MESSAGE("Wobbuffet used Forest's Curse!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESTS_CURSE, player); MESSAGE("Grass type was added to Foe Arcanine!"); // Turn 3 diff --git a/test/battle/move_effect/revival_blessing.c b/test/battle/move_effect/revival_blessing.c index 397e01e73a..d44e9110d5 100644 --- a/test/battle/move_effect/revival_blessing.c +++ b/test/battle/move_effect/revival_blessing.c @@ -5,12 +5,6 @@ // behaviors. These have been tested in-game, in double, in multi, and in link battles. AI will always // revive their first fainted party member in order. -#if B_EXPANDED_MOVE_NAMES -#define REVIVAL_BLESSING "Revival Blessing" -#else -#define REVIVAL_BLESSING "RevivlBlesng" -#endif - ASSUMPTIONS { ASSUME(gMovesInfo[MOVE_REVIVAL_BLESSING].effect == EFFECT_REVIVAL_BLESSING); @@ -26,7 +20,7 @@ SINGLE_BATTLE_TEST("Revival Blessing revives a chosen fainted party member for t } WHEN { TURN { MOVE(player, MOVE_REVIVAL_BLESSING); SEND_OUT(player, 2); } } SCENE { - MESSAGE("Wobbuffet used " REVIVAL_BLESSING "!"); + MESSAGE("Wobbuffet used Revival Blessing!"); MESSAGE("Wynaut was revived and is ready to fight again!"); } } @@ -41,7 +35,7 @@ SINGLE_BATTLE_TEST("Revival Blessing revives a fainted party member for an oppon } WHEN { TURN { MOVE(opponent, MOVE_REVIVAL_BLESSING); SEND_OUT(opponent, 1); } } SCENE { - MESSAGE("Foe Raichu used " REVIVAL_BLESSING "!"); + MESSAGE("Foe Raichu used Revival Blessing!"); MESSAGE("Pichu was revived and is ready to fight again!"); } } @@ -54,7 +48,7 @@ SINGLE_BATTLE_TEST("Revival Blessing fails if no party members are fainted") } WHEN { TURN { MOVE(player, MOVE_REVIVAL_BLESSING); } } SCENE { - MESSAGE("Wobbuffet used " REVIVAL_BLESSING "!"); + MESSAGE("Wobbuffet used Revival Blessing!"); MESSAGE("But it failed!"); } } @@ -82,10 +76,10 @@ TO_DO_BATTLE_TEST("Revival Blessing cannot revive a partner's party member"); // TURN { MOVE(user, MOVE_REVIVAL_BLESSING); } // } SCENE { // if (user == opponentLeft) { -// MESSAGE("Foe Wobbuffet used " REVIVAL_BLESSING "!"); +// MESSAGE("Foe Wobbuffet used Revival Blessing!"); // MESSAGE("But it failed!"); // } else { -// MESSAGE("Foe Wynaut used " REVIVAL_BLESSING "!"); +// MESSAGE("Foe Wynaut used Revival Blessing!"); // MESSAGE("Wynaut was revived and is ready to fight again!"); // } // } @@ -108,7 +102,7 @@ TO_DO_BATTLE_TEST("Revived battlers still lose their turn"); // } SCENE { // MESSAGE("Wobbuffet used Tackle!"); // MESSAGE("Foe Wynaut fainted!"); -// MESSAGE("Foe Wobbuffet used " REVIVAL_BLESSING "!"); +// MESSAGE("Foe Wobbuffet used Revival Blessing!"); // MESSAGE("Wynaut was revived and is ready to fight again!"); // NOT { MESSAGE("Wynaut used Celebrate!"); } // } diff --git a/test/battle/move_effect/semi_invulnerable_moves.c b/test/battle/move_effect/semi_invulnerable_moves.c index 97760225d1..3ba4889005 100644 --- a/test/battle/move_effect/semi_invulnerable_moves.c +++ b/test/battle/move_effect/semi_invulnerable_moves.c @@ -58,7 +58,7 @@ SINGLE_BATTLE_TEST("Semi-invulnerable moves make the user semi-invulnerable turn break; case MOVE_PHANTOM_FORCE: NOT MESSAGE("Wobbuffet vanished instantly!"); - MESSAGE("Wobbuffet used PhantomForce!"); + MESSAGE("Wobbuffet used Phantom Force!"); break; case MOVE_SHADOW_FORCE: NOT MESSAGE("Wobbuffet vanished instantly!"); @@ -112,7 +112,7 @@ SINGLE_BATTLE_TEST("Semi-invulnerable moves make the user semi-invulnerable turn MESSAGE("Wobbuffet used Dive!"); break; case MOVE_PHANTOM_FORCE: - MESSAGE("Wobbuffet used PhantomForce!"); + MESSAGE("Wobbuffet used Phantom Force!"); break; case MOVE_SHADOW_FORCE: MESSAGE("Wobbuffet used Shadow Force!"); @@ -163,7 +163,7 @@ SINGLE_BATTLE_TEST("Semi-invulnerable moves don't need to charge with Power Herb break; case MOVE_PHANTOM_FORCE: NOT MESSAGE("Wobbuffet vanished instantly!"); - MESSAGE("Wobbuffet used PhantomForce!"); + MESSAGE("Wobbuffet used Phantom Force!"); break; case MOVE_SHADOW_FORCE: NOT MESSAGE("Wobbuffet vanished instantly!"); @@ -215,7 +215,7 @@ SINGLE_BATTLE_TEST("Semi-invulnerable moves don't need to charge with Power Herb MESSAGE("Wobbuffet used Dive!"); break; case MOVE_PHANTOM_FORCE: - MESSAGE("Wobbuffet used PhantomForce!"); + MESSAGE("Wobbuffet used Phantom Force!"); break; case MOVE_SHADOW_FORCE: MESSAGE("Wobbuffet used Shadow Force!"); diff --git a/test/battle/terrain/electric.c b/test/battle/terrain/electric.c index c6fda3fb3e..eb7a112bb5 100644 --- a/test/battle/terrain/electric.c +++ b/test/battle/terrain/electric.c @@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Electric Terrain protects grounded battlers from falling asl TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); MOVE(opponent, MOVE_SPORE); } TURN { MOVE(player, MOVE_SPORE); } } SCENE { - MESSAGE("Wobbuffet used ElctrcTrrain!"); + MESSAGE("Wobbuffet used Electric Terrain!"); MESSAGE("Foe Claydol used Spore!"); MESSAGE("Wobbuffet surrounds itself with electrified terrain!"); MESSAGE("Wobbuffet used Spore!"); @@ -51,7 +51,7 @@ SINGLE_BATTLE_TEST("Electric Terrain increases power of Electric-type moves by 3 TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); } TURN { MOVE(player, MOVE_THUNDER_SHOCK); } } SCENE { - MESSAGE("Wobbuffet used ThunderShock!"); + MESSAGE("Wobbuffet used Thunder Shock!"); HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { if (B_TERRAIN_TYPE_BOOST >= GEN_8) diff --git a/test/battle/terrain/misty.c b/test/battle/terrain/misty.c index c76689ce79..cd89b2d9ab 100644 --- a/test/battle/terrain/misty.c +++ b/test/battle/terrain/misty.c @@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Misty Terrain protects grounded battlers from non-volatile s TURN { MOVE(player, MOVE_MISTY_TERRAIN); MOVE(opponent, MOVE_TOXIC); } TURN { MOVE(player, MOVE_TOXIC); } } SCENE { - MESSAGE("Wobbuffet used MistyTerrain!"); + MESSAGE("Wobbuffet used Misty Terrain!"); MESSAGE("Foe Claydol used Toxic!"); MESSAGE("Wobbuffet surrounds itself with a protective mist!"); NOT { STATUS_ICON(opponent, badPoison: TRUE); } diff --git a/test/battle/terrain/psychic.c b/test/battle/terrain/psychic.c index 20f7deef6e..1881db6297 100644 --- a/test/battle/terrain/psychic.c +++ b/test/battle/terrain/psychic.c @@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority mov TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); } TURN { MOVE(player, MOVE_QUICK_ATTACK); MOVE(opponent, MOVE_QUICK_ATTACK); } } SCENE { - MESSAGE("Claydol used PsychcTrrain!"); + MESSAGE("Claydol used Psychic Terrain!"); MESSAGE("Claydol cannot use Quick Attack!"); NOT { HP_BAR(opponent); } MESSAGE("Foe Wobbuffet used Quick Attack!"); @@ -69,7 +69,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); } TURN { MOVE(player, MOVE_RECOVER); } } SCENE { - MESSAGE("Sableye used PsychcTrrain!"); + MESSAGE("Sableye used Psychic Terrain!"); MESSAGE("Sableye used Recover!"); HP_BAR(player); } @@ -84,7 +84,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); } TURN { MOVE(player, MOVE_HAZE); } } SCENE { - MESSAGE("Sableye used PsychcTrrain!"); + MESSAGE("Sableye used Psychic Terrain!"); MESSAGE("Sableye used Haze!"); } } @@ -98,7 +98,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); } TURN { MOVE(player, MOVE_SPIKES); } } SCENE { - MESSAGE("Sableye used PsychcTrrain!"); + MESSAGE("Sableye used Psychic Terrain!"); MESSAGE("Sableye used Spikes!"); } } @@ -114,7 +114,7 @@ DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all TURN { MOVE(playerLeft, MOVE_PSYCHIC_TERRAIN); } TURN { MOVE(playerLeft, MOVE_HEAL_PULSE, target: playerRight); } } SCENE { - MESSAGE("Sableye used PsychcTrrain!"); + MESSAGE("Sableye used Psychic Terrain!"); MESSAGE("Sableye used Heal Pulse!"); } } @@ -128,7 +128,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves") TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); } TURN { MOVE(player, MOVE_SUNNY_DAY); } } SCENE { - MESSAGE("Sableye used PsychcTrrain!"); + MESSAGE("Sableye used Psychic Terrain!"); MESSAGE("Sableye used Sunny Day!"); } } diff --git a/test/text.c b/test/text.c new file mode 100644 index 0000000000..ee81386c00 --- /dev/null +++ b/test/text.c @@ -0,0 +1,77 @@ +#include "global.h" +#include "test/test.h" +#include "item.h" +#include "text.h" +#include "constants/moves.h" + +TEST("Move names fit on Pokemon Summary Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 72; + u32 move = MOVE_NONE; + for (i = 1; i < MOVES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gMovesInfo[i].name) { move = i; } + } + EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); +} + +TEST("Move names fit on Battle Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 64; + u32 move = MOVE_NONE; + for (i = 1; i < MOVES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gMovesInfo[i].name) { move = i; } + } + EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); +} + +TEST("Move names fit on Contest Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 61; + u32 move = MOVE_NONE; + for (i = 1; i < MOVES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gMovesInfo[i].name) { move = i; } + } + // All moves explicitly listed here are too big to fit. + switch (move) + { + case MOVE_NATURES_MADNESS: + EXPECT_GT(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); + break; + default: + EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); + break; + } +} + +TEST("Move names fit on TMs & HMs Bag Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 63; + u32 move = MOVE_NONE; + for (i = 1; i < ITEMS_COUNT; i++) + { + if (gItemsInfo[i].pocket == POCKET_TM_HM) + { + PARAMETRIZE_LABEL("%S", gMovesInfo[gItemsInfo[i].secondaryId].name) { move = gItemsInfo[i].secondaryId; } + } + } + EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); +} + +TEST("Move names fit on Move Relearner Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 72; + u32 move = MOVE_NONE; + for (i = 1; i < MOVES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gMovesInfo[i].name) { move = i; } + } + EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); +} From 484e288cad2736eb70c96e6c1cccf7770d1f1d17 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 16 Mar 2024 13:28:08 +0000 Subject: [PATCH 25/71] Expanded item names --- include/config/item.h | 1 + include/constants/global.h | 2 +- src/data/items.h | 264 +++++++++++++++++++------------------ 3 files changed, 137 insertions(+), 130 deletions(-) diff --git a/include/config/item.h b/include/config/item.h index 1a1a196a16..2f7db2ed92 100644 --- a/include/config/item.h +++ b/include/config/item.h @@ -2,6 +2,7 @@ #define GUARD_CONFIG_ITEM_H // Item config +#define I_EXPANDED_ITEM_NAMES FALSE // If set to TRUE, item names are increased from 14 characters to 20 characters. #define I_SHINY_CHARM_ADDITIONAL_ROLLS 2 // Amount of additional shiny rolls if the player has the Shiny Charm. Set it to 0 to disable Shiny Charm's effects. #define I_KEY_FOSSILS GEN_LATEST // In Gen4+, all Gen 3 fossils became regular items. #define I_KEY_ESCAPE_ROPE GEN_LATEST // In Gen8, Escape Rope became a Key Item. Keep in mind, this will make it free to buy in marts. diff --git a/include/constants/global.h b/include/constants/global.h index daf1c9edb4..5118beb189 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -102,7 +102,7 @@ #define CONTEST_CATEGORIES_COUNT 5 // string lengths -#define ITEM_NAME_LENGTH 14 +#define ITEM_NAME_LENGTH ((I_EXPANDED_ITEM_NAMES == TRUE) ? 20 : 14) #define ITEM_NAME_PLURAL_LENGTH ITEM_NAME_LENGTH + 2 // 2 is used for the instance where a word's suffix becomes y->ies #define POKEMON_NAME_LENGTH 10 #define POKEMON_NAME_BUFFER_SIZE max(20, POKEMON_NAME_LENGTH + 1) // Frequently used buffer size. Larger than necessary diff --git a/src/data/items.h b/src/data/items.h index 3a65f8758c..eaa52b5d8f 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -1,5 +1,11 @@ #include "constants/moves.h" +#if I_EXPANDED_ITEM_NAMES == TRUE +#define HANDLE_EXPANDED_ITEM_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__)) +#else +#define HANDLE_EXPANDED_ITEM_NAME(_name, ...) _(_name) +#endif + #if I_USE_EVO_HELD_ITEMS_FROM_BAG == TRUE #define EVO_HELD_ITEM_TYPE ITEM_USE_PARTY_MENU #define EVO_HELD_ITEM_FIELD_FUNC ItemUseOutOfBattle_EvolutionStone @@ -1041,8 +1047,8 @@ const struct Item gItemsInfo[] = [ITEM_PEWTER_CRUNCHIES] = { - .name = _("PewtrCrnches"), - .pluralName = _("PewtrCrnches"), + .name = HANDLE_EXPANDED_ITEM_NAME("PewtrCrnches", "Pewter Crunchies"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("PewtrCrnches", "Pewter Crunchies"), .price = 250, .description = sFullHealDesc, .pocket = POCKET_ITEMS, @@ -1055,7 +1061,7 @@ const struct Item gItemsInfo[] = [ITEM_RAGE_CANDY_BAR] = { - .name = _("RageCandyBar"), + .name = HANDLE_EXPANDED_ITEM_NAME("RageCandyBar", "Rage Candy Bar"), .price = (I_PRICE >= GEN_7) ? 350 : 300, .description = sFullHealDesc, .pocket = POCKET_ITEMS, @@ -1111,7 +1117,7 @@ const struct Item gItemsInfo[] = [ITEM_LUMIOSE_GALETTE] = { - .name = _("LumioseGlete"), + .name = HANDLE_EXPANDED_ITEM_NAME("LumioseGlete", "Lumiose Galette"), .price = (I_PRICE >= GEN_7) ? 350 : 200, .description = sFullHealDesc, .pocket = POCKET_ITEMS, @@ -1124,7 +1130,7 @@ const struct Item gItemsInfo[] = [ITEM_SHALOUR_SABLE] = { - .name = _("ShalourSable"), + .name = HANDLE_EXPANDED_ITEM_NAME("ShalourSable", "Shalour Sable"), .price = (I_PRICE >= GEN_7) ? 350 : 200, .description = sFullHealDesc, .pocket = POCKET_ITEMS, @@ -1275,7 +1281,7 @@ const struct Item gItemsInfo[] = [ITEM_HEALTH_FEATHER] = { - .name = _("HealthFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("HealthFeather", "Health Feather"), .price = (I_PRICE >= GEN_7) ? 300 : 3000, .description = sHealthFeatherDesc, .pocket = POCKET_ITEMS, @@ -1287,7 +1293,7 @@ const struct Item gItemsInfo[] = [ITEM_MUSCLE_FEATHER] = { - .name = _("MuscleFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("MuscleFeather", "Muscle Feather"), .price = (I_PRICE >= GEN_7) ? 300 : 3000, .description = sMuscleFeatherDesc, .pocket = POCKET_ITEMS, @@ -1299,7 +1305,7 @@ const struct Item gItemsInfo[] = [ITEM_RESIST_FEATHER] = { - .name = _("ResistFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("ResistFeather", "Resist Feather"), .price = (I_PRICE >= GEN_7) ? 300 : 3000, .description = sResistFeatherDesc, .pocket = POCKET_ITEMS, @@ -1311,7 +1317,7 @@ const struct Item gItemsInfo[] = [ITEM_GENIUS_FEATHER] = { - .name = _("GeniusFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("GeniusFeather", "Genius Feather"), .price = (I_PRICE >= GEN_7) ? 300 : 3000, .description = sGeniusFeatherDesc, .pocket = POCKET_ITEMS, @@ -1323,7 +1329,7 @@ const struct Item gItemsInfo[] = [ITEM_CLEVER_FEATHER] = { - .name = _("CleverFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("CleverFeather", "Clever Feather"), .price = (I_PRICE >= GEN_7) ? 300 : 3000, .description = sCleverFeatherDesc, .pocket = POCKET_ITEMS, @@ -1335,7 +1341,7 @@ const struct Item gItemsInfo[] = [ITEM_SWIFT_FEATHER] = { - .name = _("SwiftFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("SwiftFeather", "Swift Feather"), .price = (I_PRICE >= GEN_7) ? 300 : 3000, .description = sSwiftFeatherDesc, .pocket = POCKET_ITEMS, @@ -1349,7 +1355,7 @@ const struct Item gItemsInfo[] = [ITEM_ABILITY_CAPSULE] = { - .name = _("AbilityCapsle"), + .name = HANDLE_EXPANDED_ITEM_NAME("AbilityCapsle", "Ability Capsule"), .price = (I_PRICE < GEN_7) ? 1000 : ((I_PRICE < GEN_9) ? 10000 : 100000), .holdEffectParam = 0, .description = COMPOUND_STRING( @@ -1362,8 +1368,8 @@ const struct Item gItemsInfo[] = [ITEM_ABILITY_PATCH] = { - .name = _("AbilityPatch"), - .pluralName = _("AbilityPatches"), + .name = HANDLE_EXPANDED_ITEM_NAME("AbilityPatch", "Ability Patch"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("AbilityPatches", "Ability Patches"), .price = (I_PRICE >= GEN_9) ? 250000 : 20, .holdEffectParam = 0, .description = COMPOUND_STRING( @@ -1712,8 +1718,8 @@ const struct Item gItemsInfo[] = [ITEM_EXP_CANDY_XS] = { - .name = _("Exp.Candy XS"), - .pluralName = _("Exp.Candies XS"), + .name = HANDLE_EXPANDED_ITEM_NAME("Exp.Candy XS", "Exp. Candy XS"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Exp.Candies XS", "Exp. Candies XS"), .price = 20, .holdEffectParam = EXP_100, .description = COMPOUND_STRING( @@ -1729,8 +1735,8 @@ const struct Item gItemsInfo[] = [ITEM_EXP_CANDY_S] = { - .name = _("Exp.Candy S"), - .pluralName = _("Exp.Candies S"), + .name = HANDLE_EXPANDED_ITEM_NAME("Exp.Candy S", "Exp. Candy S"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Exp.Candies S", "Exp. Candies S"), .price = 240, .holdEffectParam = EXP_800, .description = COMPOUND_STRING( @@ -1746,8 +1752,8 @@ const struct Item gItemsInfo[] = [ITEM_EXP_CANDY_M] = { - .name = _("Exp.Candy M"), - .pluralName = _("Exp.Candies M"), + .name = HANDLE_EXPANDED_ITEM_NAME("Exp.Candy M", "Exp. Candy M"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Exp.Candies M", "Exp. Candies M"), .price = 1000, .holdEffectParam = EXP_3000, .description = COMPOUND_STRING( @@ -1763,8 +1769,8 @@ const struct Item gItemsInfo[] = [ITEM_EXP_CANDY_L] = { - .name = _("Exp.Candy L"), - .pluralName = _("Exp.Candies L"), + .name = HANDLE_EXPANDED_ITEM_NAME("Exp.Candy L", "Exp. Candy L"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Exp.Candies L", "Exp. Candies L"), .price = 3000, .holdEffectParam = EXP_10000, .description = COMPOUND_STRING( @@ -1780,8 +1786,8 @@ const struct Item gItemsInfo[] = [ITEM_EXP_CANDY_XL] = { - .name = _("Exp.Candy XL"), - .pluralName = _("Exp.Candies L"), + .name = HANDLE_EXPANDED_ITEM_NAME("Exp.Candy XL", "Exp. Candy XL"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Exp.Candies XL", "Exp. Candies XL"), .price = 10000, .holdEffectParam = EXP_30000, .description = COMPOUND_STRING( @@ -1797,8 +1803,8 @@ const struct Item gItemsInfo[] = [ITEM_DYNAMAX_CANDY] = { - .name = _("DynamaxCandy"), - .pluralName = _("DynamaxCandies"), + .name = HANDLE_EXPANDED_ITEM_NAME("DynamaxCandy", "Dynamax Candy"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("DynamaxCandies", "Dynamax Candies"), .price = 0, .description = COMPOUND_STRING( "Raises the Dynamax\n" @@ -2218,8 +2224,8 @@ const struct Item gItemsInfo[] = [ITEM_MAX_MUSHROOMS] = { - .name = _("MaxMushrooms"), - .pluralName = _("MaxMushrooms"), + .name = HANDLE_EXPANDED_ITEM_NAME("MaxMushrooms", "Max Mushrooms"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("MaxMushrooms", "Max Mushrooms"), .price = 8000, .description = COMPOUND_STRING( "Raises every stat\n" @@ -2250,7 +2256,7 @@ const struct Item gItemsInfo[] = [ITEM_GOLD_BOTTLE_CAP] = { - .name = _("GoldBottlCap"), + .name = HANDLE_EXPANDED_ITEM_NAME("GoldBottlCap", "Gold Bottle Cap"), .price = (I_PRICE >= GEN_9) ? 60000 : 10000, .description = COMPOUND_STRING( "A beautiful bottle\n" @@ -2543,7 +2549,7 @@ const struct Item gItemsInfo[] = [ITEM_PRETTY_FEATHER] = { - .name = _("PrettyFeather"), + .name = HANDLE_EXPANDED_ITEM_NAME("PrettyFeather", "Pretty Feather"), .price = (I_PRICE >= GEN_7) ? 1000 * TREASURE_FACTOR: 200, .description = COMPOUND_STRING( "A beautiful yet\n" @@ -2655,7 +2661,7 @@ const struct Item gItemsInfo[] = [ITEM_STRANGE_SOUVENIR] = { - .name = _("StrngeSouvnr"), + .name = HANDLE_EXPANDED_ITEM_NAME("StrngeSouvnr", "Strange Souvenir"), .price = (I_PRICE >= GEN_7) ? 3000 : 10, .description = COMPOUND_STRING( "An ornament that\n" @@ -2849,7 +2855,7 @@ const struct Item gItemsInfo[] = [ITEM_FOSSILIZED_BIRD] = { - .name = _("FosslzedBird"), + .name = HANDLE_EXPANDED_ITEM_NAME("FosslzedBird", "Fossilized Bird"), .price = 5000, .description = COMPOUND_STRING( "A fossil of an\n" @@ -2863,8 +2869,8 @@ const struct Item gItemsInfo[] = [ITEM_FOSSILIZED_FISH] = { - .name = _("FosslzedFish"), - .pluralName = _("FosslzedFishes"), + .name = HANDLE_EXPANDED_ITEM_NAME("FosslzedFish", "Fossilized Fish"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("FosslzedFishes", "Fossilized Fishes"), .price = 5000, .description = sFossilizedFishDesc, .pocket = POCKET_ITEMS, @@ -2875,7 +2881,7 @@ const struct Item gItemsInfo[] = [ITEM_FOSSILIZED_DRAKE] = { - .name = _("FosslzedDrke"), + .name = HANDLE_EXPANDED_ITEM_NAME("FosslzedDrke", "Fossilized Drake"), .price = 5000, .description = COMPOUND_STRING( "A fossil of an\n" @@ -2889,7 +2895,7 @@ const struct Item gItemsInfo[] = [ITEM_FOSSILIZED_DINO] = { - .name = _("FosslzedDino"), + .name = HANDLE_EXPANDED_ITEM_NAME("FosslzedDino", "Fossilized Dino"), .price = 5000, .description = sFossilizedFishDesc, .pocket = POCKET_ITEMS, @@ -3002,8 +3008,8 @@ const struct Item gItemsInfo[] = [ITEM_SURPRISE_MULCH] = { - .name = _("SurprseMulch"), - .pluralName = _("SurprseMulch"), + .name = HANDLE_EXPANDED_ITEM_NAME("SurprseMulch", "Surprise Mulch"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("SurprseMulch", "Surprise Mulch"), .price = 200, #if OW_BERRY_MULCH_USAGE == TRUE .description = COMPOUND_STRING( @@ -3090,7 +3096,7 @@ const struct Item gItemsInfo[] = [ITEM_YELLOW_APRICORN] = { - .name = _("YellwApricorn"), + .name = HANDLE_EXPANDED_ITEM_NAME("YellwApricorn", "Yellow Apricorn"), .price = (I_PRICE == GEN_4) ? 0 : ((I_PRICE >= GEN_5 && I_PRICE <= GEN_7) ? 20 : 200), .description = COMPOUND_STRING( "A yellow apricorn.\n" @@ -3103,7 +3109,7 @@ const struct Item gItemsInfo[] = [ITEM_GREEN_APRICORN] = { - .name = _("GreenApricorn"), + .name = HANDLE_EXPANDED_ITEM_NAME("GreenApricorn", "Green Apricorn"), .price = (I_PRICE == GEN_4) ? 0 : ((I_PRICE >= GEN_5 && I_PRICE <= GEN_7) ? 20 : 200), .description = COMPOUND_STRING( "A green apricorn.\n" @@ -3129,7 +3135,7 @@ const struct Item gItemsInfo[] = [ITEM_WHITE_APRICORN] = { - .name = _("WhiteApricorn"), + .name = HANDLE_EXPANDED_ITEM_NAME("WhiteApricorn", "White Apricorn"), .price = (I_PRICE == GEN_4) ? 0 : ((I_PRICE >= GEN_5 && I_PRICE <= GEN_7) ? 20 : 200), .description = COMPOUND_STRING( "A white apricorn.\n" @@ -3142,7 +3148,7 @@ const struct Item gItemsInfo[] = [ITEM_BLACK_APRICORN] = { - .name = _("BlackApricorn"), + .name = HANDLE_EXPANDED_ITEM_NAME("BlackApricorn", "Black Apricorn"), .price = (I_PRICE == GEN_4) ? 0 : ((I_PRICE >= GEN_5 && I_PRICE <= GEN_7) ? 20 : 200), .description = COMPOUND_STRING( "A black apricorn.\n" @@ -3155,7 +3161,7 @@ const struct Item gItemsInfo[] = [ITEM_WISHING_PIECE] = { - .name = _("WishingPiece"), + .name = HANDLE_EXPANDED_ITEM_NAME("WishingPiece", "Wishing Piece"), .price = 20, .description = COMPOUND_STRING( "Throw into a\n" @@ -3169,7 +3175,7 @@ const struct Item gItemsInfo[] = [ITEM_GALARICA_TWIG] = { - .name = _("GalaricaTwig"), + .name = HANDLE_EXPANDED_ITEM_NAME("GalaricaTwig", "Galarica Twig"), .price = 20 * TREASURE_FACTOR, .description = COMPOUND_STRING( "A twig from a tree\n" @@ -3571,7 +3577,7 @@ const struct Item gItemsInfo[] = [ITEM_GALARICA_CUFF] = { - .name = _("GalaricaCuff"), + .name = HANDLE_EXPANDED_ITEM_NAME("GalaricaCuff", "Galarica Cuff"), .price = (I_PRICE >= GEN_9) ? 3000 : 6000, .description = COMPOUND_STRING( "A cuff from Galar\n" @@ -3586,8 +3592,8 @@ const struct Item gItemsInfo[] = [ITEM_GALARICA_WREATH] = { - .name = _("GalrcaWreath"), - .pluralName = _("GalrcaWreathes"), + .name = HANDLE_EXPANDED_ITEM_NAME("GalrcaWreath", "Galarica Wreath"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("GalrcaWreathes", "Galarica Wreathes"), .price = (I_PRICE >= GEN_9) ? 3000 : 6000, .description = COMPOUND_STRING( "A wreath made in\n" @@ -3771,7 +3777,7 @@ const struct Item gItemsInfo[] = [ITEM_STRAWBERRY_SWEET] = { - .name = _("StrwbrySweet"), + .name = HANDLE_EXPANDED_ITEM_NAME("StrwbrySweet", "Strawberry Sweet"), .price = 500 * TREASURE_FACTOR, .description = COMPOUND_STRING( "Strawberry-shaped\n" @@ -4329,8 +4335,8 @@ const struct Item gItemsInfo[] = [ITEM_ELECTRIC_MEMORY] = { - .name = _("ElectrcMemory"), - .pluralName = _("ElectrcMemories"), + .name = HANDLE_EXPANDED_ITEM_NAME("ElectrcMemory", "Electric Memory"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("ElectrcMemories", "Electric Memories"), .price = 1000, .holdEffect = HOLD_EFFECT_MEMORY, .holdEffectParam = 0, @@ -4383,8 +4389,8 @@ const struct Item gItemsInfo[] = [ITEM_FIGHTING_MEMORY] = { - .name = _("FightngMemory"), - .pluralName = _("FightngMemories"), + .name = HANDLE_EXPANDED_ITEM_NAME("FightngMemory", "Fighting Memory"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("FightngMemories", "Fighting Memories"), .price = 1000, .holdEffect = HOLD_EFFECT_MEMORY, .holdEffectParam = 0, @@ -4455,8 +4461,8 @@ const struct Item gItemsInfo[] = [ITEM_PSYCHIC_MEMORY] = { - .name = _("PsychicMemory"), - .pluralName = _("PsychicMemories"), + .name = HANDLE_EXPANDED_ITEM_NAME("PsychicMemory", "Psychic Memory"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("PsychicMemories", "Psychic Memories"), .price = 1000, .holdEffect = HOLD_EFFECT_MEMORY, .holdEffectParam = 0, @@ -4599,7 +4605,7 @@ const struct Item gItemsInfo[] = [ITEM_RUSTED_SWORD] = { - .name = _("RustedSword"), + .name = HANDLE_EXPANDED_ITEM_NAME("RustedSword", "Rusted Sword"), .price = 0, .description = COMPOUND_STRING( "A rusty sword. A\n" @@ -4612,7 +4618,7 @@ const struct Item gItemsInfo[] = [ITEM_RUSTED_SHIELD] = { - .name = _("RustedShield"), + .name = HANDLE_EXPANDED_ITEM_NAME("RustedShield", "Rusted Shield"), .price = 0, .description = COMPOUND_STRING( "A rusty shield. A\n" @@ -4672,7 +4678,7 @@ const struct Item gItemsInfo[] = [ITEM_CHARIZARDITE_X] = { - .name = _("CharizarditeX"), + .name = HANDLE_EXPANDED_ITEM_NAME("CharizarditeX", "Charizardite X"), .pluralName = _("Charizardites X"), .price = 0, .holdEffect = HOLD_EFFECT_MEGA_STONE, @@ -4685,7 +4691,7 @@ const struct Item gItemsInfo[] = [ITEM_CHARIZARDITE_Y] = { - .name = _("CharizarditeY"), + .name = HANDLE_EXPANDED_ITEM_NAME("CharizarditeY", "Charizardite Y"), .pluralName = _("Charizardites Y"), .price = 0, .holdEffect = HOLD_EFFECT_MEGA_STONE, @@ -6156,7 +6162,7 @@ const struct Item gItemsInfo[] = [ITEM_ULTRANECROZIUM_Z] = { - .name = _("U-Necrozium Z"), + .name = HANDLE_EXPANDED_ITEM_NAME("U-Necrozium Z", "Ultranecrozium Z"), .price = 0, .holdEffect = HOLD_EFFECT_Z_CRYSTAL, .description = COMPOUND_STRING( @@ -6266,7 +6272,7 @@ const struct Item gItemsInfo[] = [ITEM_DEEP_SEA_SCALE] = { - .name = _("DeepSeaScale"), + .name = HANDLE_EXPANDED_ITEM_NAME("DeepSeaScale", "Deep Sea Scale"), .price = (I_PRICE >= GEN_7) ? 2000 : 200, .holdEffect = HOLD_EFFECT_DEEP_SEA_SCALE, .description = COMPOUND_STRING( @@ -6282,8 +6288,8 @@ const struct Item gItemsInfo[] = [ITEM_DEEP_SEA_TOOTH] = { - .name = _("DeepSeaTooth"), - .pluralName = _("DeepSeaTeeth"), + .name = HANDLE_EXPANDED_ITEM_NAME("DeepSeaTooth", "Deep Sea Tooth"), + .pluralName = _("Deep Sea Teeth"), .price = (I_PRICE >= GEN_7) ? 2000 : 200, .holdEffect = HOLD_EFFECT_DEEP_SEA_TOOTH, .description = COMPOUND_STRING( @@ -6767,8 +6773,8 @@ const struct Item gItemsInfo[] = [ITEM_NEVER_MELT_ICE] = { - .name = _("Never-MeltIce"), - .pluralName = _("Never-MeltIce"), + .name = HANDLE_EXPANDED_ITEM_NAME("Never-MeltIce", "Never-Melt Ice"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Never-MeltIce", "Never-Melt Ice"), .price = (I_PRICE >= GEN_9) ? 3000 : ((I_PRICE >= GEN_7) ? 1000 : 100), .holdEffect = HOLD_EFFECT_ICE_POWER, .holdEffectParam = TYPE_BOOST_PARAM, @@ -7913,8 +7919,8 @@ const struct Item gItemsInfo[] = [ITEM_WEAKNESS_POLICY] = { - .name = _("WeaknssPolicy"), - .pluralName = _("WeaknssPolicies"), + .name = HANDLE_EXPANDED_ITEM_NAME("WeaknssPolicy", "Weakness Policy"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("WeaknssPolicies", "Weakness Policies"), .price = (I_PRICE >= GEN_9) ? 50000 : 1000, .holdEffect = HOLD_EFFECT_WEAKNESS_POLICY, .holdEffectParam = 0, @@ -7946,8 +7952,8 @@ const struct Item gItemsInfo[] = [ITEM_SAFETY_GOGGLES] = { - .name = _("SafetyGoggles"), - .pluralName = _("SafetyGoggles"), + .name = HANDLE_EXPANDED_ITEM_NAME("SafetyGoggles", "Safety Goggles"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("SafetyGoggles", "Safety Goggles"), .price = (I_PRICE >= GEN_9) ? 20000 : ((I_PRICE >= GEN_7) ? 4000 : 1000), .holdEffect = HOLD_EFFECT_SAFETY_GOGGLES, .description = COMPOUND_STRING( @@ -7962,7 +7968,7 @@ const struct Item gItemsInfo[] = [ITEM_ADRENALINE_ORB] = { - .name = _("AdrenalineOrb"), + .name = HANDLE_EXPANDED_ITEM_NAME("AdrenalineOrb", "Adrenaline Orb"), .price = (I_PRICE >= GEN_9) ? 5000 : ((I_PRICE >= GEN_8) ? 4000 : 300), .holdEffect = HOLD_EFFECT_ADRENALINE_ORB, .description = COMPOUND_STRING( @@ -7977,7 +7983,7 @@ const struct Item gItemsInfo[] = [ITEM_TERRAIN_EXTENDER] = { - .name = _("TerainExtendr"), + .name = HANDLE_EXPANDED_ITEM_NAME("TerainExtendr", "Terrain Extender"), .price = (I_PRICE >= GEN_9) ? 15000 : 4000, .holdEffect = HOLD_EFFECT_TERRAIN_EXTENDER, .description = COMPOUND_STRING( @@ -7992,8 +7998,8 @@ const struct Item gItemsInfo[] = [ITEM_PROTECTIVE_PADS] = { - .name = _("ProtectvePads"), - .pluralName = _("ProtectvePads"), + .name = HANDLE_EXPANDED_ITEM_NAME("ProtectvePads", "Protective Pads"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("ProtectvePads", "Protective Pads"), .price = (I_PRICE >= GEN_9) ? 15000 : 4000, .holdEffect = HOLD_EFFECT_PROTECTIVE_PADS, .description = COMPOUND_STRING( @@ -8038,8 +8044,8 @@ const struct Item gItemsInfo[] = [ITEM_HEAVY_DUTY_BOOTS] = { - .name = _("Heavy-DtyBts"), - .pluralName = _("Heavy-DtyBts"), + .name = HANDLE_EXPANDED_ITEM_NAME("Heavy-DtyBts", "Heavy-Duty Boots"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("Heavy-DtyBts", "Heavy-Duty Boots"), .price = (I_PRICE >= GEN_9) ? 20000 : 4000, .holdEffect = HOLD_EFFECT_HEAVY_DUTY_BOOTS, .description = COMPOUND_STRING( @@ -8054,8 +8060,8 @@ const struct Item gItemsInfo[] = [ITEM_BLUNDER_POLICY] = { - .name = _("BlundrPolicy"), - .pluralName = _("BlundrPolicies"), + .name = HANDLE_EXPANDED_ITEM_NAME("BlundrPolicy", "Blunder Policy"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("BlundrPolicies", "Blunder Policies"), .price = (I_PRICE >= GEN_9) ? 30000 : 4000, .holdEffect = HOLD_EFFECT_BLUNDER_POLICY, .description = COMPOUND_STRING( @@ -8085,7 +8091,7 @@ const struct Item gItemsInfo[] = [ITEM_UTILITY_UMBRELLA] = { - .name = _("UtltyUmbrlla"), + .name = HANDLE_EXPANDED_ITEM_NAME("UtltyUmbrlla", "Utility Umbrella"), .price = (I_PRICE >= GEN_9) ? 15000 : 4000, .holdEffect = HOLD_EFFECT_UTILITY_UMBRELLA, .description = COMPOUND_STRING( @@ -10736,7 +10742,7 @@ const struct Item gItemsInfo[] = [ITEM_CATCHING_CHARM] = { - .name = _("CatchngCharm"), + .name = HANDLE_EXPANDED_ITEM_NAME("CatchngCharm", "Catching Charm"), .price = 0, .importance = 1, .description = COMPOUND_STRING( @@ -10766,7 +10772,7 @@ const struct Item gItemsInfo[] = [ITEM_ROTOM_CATALOG] = { - .name = _("RotomCatalog"), + .name = HANDLE_EXPANDED_ITEM_NAME("RotomCatalog", "Rotom Catalog"), .price = 0, .importance = 1, .description = COMPOUND_STRING( @@ -10880,8 +10886,8 @@ const struct Item gItemsInfo[] = [ITEM_REINS_OF_UNITY] = { - .name = _("ReinsOfUnity"), - .pluralName = _("ReinsOfUnity"), + .name = HANDLE_EXPANDED_ITEM_NAME("ReinsOfUnity", "Reins of Unity"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("ReinsOfUnity", "Reins of Unity"), .price = 0, .importance = 1, .description = COMPOUND_STRING( @@ -11679,7 +11685,7 @@ const struct Item gItemsInfo[] = [ITEM_ABILITY_SHIELD] = { - .name = _("AbilityShield"), + .name = HANDLE_EXPANDED_ITEM_NAME("AbilityShield", "Ability Shield"), .price = 20000, .holdEffect = HOLD_EFFECT_ABILITY_SHIELD, .description = COMPOUND_STRING( @@ -11711,7 +11717,7 @@ const struct Item gItemsInfo[] = [ITEM_PUNCHING_GLOVE] = { - .name = _("PunchingGlove"), + .name = HANDLE_EXPANDED_ITEM_NAME("PunchingGlove", "Punching Glove"), .price = 15000, .holdEffect = HOLD_EFFECT_PUNCHING_GLOVE, .description = COMPOUND_STRING( @@ -11757,7 +11763,7 @@ const struct Item gItemsInfo[] = [ITEM_AUSPICIOUS_ARMOR] = { - .name = _("AuspciousArmr"), + .name = HANDLE_EXPANDED_ITEM_NAME("AuspciousArmr", "Auspicious Armor"), .price = 3000, .description = COMPOUND_STRING( "Armor inhabited by\n" @@ -11772,8 +11778,8 @@ const struct Item gItemsInfo[] = [ITEM_BOOSTER_ENERGY] = { - .name = _("BoosterEnergy"), - .pluralName = _("BoosterEnergies"), + .name = HANDLE_EXPANDED_ITEM_NAME("BoosterEnergy", "Booster Energy"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("BoosterEnergies", "Booster Energies"), .price = 0, .holdEffect = HOLD_EFFECT_BOOSTER_ENERGY, .description = COMPOUND_STRING( @@ -11788,7 +11794,7 @@ const struct Item gItemsInfo[] = [ITEM_BIG_BAMBOO_SHOOT] = { - .name = _("BigBmbooShoot"), + .name = HANDLE_EXPANDED_ITEM_NAME("BigBmbooShoot", "Big Bamboo Shoot"), .price = 3000, .description = COMPOUND_STRING( "A large and rare\n" @@ -11802,7 +11808,7 @@ const struct Item gItemsInfo[] = [ITEM_GIMMIGHOUL_COIN] = { - .name = _("GimighoulCoin"), + .name = HANDLE_EXPANDED_ITEM_NAME("GimighoulCoin", "Gimmighoul Coin"), .price = 400, .description = COMPOUND_STRING( "Gimmighoul hoard\n" @@ -11815,7 +11821,7 @@ const struct Item gItemsInfo[] = [ITEM_LEADERS_CREST] = { - .name = _("Leader'sCrest"), + .name = HANDLE_EXPANDED_ITEM_NAME("Leader'sCrest", "Leader's Crest"), .price = 3000, .description = COMPOUND_STRING( "A shard of an old\n" @@ -11828,7 +11834,7 @@ const struct Item gItemsInfo[] = [ITEM_MALICIOUS_ARMOR] = { - .name = _("MaliciousArmr"), + .name = HANDLE_EXPANDED_ITEM_NAME("MaliciousArmr", "Malicious Armor"), .price = 3000, .description = COMPOUND_STRING( "Armor inhabited by\n" @@ -11858,8 +11864,8 @@ const struct Item gItemsInfo[] = [ITEM_SCROLL_OF_DARKNESS] = { - .name = _("ScrllOfDrknss"), - .pluralName = _("ScrllsOfDrknss"), + .name = HANDLE_EXPANDED_ITEM_NAME("ScrllOfDrknss", "Scroll of Darkness"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("ScrllsOfDrknss", "Scrolls of Darkness"), .price = 0, .description = COMPOUND_STRING( "A peculiar scroll\n" @@ -11874,8 +11880,8 @@ const struct Item gItemsInfo[] = [ITEM_SCROLL_OF_WATERS] = { - .name = _("ScrollOfWatrs"), - .pluralName = _("ScrollsOfWatrs"), + .name = HANDLE_EXPANDED_ITEM_NAME("ScrollOfWatrs", "Scroll of Waters"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("ScrollsOfWatrs", "Scrolls of Waters"), .price = 0, .description = COMPOUND_STRING( "A peculiar scroll\n" @@ -11904,7 +11910,7 @@ const struct Item gItemsInfo[] = [ITEM_TINY_BAMBOO_SHOOT] = { - .name = _("TinyBmbooShot"), + .name = HANDLE_EXPANDED_ITEM_NAME("TinyBmbooShot", "Tiny Bamboo Shoot"), .price = 750, .description = COMPOUND_STRING( "A small and rare\n" @@ -11918,7 +11924,7 @@ const struct Item gItemsInfo[] = [ITEM_BUG_TERA_SHARD] = { - .name = _("Bug TeraShard"), + .name = HANDLE_EXPANDED_ITEM_NAME("Bug TeraShard", "Bug Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11928,7 +11934,7 @@ const struct Item gItemsInfo[] = [ITEM_DARK_TERA_SHARD] = { - .name = _("DarkTeraShard"), + .name = HANDLE_EXPANDED_ITEM_NAME("DarkTeraShard", "Dark Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11938,7 +11944,7 @@ const struct Item gItemsInfo[] = [ITEM_DRAGON_TERA_SHARD] = { - .name = _("DragnTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("DragnTeraShrd", "Dragon Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11948,7 +11954,7 @@ const struct Item gItemsInfo[] = [ITEM_ELECTRIC_TERA_SHARD] = { - .name = _("EltrcTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("EltrcTeraShrd", "Electric Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11958,7 +11964,7 @@ const struct Item gItemsInfo[] = [ITEM_FAIRY_TERA_SHARD] = { - .name = _("FairyTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("FairyTeraShrd", "Fairy Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11968,7 +11974,7 @@ const struct Item gItemsInfo[] = [ITEM_FIGHTING_TERA_SHARD] = { - .name = _("FghtngTerShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("FghtngTerShrd", "Fighting Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11978,7 +11984,7 @@ const struct Item gItemsInfo[] = [ITEM_FIRE_TERA_SHARD] = { - .name = _("FireTeraShard"), + .name = HANDLE_EXPANDED_ITEM_NAME("FireTeraShard", "Fire Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11988,7 +11994,7 @@ const struct Item gItemsInfo[] = [ITEM_FLYING_TERA_SHARD] = { - .name = _("FlyngTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("FlyngTeraShrd", "Flying Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -11998,7 +12004,7 @@ const struct Item gItemsInfo[] = [ITEM_GHOST_TERA_SHARD] = { - .name = _("GhostTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("GhostTeraShrd", "Ghost Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12008,7 +12014,7 @@ const struct Item gItemsInfo[] = [ITEM_GRASS_TERA_SHARD] = { - .name = _("GrassTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("GrassTeraShrd", "Grass Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12018,7 +12024,7 @@ const struct Item gItemsInfo[] = [ITEM_GROUND_TERA_SHARD] = { - .name = _("GrondTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("GrondTeraShrd", "Ground Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12028,7 +12034,7 @@ const struct Item gItemsInfo[] = [ITEM_ICE_TERA_SHARD] = { - .name = _("Ice TeraShard"), + .name = HANDLE_EXPANDED_ITEM_NAME("Ice TeraShard", "Ice Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12038,7 +12044,7 @@ const struct Item gItemsInfo[] = [ITEM_NORMAL_TERA_SHARD] = { - .name = _("NormlTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("NormlTeraShrd", "Normal Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12048,7 +12054,7 @@ const struct Item gItemsInfo[] = [ITEM_POISON_TERA_SHARD] = { - .name = _("PoisnTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("PoisnTeraShrd", "Poison Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12058,7 +12064,7 @@ const struct Item gItemsInfo[] = [ITEM_PSYCHIC_TERA_SHARD] = { - .name = _("PschcTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("PschcTeraShrd", "Psychic Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12068,7 +12074,7 @@ const struct Item gItemsInfo[] = [ITEM_ROCK_TERA_SHARD] = { - .name = _("RockTeraShard"), + .name = HANDLE_EXPANDED_ITEM_NAME("RockTeraShard", "Rock Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12078,7 +12084,7 @@ const struct Item gItemsInfo[] = [ITEM_STEEL_TERA_SHARD] = { - .name = _("SteelTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("SteelTeraShrd", "Steel Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12088,7 +12094,7 @@ const struct Item gItemsInfo[] = [ITEM_WATER_TERA_SHARD] = { - .name = _("WaterTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("WaterTeraShrd", "Water Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12098,7 +12104,7 @@ const struct Item gItemsInfo[] = [ITEM_ADAMANT_CRYSTAL] = { - .name = _("AdamantCrystl"), + .name = HANDLE_EXPANDED_ITEM_NAME("AdamantCrystl", "Adamant Crystal"), .price = 0, .description = COMPOUND_STRING( "A large, glowing gem\n" @@ -12126,7 +12132,7 @@ const struct Item gItemsInfo[] = [ITEM_LUSTROUS_GLOBE] = { - .name = _("LustrousGlobe"), + .name = HANDLE_EXPANDED_ITEM_NAME("LustrousGlobe", "Lustrous Globe"), .price = 0, .description = COMPOUND_STRING( "A large, glowing gem\n" @@ -12140,7 +12146,7 @@ const struct Item gItemsInfo[] = [ITEM_BLACK_AUGURITE] = { - .name = _("BlackAugurite"), + .name = HANDLE_EXPANDED_ITEM_NAME("BlackAugurite", "Black Augurite"), .price = 8000, .description = COMPOUND_STRING( "A black stone that\n" @@ -12231,7 +12237,7 @@ const struct Item gItemsInfo[] = [ITEM_UNREMARKABLE_TEACUP] = { - .name = _("UnrmkblTeacup"), + .name = HANDLE_EXPANDED_ITEM_NAME("UnrmkblTeacup", "Unremarkable Teacup"), .price = 1600, .description = COMPOUND_STRING( "A cracked teacup\n" @@ -12246,7 +12252,7 @@ const struct Item gItemsInfo[] = [ITEM_MASTERPIECE_TEACUP] = { - .name = _("MstrpceTeacup"), + .name = HANDLE_EXPANDED_ITEM_NAME("MstrpceTeacup", "Masterpiece Teacup"), .price = 38000, .description = COMPOUND_STRING( "A chipped teacup\n" @@ -12261,7 +12267,7 @@ const struct Item gItemsInfo[] = [ITEM_CORNERSTONE_MASK] = { - .name = _("CornrstneMask"), + .name = HANDLE_EXPANDED_ITEM_NAME("CornrstneMask", "Cornerstone Mask"), .price = 0, .description = COMPOUND_STRING( "Allows Ogerpon to\n" @@ -12274,7 +12280,7 @@ const struct Item gItemsInfo[] = [ITEM_WELLSPRING_MASK] = { - .name = _("WellsprngMask"), + .name = HANDLE_EXPANDED_ITEM_NAME("WellsprngMask", "Wellspring Mask"), .price = 0, .description = COMPOUND_STRING( "Allows Ogerpon to\n" @@ -12287,7 +12293,7 @@ const struct Item gItemsInfo[] = [ITEM_HEARTHFLAME_MASK] = { - .name = _("HrthflameMask"), + .name = HANDLE_EXPANDED_ITEM_NAME("HrthflameMask", "Hearthflame Mask"), .price = 0, .description = COMPOUND_STRING( "Allows Ogerpon to\n" @@ -12378,8 +12384,8 @@ const struct Item gItemsInfo[] = [ITEM_FRESH_START_MOCHI] = { - .name = _("FrshStrtMochi"), - .pluralName = _("FrshStrtMochi"), + .name = HANDLE_EXPANDED_ITEM_NAME("FrshStrtMochi", "Fresh Start Mochi"), + .pluralName = HANDLE_EXPANDED_ITEM_NAME("FrshStrtMochi", "Fresh Start Mochi"), .price = 300, .description = COMPOUND_STRING( "An item that resets\n" @@ -12394,7 +12400,7 @@ const struct Item gItemsInfo[] = [ITEM_GLIMMERING_CHARM] = { - .name = _("GlmmringCharm"), + .name = HANDLE_EXPANDED_ITEM_NAME("GlmmringCharm", "Glimmering Charm"), .price = 0, .importance = 1, .description = COMPOUND_STRING( @@ -12422,7 +12428,7 @@ const struct Item gItemsInfo[] = [ITEM_STELLAR_TERA_SHARD] = { - .name = _("StllrTeraShrd"), + .name = HANDLE_EXPANDED_ITEM_NAME("StllrTeraShrd", "Stellar Tera Shard"), .price = 0, .description = sTeraShardDesc, .pocket = POCKET_ITEMS, @@ -12432,7 +12438,7 @@ const struct Item gItemsInfo[] = [ITEM_JUBILIFE_MUFFIN] = { - .name = _("JublifeMuffin"), + .name = HANDLE_EXPANDED_ITEM_NAME("JublifeMuffin", "Jubilife Muffin"), .price = 250, .description = sFullHealDesc, .pocket = POCKET_ITEMS, @@ -12560,7 +12566,7 @@ const struct Item gItemsInfo[] = [ITEM_AUX_POWERGUARD] = { - .name = _("AuxPowerguard"), + .name = HANDLE_EXPANDED_ITEM_NAME("AuxPowerguard", "Aux Powerguard"), .price = 1200, .holdEffectParam = X_ITEM_STAGES, .description = COMPOUND_STRING( @@ -12581,7 +12587,7 @@ const struct Item gItemsInfo[] = [ITEM_CHOICE_DUMPLING] = { - .name = _("ChoiceDumplng"), + .name = HANDLE_EXPANDED_ITEM_NAME("ChoiceDumplng", "Choice Dumpling"), .price = 1200, .description = sQuestionMarksDesc, .pocket = POCKET_ITEMS, @@ -12603,7 +12609,7 @@ const struct Item gItemsInfo[] = [ITEM_TWICE_SPICED_RADISH] = { - .name = _("2xSpicedRadsh"), + .name = HANDLE_EXPANDED_ITEM_NAME("2xSpicedRadsh", "Twice-Spiced Radish"), .price = 1600, .description = sQuestionMarksDesc, .pocket = POCKET_ITEMS, From aab9678b0272216188240b392eb9fe05f833812e Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 16 Mar 2024 15:26:03 +0000 Subject: [PATCH 26/71] Enable GF item names by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the Pokémon Storage System, even FONT_SMALL_NARROWER isn't sufficient to prevent clipping in all cases. e.g. Unremarkable Teacup clips. We have decided to accept that cost to make the rest of the user experience better, but downstream projects that don't like that trade-off can either a) alter the Pokémon Storage System UI, or b) set I_EXPANDED_ITEM_NAMES to FALSE. --- include/config/item.h | 2 +- include/config/test.h | 2 + include/item.h | 4 +- src/item.c | 26 +-- src/item_menu.c | 39 ++--- src/player_pc.c | 14 +- src/pokemon_storage_system.c | 4 +- src/pokemon_summary_screen.c | 6 +- src/shop.c | 3 +- src/strings.c | 16 +- test/battle/hold_effect/safety_goggles.c | 2 +- test/battle/move_effect/embargo.c | 2 +- test/battle/move_effect/knock_off.c | 4 +- test/text.c | 194 +++++++++++++++++++++++ 14 files changed, 264 insertions(+), 54 deletions(-) diff --git a/include/config/item.h b/include/config/item.h index 2f7db2ed92..644a73ade6 100644 --- a/include/config/item.h +++ b/include/config/item.h @@ -2,7 +2,7 @@ #define GUARD_CONFIG_ITEM_H // Item config -#define I_EXPANDED_ITEM_NAMES FALSE // If set to TRUE, item names are increased from 14 characters to 20 characters. +#define I_EXPANDED_ITEM_NAMES TRUE // If set to FALSE, item names are decreased from 20 characters to 14 characters. #define I_SHINY_CHARM_ADDITIONAL_ROLLS 2 // Amount of additional shiny rolls if the player has the Shiny Charm. Set it to 0 to disable Shiny Charm's effects. #define I_KEY_FOSSILS GEN_LATEST // In Gen4+, all Gen 3 fossils became regular items. #define I_KEY_ESCAPE_ROPE GEN_LATEST // In Gen8, Escape Rope became a Key Item. Keep in mind, this will make it free to buy in marts. diff --git a/include/config/test.h b/include/config/test.h index 9d8066864a..8dba50f8a7 100644 --- a/include/config/test.h +++ b/include/config/test.h @@ -3,5 +3,7 @@ #undef B_EXPANDED_MOVE_NAMES #define B_EXPANDED_MOVE_NAMES TRUE +#undef I_EXPANDED_ITEM_NAMES +#define I_EXPANDED_ITEM_NAMES TRUE #endif // GUARD_CONFIG_TEST_H diff --git a/include/item.h b/include/item.h index 53121d1127..88c9fe5e3f 100644 --- a/include/item.h +++ b/include/item.h @@ -37,8 +37,8 @@ extern struct BagPocket gBagPockets[]; void ApplyNewEncryptionKeyToBagItems(u32 newKey); void ApplyNewEncryptionKeyToBagItems_(u32 newKey); void SetBagItemsPointers(void); -void CopyItemName(u16 itemId, u8 *dst); -void CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity); +u8 *CopyItemName(u16 itemId, u8 *dst); +u8 *CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity); bool8 IsBagPocketNonEmpty(u8 pocket); bool8 CheckBagHasItem(u16 itemId, u16 count); bool8 HasAtLeastOneBerry(void); diff --git a/src/item.c b/src/item.c index c9318ee4a7..4e903b4883 100644 --- a/src/item.c +++ b/src/item.c @@ -81,24 +81,28 @@ void SetBagItemsPointers(void) gBagPockets[BERRIES_POCKET].capacity = BAG_BERRIES_COUNT; } -void CopyItemName(u16 itemId, u8 *dst) +u8 *CopyItemName(u16 itemId, u8 *dst) { - StringCopy(dst, ItemId_GetName(itemId)); + return StringCopy(dst, ItemId_GetName(itemId)); } const u8 sText_s[] =_("s"); -void CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity) +u8 *CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity) { - u8 *end = StringCopy(dst, ItemId_GetName(itemId)) - 1; - - if (quantity < 2) - return; - - if (DoesItemHavePluralName(itemId)) - StringCopy(dst, ItemId_GetPluralName(itemId)); + if (quantity == 1) + { + return StringCopy(dst, ItemId_GetName(itemId)); + } + else if (DoesItemHavePluralName(itemId)) + { + return StringCopy(dst, ItemId_GetPluralName(itemId)); + } else - StringAppend(end, sText_s); + { + u8 *end = StringCopy(dst, ItemId_GetName(itemId)); + return StringCopy(end, sText_s); + } } bool8 IsBagPocketNonEmpty(u8 pocket) diff --git a/src/item_menu.c b/src/item_menu.c index dde142d021..ad9fc6f4bc 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -109,7 +109,7 @@ struct ListBuffer1 { }; struct ListBuffer2 { - u8 name[MAX_POCKET_ITEMS][max(ITEM_NAME_LENGTH, MOVE_NAME_LENGTH + 3) + 10]; + u8 name[MAX_POCKET_ITEMS][max(ITEM_NAME_LENGTH, MOVE_NAME_LENGTH) + 15]; }; struct TempWallyBag { @@ -905,19 +905,12 @@ static void LoadBagItemListBuffers(u8 pocketId) static void GetItemName(u8 *dest, u16 itemId) { - u32 fontId; + u8 *end; switch (gBagPosition.pocket) { case TMHM_POCKET: - StringCopy(gStringVar2, GetMoveName(ItemIdToBattleMoveId(itemId))); - fontId = GetFontIdToFit(gStringVar2, FONT_NARROW, 0, 73); - if (fontId != FONT_NARROW) - { - gStringVar2[0] = EXT_CTRL_CODE_BEGIN; - gStringVar2[1] = EXT_CTRL_CODE_FONT; - gStringVar2[2] = fontId; - StringCopy(&gStringVar2[3], GetMoveName(ItemIdToBattleMoveId(itemId))); - } + end = StringCopy(gStringVar2, GetMoveName(ItemIdToBattleMoveId(itemId))); + PrependFontIdToFit(gStringVar2, end, FONT_NARROW, 73); if (itemId >= ITEM_HM01) { // Get HM number @@ -933,11 +926,13 @@ static void GetItemName(u8 *dest, u16 itemId) break; case BERRIES_POCKET: ConvertIntToDecimalStringN(gStringVar1, itemId - FIRST_BERRY_INDEX + 1, STR_CONV_MODE_LEADING_ZEROS, 2); - CopyItemName(itemId, gStringVar2); + end = CopyItemName(itemId, gStringVar2); + PrependFontIdToFit(gStringVar2, end, FONT_NARROW, 73); StringExpandPlaceholders(dest, gText_NumberItem_TMBerry); break; default: - CopyItemName(itemId, dest); + end = CopyItemName(itemId, dest); + PrependFontIdToFit(dest, end, FONT_NARROW, 88); break; } } @@ -1676,7 +1671,8 @@ static void OpenContextMenu(u8 taskId) } else { - CopyItemName(gSpecialVar_ItemId, gStringVar1); + u8 *end = CopyItemName(gSpecialVar_ItemId, gStringVar1); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(WIN_DESCRIPTION) - 10 - 6); StringExpandPlaceholders(gStringVar4, gText_Var1IsSelected); FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0)); BagMenu_Print(WIN_DESCRIPTION, FONT_NORMAL, gStringVar4, 3, 1, 0, 0, 0, COLORID_NORMAL); @@ -1842,7 +1838,8 @@ static void ItemMenu_Toss(u8 taskId) } else { - CopyItemName(gSpecialVar_ItemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSpecialVar_ItemId, gStringVar1, 2); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(WIN_DESCRIPTION) - 10 - 6); StringExpandPlaceholders(gStringVar4, gText_TossHowManyVar1s); FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0)); BagMenu_Print(WIN_DESCRIPTION, FONT_NORMAL, gStringVar4, 3, 1, 0, 0, 0, COLORID_NORMAL); @@ -1855,7 +1852,8 @@ static void AskTossItems(u8 taskId) { s16 *data = gTasks[taskId].data; - CopyItemName(gSpecialVar_ItemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSpecialVar_ItemId, gStringVar1, tItemCount); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(WIN_DESCRIPTION) - 10 - 6); ConvertIntToDecimalStringN(gStringVar2, tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_ITEM_DIGITS); StringExpandPlaceholders(gStringVar4, gText_ConfirmTossItems); FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0)); @@ -1898,7 +1896,8 @@ static void ConfirmToss(u8 taskId) { s16 *data = gTasks[taskId].data; - CopyItemName(gSpecialVar_ItemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSpecialVar_ItemId, gStringVar1, tItemCount); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(WIN_DESCRIPTION) - 10 - 6); ConvertIntToDecimalStringN(gStringVar2, tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_ITEM_DIGITS); StringExpandPlaceholders(gStringVar4, gText_ThrewAwayVar2Var1s); FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0)); @@ -2235,7 +2234,8 @@ static void Task_ItemContext_Deposit(u8 taskId) } else { - CopyItemName(gSpecialVar_ItemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSpecialVar_ItemId, gStringVar1, 2); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(WIN_DESCRIPTION) - 10 - 6); StringExpandPlaceholders(gStringVar4, gText_DepositHowManyVar1); FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0)); BagMenu_Print(WIN_DESCRIPTION, FONT_NORMAL, gStringVar4, 3, 1, 0, 0, 0, COLORID_NORMAL); @@ -2282,7 +2282,8 @@ static void TryDepositItem(u8 taskId) else if (AddPCItem(gSpecialVar_ItemId, tItemCount) == TRUE) { // Successfully deposited - CopyItemName(gSpecialVar_ItemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSpecialVar_ItemId, gStringVar1, tItemCount); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(WIN_DESCRIPTION) - 10 - 6); ConvertIntToDecimalStringN(gStringVar2, tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_ITEM_DIGITS); StringExpandPlaceholders(gStringVar4, gText_DepositedVar2Var1s); BagMenu_Print(WIN_DESCRIPTION, FONT_NORMAL, gStringVar4, 3, 1, 0, 0, 0, COLORID_NORMAL); diff --git a/src/player_pc.c b/src/player_pc.c index a10cb2ac2b..3dd5fcc36f 100644 --- a/src/player_pc.c +++ b/src/player_pc.c @@ -293,6 +293,7 @@ static const struct ListMenuTemplate sListMenuTemplate_ItemStorage = .scrollMultiple = LIST_NO_MULTIPLE_SCROLL, .fontId = FONT_NARROW, .cursorKind = CURSOR_BLACK_ARROW, + .textNarrowWidth = 74, }; static const struct WindowTemplate sWindowTemplates_ItemStorage[ITEMPC_WIN_COUNT] = @@ -1349,6 +1350,7 @@ static void ItemStorage_PrintItemQuantity(u8 windowId, u16 value, u32 mode, u8 x // Start an item Withdraw/Toss static void ItemStorage_DoItemAction(u8 taskId) { + u8 *end; s16 *data = gTasks[taskId].data; u16 pos = gPlayerPCItemPageInfo.cursorPos + gPlayerPCItemPageInfo.itemsAbove; ItemStorage_RemoveScrollIndicator(); @@ -1364,7 +1366,8 @@ static void ItemStorage_DoItemAction(u8 taskId) } // Withdrawing multiple items, show "how many" message - CopyItemName(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1); + end = CopyItemNameHandlePlural(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1, 2); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(ITEMPC_WIN_MESSAGE) - 6); ItemStorage_PrintMessage(ItemStorage_GetMessage(MSG_HOW_MANY_TO_WITHDRAW)); } else @@ -1377,7 +1380,8 @@ static void ItemStorage_DoItemAction(u8 taskId) } // Tossing multiple items, show "how many" message - CopyItemName(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1); + end = CopyItemNameHandlePlural(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1, 2); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(ITEMPC_WIN_MESSAGE) - 6); ItemStorage_PrintMessage(ItemStorage_GetMessage(MSG_HOW_MANY_TO_TOSS)); } @@ -1426,7 +1430,8 @@ static void ItemStorage_DoItemWithdraw(u8 taskId) if (AddBagItem(gSaveBlock1Ptr->pcItems[pos].itemId, tQuantity) == TRUE) { // Item withdrawn - CopyItemName(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1, tQuantity); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(ITEMPC_WIN_MESSAGE) - 6); ConvertIntToDecimalStringN(gStringVar2, tQuantity, STR_CONV_MODE_LEFT_ALIGN, 3); ItemStorage_PrintMessage(ItemStorage_GetMessage(MSG_WITHDREW_ITEM)); gTasks[taskId].func = ItemStorage_HandleRemoveItem; @@ -1448,7 +1453,8 @@ static void ItemStorage_DoItemToss(u8 taskId) if (!ItemId_GetImportance(gSaveBlock1Ptr->pcItems[pos].itemId)) { // Show toss confirmation prompt - CopyItemName(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1); + u8 *end = CopyItemNameHandlePlural(gSaveBlock1Ptr->pcItems[pos].itemId, gStringVar1, tQuantity); + WrapFontIdToFit(gStringVar1, end, FONT_NORMAL, WindowWidthPx(ITEMPC_WIN_MESSAGE) - 6); ConvertIntToDecimalStringN(gStringVar2, tQuantity, STR_CONV_MODE_LEFT_ALIGN, 3); ItemStorage_PrintMessage(ItemStorage_GetMessage(MSG_OKAY_TO_THROW_AWAY)); CreateYesNoMenuWithCallbacks(taskId, &sWindowTemplates_ItemStorage[ITEMPC_WIN_YESNO], 1, 0, 1, 0x214, 0xE, &ItemTossYesNoFuncs); diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index 013f7e21b4..a079a10c24 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -4031,11 +4031,11 @@ static void PrintDisplayMonInfo(void) AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_NORMAL, sStorage->displayMonNameText, 6, 0, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonSpeciesName, 6, 15, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 29, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SMALL, sStorage->displayMonItemName, 6, 43, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonItemName, FONT_SMALL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 22), sStorage->displayMonItemName, 6, 43, TEXT_SKIP_DRAW, NULL); } else { - AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SMALL, sStorage->displayMonItemName, 6, 0, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonItemName, FONT_SMALL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 22), sStorage->displayMonItemName, 6, 0, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_NORMAL, sStorage->displayMonNameText, 6, 13, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonSpeciesName, 6, 28, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 42, TEXT_SKIP_DRAW, NULL); diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index ee2d0e9853..1989913fc2 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -3434,6 +3434,7 @@ static void Task_PrintSkillsPage(u8 taskId) static void PrintHeldItemName(void) { const u8 *text; + u32 fontId; int x; if (sMonSummaryScreen->summary.item == ITEM_ENIGMA_BERRY_E_READER @@ -3452,8 +3453,9 @@ static void PrintHeldItemName(void) text = gStringVar1; } - x = GetStringCenterAlignXOffset(FONT_NORMAL, text, 72) + 6; - PrintTextOnWindow(AddWindowFromTemplateList(sPageSkillsTemplate, PSS_DATA_WINDOW_SKILLS_HELD_ITEM), text, x, 1, 0, 0); + fontId = GetFontIdToFit(text, FONT_NORMAL, 0, WindowTemplateWidthPx(&sPageSkillsTemplate[PSS_DATA_WINDOW_SKILLS_HELD_ITEM]) - 8); + x = GetStringCenterAlignXOffset(fontId, text, 72) + 6; + PrintTextOnWindowWithFont(AddWindowFromTemplateList(sPageSkillsTemplate, PSS_DATA_WINDOW_SKILLS_HELD_ITEM), text, x, 1, 0, 0, fontId); } static void PrintRibbonCount(void) diff --git a/src/shop.c b/src/shop.c index 88d66a43e9..e517c21155 100644 --- a/src/shop.c +++ b/src/shop.c @@ -216,7 +216,8 @@ static const struct ListMenuTemplate sShopBuyMenuListTemplate = .itemVerticalPadding = 0, .scrollMultiple = LIST_NO_MULTIPLE_SCROLL, .fontId = FONT_NARROW, - .cursorKind = CURSOR_BLACK_ARROW + .cursorKind = CURSOR_BLACK_ARROW, + .textNarrowWidth = 84, }; static const struct BgTemplate sShopBuyMenuBgTemplates[] = diff --git a/src/strings.c b/src/strings.c index 4fde380240..84c8112681 100644 --- a/src/strings.c +++ b/src/strings.c @@ -221,20 +221,20 @@ const u8 gText_xVar1[] = _("×{STR_VAR_1}"); const u8 gText_Berry2[] = _(" BERRY"); // Unused const u8 gText_Coins[] = _("{STR_VAR_1} COINS"); const u8 gText_CloseBag[] = _("CLOSE BAG"); -const u8 gText_Var1IsSelected[] = _("{STR_VAR_1} is\nselected."); +const u8 gText_Var1IsSelected[] = _("{STR_VAR_1}\nis selected."); const u8 gText_CantWriteMail[] = _("You can't write\nMAIL here."); const u8 gText_NoPokemon[] = _("There is no\nPOKéMON."); const u8 gText_MoveVar1Where[] = _("Move the\n{STR_VAR_1}\nwhere?"); const u8 gText_Var1CantBeHeld[] = _("The {STR_VAR_1} can't be held."); const u8 gText_Var1CantBeHeldHere[] = _("The {STR_VAR_1} can't be held\nhere."); -const u8 gText_DepositHowManyVar1[] = _("Deposit how many\n{STR_VAR_1}(s)?"); -const u8 gText_DepositedVar2Var1s[] = _("Deposited {STR_VAR_2}\n{STR_VAR_1}(s)."); +const u8 gText_DepositHowManyVar1[] = _("Deposit how many\n{STR_VAR_1}?"); +const u8 gText_DepositedVar2Var1s[] = _("Deposited {STR_VAR_2}\n{STR_VAR_1}."); const u8 gText_NoRoomForItems[] = _("There's no room to\nstore items."); const u8 gText_CantStoreImportantItems[] = _("Important items\ncan't be stored in\nthe PC!"); const u8 gText_TooImportantToToss[] = _("That's much too\nimportant to toss\nout!"); -const u8 gText_TossHowManyVar1s[] = _("Toss out how many\n{STR_VAR_1}(s)?"); -const u8 gText_ThrewAwayVar2Var1s[] = _("Threw away {STR_VAR_2}\n{STR_VAR_1}(s)."); -const u8 gText_ConfirmTossItems[] = _("Is it okay to\nthrow away {STR_VAR_2}\n{STR_VAR_1}(s)?"); +const u8 gText_TossHowManyVar1s[] = _("Toss out how many\n{STR_VAR_1}?"); +const u8 gText_ThrewAwayVar2Var1s[] = _("Threw away {STR_VAR_2}\n{STR_VAR_1}."); +const u8 gText_ConfirmTossItems[] = _("Is it okay to\nthrow away {STR_VAR_2}\n{STR_VAR_1}?"); const u8 gText_DadsAdvice[] = _("DAD's advice…\n{PLAYER}, there's a time and place for\leverything!{PAUSE_UNTIL_PRESS}"); const u8 gText_CantDismountBike[] = _("You can't dismount your BIKE here.{PAUSE_UNTIL_PRESS}"); const u8 gText_ItemFinderNearby[] = _("Huh?\nThe ITEMFINDER's responding!\pThere's an item buried around here!{PAUSE_UNTIL_PRESS}"); @@ -597,8 +597,8 @@ const u8 gText_TakeOutItemsFromPC[] = _("Take out items from the PC."); const u8 gText_ThrowAwayItemsInPC[] = _("Throw away items stored in the PC."); const u8 gText_NoItems[] = _("There are no items.{PAUSE_UNTIL_PRESS}"); const u8 gText_NoRoomInBag[] = _("There is no more\nroom in the BAG."); -const u8 gText_WithdrawHowManyItems[] = _("Withdraw how many\n{STR_VAR_1}(s)?"); -const u8 gText_WithdrawXItems[] = _("Withdrew {STR_VAR_2}\n{STR_VAR_1}(s)."); +const u8 gText_WithdrawHowManyItems[] = _("Withdraw how many\n{STR_VAR_1}?"); +const u8 gText_WithdrawXItems[] = _("Withdrew {STR_VAR_2}\n{STR_VAR_1}."); const u8 gText_Read[] = _("READ"); const u8 gText_MoveToBag[] = _("MOVE TO BAG"); const u8 gText_Give2[] = _("GIVE"); diff --git a/test/battle/hold_effect/safety_goggles.c b/test/battle/hold_effect/safety_goggles.c index b20f19e0a5..4c5c6a2965 100644 --- a/test/battle/hold_effect/safety_goggles.c +++ b/test/battle/hold_effect/safety_goggles.c @@ -16,7 +16,7 @@ SINGLE_BATTLE_TEST("Safety Goggles block powder and spore moves") TURN { MOVE(player, MOVE_STUN_SPORE); } } SCENE { NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); - MESSAGE("Foe Abra is not affected thanks to its SafetyGoggles!"); + MESSAGE("Foe Abra is not affected thanks to its Safety Goggles!"); } } diff --git a/test/battle/move_effect/embargo.c b/test/battle/move_effect/embargo.c index 8d9cc9c86d..0d69496efc 100644 --- a/test/battle/move_effect/embargo.c +++ b/test/battle/move_effect/embargo.c @@ -352,7 +352,7 @@ SINGLE_BATTLE_TEST("Embargo doesn't prevent Mega Evolution") ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, opponent); MESSAGE("2 sent out Charizard!"); // Turn 3 - MESSAGE("Foe Charizard's CharizarditeY is reacting to 2's Mega Ring!"); + MESSAGE("Foe Charizard's Charizardite Y is reacting to 2's Mega Ring!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); MESSAGE("Foe Charizard has Mega Evolved into Mega Charizard!"); } diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c index 177e3b18c1..49e5c499d3 100644 --- a/test/battle/move_effect/knock_off.c +++ b/test/battle/move_effect/knock_off.c @@ -43,8 +43,8 @@ SINGLE_BATTLE_TEST("Knock Off activates after Rocky Helmet and Weakness Policy") ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); if (item == ITEM_WEAKNESS_POLICY) { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE); - MESSAGE("Using WeaknssPolicy, the Attack of Foe Wobbuffet sharply rose!"); - MESSAGE("Using WeaknssPolicy, the Sp. Atk of Foe Wobbuffet sharply rose!"); + MESSAGE("Using Weakness Policy, the Attack of Foe Wobbuffet sharply rose!"); + MESSAGE("Using Weakness Policy, the Sp. Atk of Foe Wobbuffet sharply rose!"); } else if (item == ITEM_ROCKY_HELMET) { HP_BAR(player); MESSAGE("Wobbuffet was hurt by Foe Wobbuffet's Rocky Helmet!"); diff --git a/test/text.c b/test/text.c index ee81386c00..f42511ee92 100644 --- a/test/text.c +++ b/test/text.c @@ -2,6 +2,7 @@ #include "test/test.h" #include "item.h" #include "text.h" +#include "constants/items.h" #include "constants/moves.h" TEST("Move names fit on Pokemon Summary Screen") @@ -75,3 +76,196 @@ TEST("Move names fit on Move Relearner Screen") } EXPECT_LE(GetStringWidth(fontId, gMovesInfo[move].name, 0), widthPx); } + +TEST("Item names fit on Bag Screen (list)") +{ + u32 i; + const u32 fontId = FONT_NARROWER; + const u32 tmHmBerryWidthPx = 71, restWidthPx = 88; + u32 item = ITEM_NONE; + for (i = 1; i < ITEMS_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gItemsInfo[i].name) { item = i; } + } + if (gItemsInfo[item].pocket == POCKET_TM_HM || gItemsInfo[item].pocket == POCKET_BERRIES) + EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), tmHmBerryWidthPx); + else + EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), restWidthPx); +} + +TEST("Item plural names fit on Bag Screen (left box)") +{ + u32 i; + // -6 for the question mark in FONT_NORMAL. + const u32 fontId = FONT_NARROWER, widthPx = 102 - 6; + u32 item = ITEM_NONE; + u8 pluralName[ITEM_NAME_PLURAL_LENGTH + 1]; + for (i = 1; i < ITEMS_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gItemsInfo[i].name) { item = i; } + } + CopyItemNameHandlePlural(item, pluralName, 2); + EXPECT_LE(GetStringWidth(fontId, pluralName, 0), widthPx); +} + +TEST("Item plural names fit on PC storage (left box)") +{ + u32 i; + // -6 for the question mark in FONT_NORMAL. + const u32 fontId = FONT_NARROWER, widthPx = 104 - 6; + u32 item = ITEM_NONE; + u8 pluralName[ITEM_NAME_PLURAL_LENGTH + 1]; + for (i = 1; i < ITEMS_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gItemsInfo[i].name) { item = i; } + } + CopyItemNameHandlePlural(item, pluralName, 2); + EXPECT_LE(GetStringWidth(fontId, pluralName, 0), widthPx); +} + +TEST("Item names fit on Pokemon Storage System") +{ + u32 i; + const u32 fontId = FONT_SMALL_NARROWER, widthPx = 50; + u32 item = ITEM_NONE; + for (i = 1; i < ITEMS_COUNT; i++) + { + if (gItemsInfo[i].importance) continue; + PARAMETRIZE_LABEL("%S", gItemsInfo[i].name) { item = i; } + } + // All items explicitly listed here are too big to fit. The ones + // with a hold effect are listed at the bottom in case you want to + // focus on making them fit (they are the most likely to appear on + // the storage system UI, along with anything that could be held + // in the wild). + switch (item) + { + case ITEM_ENERGY_POWDER: + case ITEM_PEWTER_CRUNCHIES: + case ITEM_RAGE_CANDY_BAR: + case ITEM_LUMIOSE_GALETTE: + case ITEM_HEALTH_FEATHER: + case ITEM_MUSCLE_FEATHER: + case ITEM_RESIST_FEATHER: + case ITEM_GENIUS_FEATHER: + case ITEM_CLEVER_FEATHER: + case ITEM_ABILITY_CAPSULE: + case ITEM_DYNAMAX_CANDY: + case ITEM_MAX_MUSHROOMS: + case ITEM_GOLD_BOTTLE_CAP: + case ITEM_PRETTY_FEATHER: + case ITEM_STRANGE_SOUVENIR: + case ITEM_FOSSILIZED_BIRD: + case ITEM_FOSSILIZED_FISH: + case ITEM_FOSSILIZED_DRAKE: + case ITEM_FOSSILIZED_DINO: + case ITEM_SURPRISE_MULCH: + case ITEM_YELLOW_APRICORN: + case ITEM_GREEN_APRICORN: + case ITEM_WHITE_APRICORN: + case ITEM_BLACK_APRICORN: + case ITEM_THUNDER_STONE: + case ITEM_GALARICA_WREATH: + case ITEM_STRAWBERRY_SWEET: + case ITEM_AUSPICIOUS_ARMOR: + case ITEM_BIG_BAMBOO_SHOOT: + case ITEM_GIMMIGHOUL_COIN: + case ITEM_LEADERS_CREST: + case ITEM_MALICIOUS_ARMOR: + case ITEM_TINY_BAMBOO_SHOOT: + case ITEM_BUG_TERA_SHARD: + case ITEM_DARK_TERA_SHARD: + case ITEM_DRAGON_TERA_SHARD: + case ITEM_ELECTRIC_TERA_SHARD: + case ITEM_FAIRY_TERA_SHARD: + case ITEM_FIGHTING_TERA_SHARD: + case ITEM_FIRE_TERA_SHARD: + case ITEM_FLYING_TERA_SHARD: + case ITEM_GHOST_TERA_SHARD: + case ITEM_GRASS_TERA_SHARD: + case ITEM_GROUND_TERA_SHARD: + case ITEM_ICE_TERA_SHARD: + case ITEM_NORMAL_TERA_SHARD: + case ITEM_POISON_TERA_SHARD: + case ITEM_PSYCHIC_TERA_SHARD: + case ITEM_ROCK_TERA_SHARD: + case ITEM_STEEL_TERA_SHARD: + case ITEM_WATER_TERA_SHARD: + case ITEM_BLACK_AUGURITE: + case ITEM_UNREMARKABLE_TEACUP: + case ITEM_MASTERPIECE_TEACUP: + case ITEM_FRESH_START_MOCHI: + case ITEM_STELLAR_TERA_SHARD: + case ITEM_JUBILIFE_MUFFIN: + case ITEM_SUPERB_REMEDY: + case ITEM_AUX_POWERGUARD: + case ITEM_CHOICE_DUMPLING: + case ITEM_TWICE_SPICED_RADISH: + // Items with hold effects: + case ITEM_ELECTRIC_MEMORY: + case ITEM_FIGHTING_MEMORY: + case ITEM_GROUND_MEMORY: + case ITEM_PSYCHIC_MEMORY: + case ITEM_DRAGON_MEMORY: + case ITEM_CHARIZARDITE_X: + case ITEM_CHARIZARDITE_Y: + case ITEM_ULTRANECROZIUM_Z: + case ITEM_DEEP_SEA_SCALE: + case ITEM_DEEP_SEA_TOOTH: + case ITEM_NEVER_MELT_ICE: + case ITEM_WEAKNESS_POLICY: + case ITEM_SAFETY_GOGGLES: + case ITEM_ADRENALINE_ORB: + case ITEM_TERRAIN_EXTENDER: + case ITEM_PROTECTIVE_PADS: + case ITEM_HEAVY_DUTY_BOOTS: + case ITEM_UTILITY_UMBRELLA: + case ITEM_MARANGA_BERRY: + case ITEM_PUNCHING_GLOVE: + case ITEM_BOOSTER_ENERGY: + case ITEM_ADAMANT_CRYSTAL: + case ITEM_LUSTROUS_GLOBE: + case ITEM_CORNERSTONE_MASK: + case ITEM_WELLSPRING_MASK: + case ITEM_HEARTHFLAME_MASK: + EXPECT_GT(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); + break; + default: + EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); + break; + } +} + +TEST("Item names fit on Pokemon Summary Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 72; + u32 item = ITEM_NONE; + for (i = 1; i < ITEMS_COUNT; i++) + { + if (gItemsInfo[i].importance) continue; + PARAMETRIZE_LABEL("%S", gItemsInfo[i].name) { item = i; } + } + // All items explicitly listed here are too big to fit. + switch (item) + { + case ITEM_UNREMARKABLE_TEACUP: + EXPECT_GT(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); + break; + default: + EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); + break; + } +} + +TEST("Item names fit on Shop Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 84; + u32 item = ITEM_NONE; + for (i = 1; i < ITEMS_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gItemsInfo[i].name) { item = i; } + } + EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); +} From 2ec35549a57b5621a3716d29e7cb719ddc80e5d5 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Fri, 29 Mar 2024 17:43:17 +0000 Subject: [PATCH 27/71] VANILLA_POKEMON_NAME_LENGTH --- include/constants/global.h | 1 + include/daycare.h | 2 +- include/global.h | 8 ++++---- include/global.tv.h | 10 +++++----- include/pokemon.h | 1 + src/battle_tower.c | 2 +- src/contest.c | 4 ++-- src/daycare.c | 8 ++++---- src/egg_hatch.c | 9 +++++---- src/hall_of_fame.c | 4 ++-- src/pokemon.c | 22 ++++++++++++++++------ src/tv.c | 8 ++++---- 12 files changed, 46 insertions(+), 33 deletions(-) diff --git a/include/constants/global.h b/include/constants/global.h index 5118beb189..80378f893a 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -105,6 +105,7 @@ #define ITEM_NAME_LENGTH ((I_EXPANDED_ITEM_NAMES == TRUE) ? 20 : 14) #define ITEM_NAME_PLURAL_LENGTH ITEM_NAME_LENGTH + 2 // 2 is used for the instance where a word's suffix becomes y->ies #define POKEMON_NAME_LENGTH 10 +#define VANILLA_POKEMON_NAME_LENGTH 10 #define POKEMON_NAME_BUFFER_SIZE max(20, POKEMON_NAME_LENGTH + 1) // Frequently used buffer size. Larger than necessary #define PLAYER_NAME_LENGTH 7 #define MAIL_WORDS_COUNT 9 diff --git a/include/daycare.h b/include/daycare.h index daea812bed..2747a66850 100644 --- a/include/daycare.h +++ b/include/daycare.h @@ -10,7 +10,7 @@ struct RecordMixingDaycareMail bool16 cantHoldItem[DAYCARE_MON_COUNT]; }; -u8 *GetMonNickname2(struct Pokemon *mon, u8 *dest); +u8 *GetMonNicknameVanilla(struct Pokemon *mon, u8 *dest); u8 *GetBoxMonNickname(struct BoxPokemon *mon, u8 *dest); u8 CountPokemonInDaycare(struct DayCare *daycare); void InitDaycareMailRecordMixing(struct DayCare *daycare, struct RecordMixingDaycareMail *mixMail); diff --git a/include/global.h b/include/global.h index 83bff49c0c..cbafe29596 100644 --- a/include/global.h +++ b/include/global.h @@ -285,7 +285,7 @@ struct BattleTowerPokemon u32 gap:1; u32 abilityNum:1; u32 personality; - u8 nickname[POKEMON_NAME_LENGTH + 1]; + u8 nickname[VANILLA_POKEMON_NAME_LENGTH + 1]; u8 friendship; }; @@ -310,7 +310,7 @@ struct BattleTowerInterview u16 playerSpecies; u16 opponentSpecies; u8 opponentName[PLAYER_NAME_LENGTH + 1]; - u8 opponentMonNickname[POKEMON_NAME_LENGTH + 1]; + u8 opponentMonNickname[VANILLA_POKEMON_NAME_LENGTH + 1]; u8 opponentLanguage; }; @@ -744,7 +744,7 @@ struct ContestWinner u32 trainerId; u16 species; u8 contestCategory; - u8 monName[POKEMON_NAME_LENGTH + 1]; + u8 monName[VANILLA_POKEMON_NAME_LENGTH + 1]; u8 trainerName[PLAYER_NAME_LENGTH + 1]; u8 contestRank:7; bool8 isShiny:1; @@ -764,7 +764,7 @@ struct DaycareMail { struct Mail message; u8 otName[PLAYER_NAME_LENGTH + 1]; - u8 monName[POKEMON_NAME_LENGTH + 1]; + u8 monName[VANILLA_POKEMON_NAME_LENGTH + 1]; u8 gameLanguage:4; u8 monLanguage:4; }; diff --git a/include/global.tv.h b/include/global.tv.h index 9c3902e7cc..6bc6d08f86 100644 --- a/include/global.tv.h +++ b/include/global.tv.h @@ -82,7 +82,7 @@ typedef union // size = 0x24 /*0x00*/ u8 kind; /*0x01*/ bool8 active; /*0x02*/ u16 species; - /*0x04*/ u8 pokemonName[POKEMON_NAME_LENGTH + 1]; + /*0x04*/ u8 pokemonName[VANILLA_POKEMON_NAME_LENGTH + 1]; /*0x0F*/ u8 trainerName[PLAYER_NAME_LENGTH + 1]; /*0x17*/ u8 unused[3]; /*0x1A*/ u8 random; @@ -98,7 +98,7 @@ typedef union // size = 0x24 /*0x01*/ bool8 active; /*0x02*/ u16 species; /*0x04*/ u16 words[2]; - /*0x08*/ u8 pokemonNickname[POKEMON_NAME_LENGTH + 1]; + /*0x08*/ u8 pokemonNickname[VANILLA_POKEMON_NAME_LENGTH + 1]; /*0x13*/ u8 contestCategory:3; u8 contestRank:2; u8 contestResult:2; @@ -196,7 +196,7 @@ typedef union // size = 0x24 /*0x01*/ bool8 active; /*0x02*/ u8 playerName[PLAYER_NAME_LENGTH + 1]; /*0x0A*/ u8 contestCategory; - /*0x0B*/ u8 nickname[POKEMON_NAME_LENGTH + 1]; + /*0x0B*/ u8 nickname[VANILLA_POKEMON_NAME_LENGTH + 1]; /*0x16*/ u8 pokeblockState; /*0x17*/ u8 language; /*0x18*/ u8 pokemonNameLanguage; @@ -209,7 +209,7 @@ typedef union // size = 0x24 /*0x01*/ bool8 active; /*0x02*/ u8 language; /*0x03*/ u8 language2; - /*0x04*/ u8 nickname[POKEMON_NAME_LENGTH + 1]; + /*0x04*/ u8 nickname[VANILLA_POKEMON_NAME_LENGTH + 1]; /*0x0F*/ u8 ball; /*0x10*/ u16 species; /*0x12*/ u8 nBallsUsed; @@ -409,7 +409,7 @@ typedef union // size = 0x24 /*0x01*/ bool8 active; /*0x02*/ u8 nRibbons; /*0x03*/ u8 selectedRibbon; - /*0x04*/ u8 nickname[POKEMON_NAME_LENGTH + 1]; + /*0x04*/ u8 nickname[VANILLA_POKEMON_NAME_LENGTH + 1]; /*0x0F*/ u8 language; /*0x10*/ u8 pokemonNameLanguage; /*0x11*/ u8 filler_12[2]; diff --git a/include/pokemon.h b/include/pokemon.h index d37e5f4ae9..1f7003fe39 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -28,6 +28,7 @@ enum { MON_DATA_HP_LOST, MON_DATA_ENCRYPT_SEPARATOR, MON_DATA_NICKNAME, + MON_DATA_NICKNAME10, MON_DATA_SPECIES, MON_DATA_HELD_ITEM, MON_DATA_MOVE1, diff --git a/src/battle_tower.c b/src/battle_tower.c index 59e9a8aeaf..7cd319b113 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -2684,7 +2684,7 @@ static void SetTowerInterviewData(void) GetBattleTowerTrainerLanguage(&gSaveBlock2Ptr->frontier.towerInterview.opponentLanguage, gTrainerBattleOpponent_A); gSaveBlock2Ptr->frontier.towerInterview.opponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[1]], MON_DATA_SPECIES, NULL); gSaveBlock2Ptr->frontier.towerInterview.playerSpecies = GetMonData(&gPlayerParty[gBattlerPartyIndexes[0]], MON_DATA_SPECIES, NULL); - for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) + for (i = 0; i < VANILLA_POKEMON_NAME_LENGTH + 1; i++) gSaveBlock2Ptr->frontier.towerInterview.opponentMonNickname[i] = gBattleMons[0].nickname[i]; gSaveBlock2Ptr->frontier.towerBattleOutcome = gBattleOutcome; } diff --git a/src/contest.c b/src/contest.c index c500fda60d..4a2e4e66c4 100644 --- a/src/contest.c +++ b/src/contest.c @@ -5648,7 +5648,7 @@ bool8 SaveContestWinner(u8 rank) gSaveBlock1Ptr->contestWinners[id].personality = gContestMons[i].personality; gSaveBlock1Ptr->contestWinners[id].species = gContestMons[i].species; gSaveBlock1Ptr->contestWinners[id].trainerId = gContestMons[i].otId; - StringCopy(gSaveBlock1Ptr->contestWinners[id].monName, gContestMons[i].nickname); + StringCopyN(gSaveBlock1Ptr->contestWinners[id].monName, gContestMons[i].nickname, VANILLA_POKEMON_NAME_LENGTH); StringCopy(gSaveBlock1Ptr->contestWinners[id].trainerName, gContestMons[i].trainerName); if(gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) gSaveBlock1Ptr->contestWinners[id].contestRank = CONTEST_RANK_LINK; @@ -5667,7 +5667,7 @@ bool8 SaveContestWinner(u8 rank) gCurContestWinner.isShiny = gContestMons[i].isShiny; gCurContestWinner.trainerId = gContestMons[i].otId; gCurContestWinner.species = gContestMons[i].species; - StringCopy(gCurContestWinner.monName, gContestMons[i].nickname); + StringCopyN(gCurContestWinner.monName, gContestMons[i].nickname, VANILLA_POKEMON_NAME_LENGTH); StringCopy(gCurContestWinner.trainerName, gContestMons[i].trainerName); gCurContestWinner.contestCategory = captionId; } diff --git a/src/daycare.c b/src/daycare.c index 537311f99d..8f4e9e7d53 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -96,11 +96,11 @@ static const u8 *const sCompatibilityMessages[] = static const u8 sJapaneseEggNickname[] = _("タマゴ"); // "tamago" ("egg" in Japanese) -u8 *GetMonNickname2(struct Pokemon *mon, u8 *dest) +u8 *GetMonNicknameVanilla(struct Pokemon *mon, u8 *dest) { u8 nickname[POKEMON_NAME_BUFFER_SIZE]; GetMonData(mon, MON_DATA_NICKNAME, nickname); - return StringCopy_Nickname(dest, nickname); + return StringCopyN(dest, nickname, VANILLA_POKEMON_NAME_LENGTH); } u8 *GetBoxMonNickname(struct BoxPokemon *mon, u8 *dest) @@ -218,7 +218,7 @@ static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycar u8 mailId; StringCopy(daycareMon->mail.otName, gSaveBlock2Ptr->playerName); - GetMonNickname2(mon, daycareMon->mail.monName); + GetMonNicknameVanilla(mon, daycareMon->mail.monName); StripExtCtrlCodes(daycareMon->mail.monName); daycareMon->mail.gameLanguage = GAME_LANGUAGE; daycareMon->mail.monLanguage = GetMonData(mon, MON_DATA_LANGUAGE); @@ -418,7 +418,7 @@ static void ClearDaycareMonMail(struct DaycareMail *mail) for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++) mail->otName[i] = 0; - for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) + for (i = 0; i < VANILLA_POKEMON_NAME_LENGTH + 1; i++) mail->monName[i] = 0; ClearMail(&mail->message); diff --git a/src/egg_hatch.c b/src/egg_hatch.c index 588563d5d2..beb6ba40c4 100644 --- a/src/egg_hatch.c +++ b/src/egg_hatch.c @@ -36,6 +36,7 @@ #include "data.h" #include "battle.h" // to get rid of later #include "constants/rgb.h" +#include "party_menu.h" #define GFXTAG_EGG 12345 #define GFXTAG_EGG_SHARD 23456 @@ -375,7 +376,7 @@ static void AddHatchedMonToParty(u8 id) GetSetPokedexFlag(species, FLAG_SET_SEEN); GetSetPokedexFlag(species, FLAG_SET_CAUGHT); - GetMonNickname2(mon, gStringVar1); + GetMonNickname(mon, gStringVar1); // A met level of 0 is interpreted on the summary screen as "hatched at" metLevel = 0; @@ -648,7 +649,7 @@ static void CB2_EggHatch(void) break; case 5: // "{mon} hatched from egg" message/fanfare - GetMonNickname2(&gPlayerParty[sEggHatchData->eggPartyId], gStringVar1); + GetMonNickname(&gPlayerParty[sEggHatchData->eggPartyId], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_HatchedFromEgg); EggHatchPrintMessage(sEggHatchData->windowId, gStringVar4, 0, 3, TEXT_SKIP_DRAW); PlayFanfare(MUS_EVOLVED); @@ -666,7 +667,7 @@ static void CB2_EggHatch(void) break; case 8: // Ready the nickname prompt - GetMonNickname2(&gPlayerParty[sEggHatchData->eggPartyId], gStringVar1); + GetMonNickname(&gPlayerParty[sEggHatchData->eggPartyId], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_NicknameHatchPrompt); EggHatchPrintMessage(sEggHatchData->windowId, gStringVar4, 0, 2, 1); sEggHatchData->state++; @@ -685,7 +686,7 @@ static void CB2_EggHatch(void) switch (Menu_ProcessInputNoWrapClearOnChoose()) { case 0: // Yes - GetMonNickname2(&gPlayerParty[sEggHatchData->eggPartyId], gStringVar3); + GetMonNickname(&gPlayerParty[sEggHatchData->eggPartyId], gStringVar3); species = GetMonData(&gPlayerParty[sEggHatchData->eggPartyId], MON_DATA_SPECIES); gender = GetMonGender(&gPlayerParty[sEggHatchData->eggPartyId]); personality = GetMonData(&gPlayerParty[sEggHatchData->eggPartyId], MON_DATA_PERSONALITY, 0); diff --git a/src/hall_of_fame.c b/src/hall_of_fame.c index 80689c264e..2837931ea1 100644 --- a/src/hall_of_fame.c +++ b/src/hall_of_fame.c @@ -452,8 +452,8 @@ static void Task_Hof_InitMonData(u8 taskId) sHofMonPtr->mon[i].isShiny = GetMonData(&gPlayerParty[i], MON_DATA_IS_SHINY); sHofMonPtr->mon[i].personality = GetMonData(&gPlayerParty[i], MON_DATA_PERSONALITY); sHofMonPtr->mon[i].lvl = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL); - GetMonData(&gPlayerParty[i], MON_DATA_NICKNAME, nickname); - for (j = 0; j < POKEMON_NAME_LENGTH; j++) + GetMonData(&gPlayerParty[i], MON_DATA_NICKNAME10, nickname); + for (j = 0; j < VANILLA_POKEMON_NAME_LENGTH; j++) sHofMonPtr->mon[i].nickname[j] = nickname[j]; gTasks[taskId].tMonNumber++; } diff --git a/src/pokemon.c b/src/pokemon.c index 521e54cbbd..47544cdf4a 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1348,7 +1348,7 @@ void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerP dest->spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL); dest->abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL); dest->personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); - GetMonData(mon, MON_DATA_NICKNAME, dest->nickname); + GetMonData(mon, MON_DATA_NICKNAME10, dest->nickname); } static void CreateEventMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId) @@ -2190,6 +2190,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) switch (field) { case MON_DATA_NICKNAME: + case MON_DATA_NICKNAME10: { if (boxMon->isBadEgg) { @@ -2232,7 +2233,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) // so if both are 0 we assume that this is a vanilla // Pokémon and replace them with EOS. This means that // two CHAR_SPACE at the end of a nickname are trimmed. - if (POKEMON_NAME_LENGTH >= 12) + if (field != MON_DATA_NICKNAME10 && POKEMON_NAME_LENGTH >= 12) { if (substruct0->nickname11 == 0 && substruct0->nickname12 == 0) { @@ -2727,14 +2728,23 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) switch (field) { case MON_DATA_NICKNAME: + case MON_DATA_NICKNAME10: { s32 i; for (i = 0; i < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH); i++) boxMon->nickname[i] = data[i]; - if (POKEMON_NAME_LENGTH >= 11) - substruct0->nickname11 = data[10]; - if (POKEMON_NAME_LENGTH >= 12) - substruct0->nickname12 = data[11]; + if (field != MON_DATA_NICKNAME10) + { + if (POKEMON_NAME_LENGTH >= 11) + substruct0->nickname11 = data[10]; + if (POKEMON_NAME_LENGTH >= 12) + substruct0->nickname12 = data[11]; + } + else + { + substruct0->nickname11 = EOS; + substruct0->nickname12 = EOS; + } break; } case MON_DATA_SPECIES: diff --git a/src/tv.c b/src/tv.c index 215af04dce..5530e519f6 100644 --- a/src/tv.c +++ b/src/tv.c @@ -1455,7 +1455,7 @@ void BravoTrainerPokemonProfile_BeforeInterview2(u8 contestStandingPlace) show->bravoTrainer.contestCategory = gSpecialVar_ContestCategory; show->bravoTrainer.contestRank = gSpecialVar_ContestRank; show->bravoTrainer.species = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_SPECIES, NULL); - GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_NICKNAME, show->bravoTrainer.pokemonNickname); + GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_NICKNAME10, show->bravoTrainer.pokemonNickname); StripExtCtrlCodes(show->bravoTrainer.pokemonNickname); show->bravoTrainer.pokemonNameLanguage = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_LANGUAGE); } @@ -1536,7 +1536,7 @@ void PutNameRaterShowOnTheAir(void) show->nameRaterShow.random2 = Random() % 2; show->nameRaterShow.randomSpecies = GetRandomDifferentSpeciesSeenByPlayer(show->nameRaterShow.species); StringCopy(show->nameRaterShow.trainerName, gSaveBlock2Ptr->playerName); - GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, show->nameRaterShow.pokemonName); + GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME10, show->nameRaterShow.pokemonName); StripExtCtrlCodes(show->nameRaterShow.pokemonName); StorePlayerIdInNormalShow(show); show->nameRaterShow.language = gGameLanguage; @@ -1612,7 +1612,7 @@ static void InterviewAfter_PkmnFanClubOpinions(void) show->fanclubOpinions.friendshipHighNybble = GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_FRIENDSHIP, NULL) >> 4; show->fanclubOpinions.questionAsked = gSpecialVar_0x8007; StringCopy(show->fanclubOpinions.playerName, gSaveBlock2Ptr->playerName); - GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_NICKNAME, show->fanclubOpinions.nickname); + GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_NICKNAME10, show->fanclubOpinions.nickname); StripExtCtrlCodes(show->fanclubOpinions.nickname); show->fanclubOpinions.species = GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_SPECIES, NULL); StorePlayerIdInNormalShow(show); @@ -2229,7 +2229,7 @@ void TryPutSpotTheCutiesOnAir(struct Pokemon *pokemon, u8 ribbonMonDataIdx) show->cuties.kind = TVSHOW_CUTIES; show->cuties.active = FALSE; // NOTE: Show is not active until passed via Record Mix. StringCopy(show->cuties.playerName, gSaveBlock2Ptr->playerName); - GetMonData(pokemon, MON_DATA_NICKNAME, show->cuties.nickname); + GetMonData(pokemon, MON_DATA_NICKNAME10, show->cuties.nickname); StripExtCtrlCodes(show->cuties.nickname); show->cuties.nRibbons = GetRibbonCount(pokemon); show->cuties.selectedRibbon = MonDataIdxToRibbon(ribbonMonDataIdx); From 06d817bd1b0b3aa020426fa420406185940b8adf Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 6 Apr 2024 14:42:41 +0100 Subject: [PATCH 28/71] Expanded species names --- include/constants/global.h | 2 +- src/battle_interface.c | 23 +++-- src/contest.c | 2 +- src/data/pokemon/species_info.h | 6 ++ .../pokemon/species_info/gen_2_families.h | 6 +- .../pokemon/species_info/gen_5_families.h | 6 +- .../pokemon/species_info/gen_6_families.h | 4 +- .../pokemon/species_info/gen_7_families.h | 6 +- .../pokemon/species_info/gen_8_families.h | 16 ++-- .../pokemon/species_info/gen_9_families.h | 52 +++++----- src/party_menu.c | 7 +- src/pokedex.c | 17 +++- src/pokemon_storage_system.c | 8 +- src/pokemon_summary_screen.c | 17 ++-- test/battle/ability/booster_energy.c | 24 ++--- test/battle/ability/hospitality.c | 12 +-- test/battle/ability/mirror_armor.c | 14 +-- test/battle/ability/protosynthesis.c | 20 ++-- test/battle/gimmick/dynamax.c | 2 +- test/battle/move_effect/sticky_web.c | 12 +-- test/text.c | 94 +++++++++++++++++++ 21 files changed, 239 insertions(+), 111 deletions(-) diff --git a/include/constants/global.h b/include/constants/global.h index 80378f893a..4555f08f21 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -104,7 +104,7 @@ // string lengths #define ITEM_NAME_LENGTH ((I_EXPANDED_ITEM_NAMES == TRUE) ? 20 : 14) #define ITEM_NAME_PLURAL_LENGTH ITEM_NAME_LENGTH + 2 // 2 is used for the instance where a word's suffix becomes y->ies -#define POKEMON_NAME_LENGTH 10 +#define POKEMON_NAME_LENGTH 12 #define VANILLA_POKEMON_NAME_LENGTH 10 #define POKEMON_NAME_BUFFER_SIZE max(20, POKEMON_NAME_LENGTH + 1) // Frequently used buffer size. Larger than necessary #define PLAYER_NAME_LENGTH 7 diff --git a/src/battle_interface.c b/src/battle_interface.c index 6637fa4c4e..df96fc1b17 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -172,6 +172,7 @@ enum static const u8 *GetHealthboxElementGfxPtr(u8); static u8 *AddTextPrinterAndCreateWindowOnHealthbox(const u8 *, u32, u32, u32, u32 *); +static u8 *AddTextPrinterAndCreateWindowOnHealthboxToFit(const u8 *, u32, u32, u32, u32 *, u32); static void RemoveWindowOnHealthbox(u32 windowId); static void UpdateHpTextInHealthboxInDoubles(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp); @@ -2296,24 +2297,21 @@ static void UpdateNickInHealthbox(u8 healthboxSpriteId, struct Pokemon *mon) if ((species == SPECIES_NIDORAN_F || species == SPECIES_NIDORAN_M) && StringCompare(nickname, GetSpeciesName(species)) == 0) gender = 100; - // AddTextPrinterAndCreateWindowOnHealthbox's arguments are the same in all 3 cases. - // It's possible they may have been different in early development phases. switch (gender) { default: StringCopy(ptr, gText_HealthboxGender_None); - windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(gDisplayedStringBattle, 0, 3, 2, &windowId); break; case MON_MALE: StringCopy(ptr, gText_HealthboxGender_Male); - windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(gDisplayedStringBattle, 0, 3, 2, &windowId); break; case MON_FEMALE: StringCopy(ptr, gText_HealthboxGender_Female); - windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(gDisplayedStringBattle, 0, 3, 2, &windowId); break; } + windowTileData = AddTextPrinterAndCreateWindowOnHealthboxToFit(gDisplayedStringBattle, 0, 3, 2, &windowId, 54); + spriteTileNum = gSprites[healthboxSpriteId].oam.tileNum * TILE_SIZE_4BPP; if (GetBattlerSide(gSprites[healthboxSpriteId].data[6]) == B_SIDE_PLAYER) @@ -2906,7 +2904,7 @@ u8 GetHPBarLevel(s16 hp, s16 maxhp) return result; } -static u8 *AddTextPrinterAndCreateWindowOnHealthbox(const u8 *str, u32 x, u32 y, u32 bgColor, u32 *windowId) +static u8 *AddTextPrinterAndCreateWindowOnHealthboxWithFont(const u8 *str, u32 x, u32 y, u32 bgColor, u32 *windowId, u32 fontId) { u16 winId; u8 color[3]; @@ -2919,12 +2917,23 @@ static u8 *AddTextPrinterAndCreateWindowOnHealthbox(const u8 *str, u32 x, u32 y, color[1] = 1; color[2] = 3; - AddTextPrinterParameterized4(winId, FONT_SMALL, x, y, 0, 0, color, TEXT_SKIP_DRAW, str); + AddTextPrinterParameterized4(winId, fontId, x, y, 0, 0, color, TEXT_SKIP_DRAW, str); *windowId = winId; return (u8 *)(GetWindowAttribute(winId, WINDOW_TILE_DATA)); } +static u8 *AddTextPrinterAndCreateWindowOnHealthbox(const u8 *str, u32 x, u32 y, u32 bgColor, u32 *windowId) +{ + return AddTextPrinterAndCreateWindowOnHealthboxWithFont(str, x, y, bgColor, windowId, FONT_SMALL); +} + +static u8 *AddTextPrinterAndCreateWindowOnHealthboxToFit(const u8 *str, u32 x, u32 y, u32 bgColor, u32 *windowId, u32 width) +{ + u32 fontId = GetFontIdToFit(str, FONT_SMALL, 0, width); + return AddTextPrinterAndCreateWindowOnHealthboxWithFont(str, x, y, bgColor, windowId, fontId); +} + static void RemoveWindowOnHealthbox(u32 windowId) { RemoveWindow(windowId); diff --git a/src/contest.c b/src/contest.c index 4a2e4e66c4..c063b4a3b4 100644 --- a/src/contest.c +++ b/src/contest.c @@ -3139,7 +3139,7 @@ static void PrintContestantMonName(u8 contestant) static void PrintContestantMonNameWithColor(u8 contestant, u8 color) { Contest_CopyStringWithColor(gContestMons[contestant].nickname, color); - Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[contestant], gDisplayedStringBattle, 5, 1, FONT_NARROW); + Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[contestant], gDisplayedStringBattle, 5, 1, GetFontIdToFit(gContestMons[contestant].nickname, FONT_NARROW, 0, 50)); } static u16 CalculateContestantRound1Points(u8 who, u8 contestCategory) diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index 63c5d752a8..014996e8c3 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -21,6 +21,12 @@ #define FLIP 0 #define NO_FLIP 1 +#if POKEMON_NAME_LENGTH >= 12 +#define HANDLE_EXPANDED_SPECIES_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__)) +#else +#define HANDLE_EXPANDED_SPECIES_NAME(_name, ...) _(_name) +#endif + const struct SpeciesInfo gSpeciesInfo[] = { [SPECIES_NONE] = diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h index 75fa9ac1e0..fcc1e18cd7 100644 --- a/src/data/pokemon/species_info/gen_2_families.h +++ b/src/data/pokemon/species_info/gen_2_families.h @@ -3407,7 +3407,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), .abilities = { ABILITY_SERENE_GRACE, ABILITY_RUN_AWAY, ABILITY_RATTLED }, .bodyColor = BODY_COLOR_YELLOW, - .speciesName = _("Dudunsprce"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Dudunsprce", "Dudunsparce"), .cryId = CRY_DUDUNSPARCE, .natDexNum = NATIONAL_DEX_DUDUNSPARCE, .categoryName = _("Land Snake"), @@ -3460,7 +3460,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD), .abilities = { ABILITY_SERENE_GRACE, ABILITY_RUN_AWAY, ABILITY_RATTLED }, .bodyColor = BODY_COLOR_YELLOW, - .speciesName = _("Dudunsprce"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Dudunsprce", "Dudunsparce"), .cryId = CRY_DUDUNSPARCE, .natDexNum = NATIONAL_DEX_DUDUNSPARCE, .categoryName = _("Land Snake"), @@ -6460,4 +6460,4 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = #ifdef __INTELLISENSE__ }; -#endif \ No newline at end of file +#endif diff --git a/src/data/pokemon/species_info/gen_5_families.h b/src/data/pokemon/species_info/gen_5_families.h index 4b1fea0263..9f7200849b 100644 --- a/src/data/pokemon/species_info/gen_5_families.h +++ b/src/data/pokemon/species_info/gen_5_families.h @@ -3462,7 +3462,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_2), .abilities = { ABILITY_SWIFT_SWIM, ABILITY_ADAPTABILITY, ABILITY_MOLD_BREAKER }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("Bsculegion"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Bsculegion", "Basculegion"), .cryId = CRY_BASCULEGION, .natDexNum = NATIONAL_DEX_BASCULEGION, .categoryName = _("Big Fish"), @@ -3516,7 +3516,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_2), .abilities = { ABILITY_SWIFT_SWIM, ABILITY_ADAPTABILITY, ABILITY_MOLD_BREAKER }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("Bsculegion"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Bsculegion", "Basculegion"), .cryId = CRY_BASCULEGION, .natDexNum = NATIONAL_DEX_BASCULEGION, .categoryName = _("Big Fish"), @@ -10404,4 +10404,4 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = #ifdef __INTELLISENSE__ }; -#endif \ No newline at end of file +#endif diff --git a/src/data/pokemon/species_info/gen_6_families.h b/src/data/pokemon/species_info/gen_6_families.h index ce1f1da228..d49628cb0c 100644 --- a/src/data/pokemon/species_info/gen_6_families.h +++ b/src/data/pokemon/species_info/gen_6_families.h @@ -767,7 +767,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_FLAME_BODY, ABILITY_NONE, ABILITY_GALE_WINGS }, .bodyColor = BODY_COLOR_RED, - .speciesName = _("Flechinder"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Flechinder", "Fletchinder"), .cryId = CRY_FLETCHINDER, .natDexNum = NATIONAL_DEX_FLETCHINDER, .categoryName = _("Ember"), @@ -5343,4 +5343,4 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = #ifdef __INTELLISENSE__ }; -#endif \ No newline at end of file +#endif diff --git a/src/data/pokemon/species_info/gen_7_families.h b/src/data/pokemon/species_info/gen_7_families.h index da640ca852..36a1133190 100644 --- a/src/data/pokemon/species_info/gen_7_families.h +++ b/src/data/pokemon/species_info/gen_7_families.h @@ -1150,7 +1150,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_3), .abilities = { ABILITY_HYPER_CUTTER, ABILITY_IRON_FIST, ABILITY_ANGER_POINT }, .bodyColor = BODY_COLOR_WHITE, - .speciesName = _("Crabminabl"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Crabminabl", "Crabominable"), .cryId = CRY_CRABOMINABLE, .natDexNum = NATIONAL_DEX_CRABOMINABLE, .categoryName = _("Woolly Crab"), @@ -5904,7 +5904,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .abilities = { ABILITY_BEAST_BOOST, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_WHITE, .noFlip = TRUE, - .speciesName = _("Blacephaln"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Blacephaln", "Blacephalon"), .cryId = CRY_BLACEPHALON, .natDexNum = NATIONAL_DEX_BLACEPHALON, .categoryName = _("Fireworks"), @@ -6160,4 +6160,4 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = #ifdef __INTELLISENSE__ }; -#endif \ No newline at end of file +#endif diff --git a/src/data/pokemon/species_info/gen_8_families.h b/src/data/pokemon/species_info/gen_8_families.h index bb9ff9c087..65c5659f7b 100644 --- a/src/data/pokemon/species_info/gen_8_families.h +++ b/src/data/pokemon/species_info/gen_8_families.h @@ -837,7 +837,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_KEEN_EYE, ABILITY_UNNERVE, ABILITY_BIG_PECKS }, .bodyColor = BODY_COLOR_BLUE, - .speciesName = _("Corvisquir"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Corvisquir", "Corvisquire"), .cryId = CRY_CORVISQUIRE, .natDexNum = NATIONAL_DEX_CORVISQUIRE, .categoryName = _("Raven"), @@ -891,7 +891,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_PRESSURE, ABILITY_UNNERVE, ABILITY_MIRROR_ARMOR }, .bodyColor = BODY_COLOR_PURPLE, - .speciesName = _("Corviknigh"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Corviknigh", "Corviknight"), .cryId = CRY_CORVIKNIGHT, .natDexNum = NATIONAL_DEX_CORVIKNIGHT, .categoryName = _("Raven"), @@ -2803,7 +2803,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_2), .abilities = { ABILITY_SWIFT_SWIM, ABILITY_NONE, ABILITY_PROPELLER_TAIL }, .bodyColor = BODY_COLOR_BROWN, - .speciesName = _("Barraskewd"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Barraskewd", "Barraskewda"), .cryId = CRY_BARRASKEWDA, .natDexNum = NATIONAL_DEX_BARRASKEWDA, .categoryName = _("Skewer"), @@ -3178,7 +3178,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_BUG), .abilities = { ABILITY_FLASH_FIRE, ABILITY_WHITE_SMOKE, ABILITY_FLAME_BODY }, .bodyColor = BODY_COLOR_RED, - .speciesName = _("Centiskorc"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Centiskorc", "Centiskorch"), .cryId = CRY_CENTISKORCH, .natDexNum = NATIONAL_DEX_CENTISKORCH, .categoryName = _("Radiator"), @@ -3506,7 +3506,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL, EGG_GROUP_AMORPHOUS), .abilities = { ABILITY_WEAK_ARMOR, ABILITY_NONE, ABILITY_CURSED_BODY }, .bodyColor = BODY_COLOR_PURPLE, - .speciesName = _("Polteageis"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Polteageis", "Polteageist"), .cryId = CRY_POLTEAGEIST, .natDexNum = NATIONAL_DEX_POLTEAGEIST, .categoryName = _("Black Tea"), @@ -3559,7 +3559,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL, EGG_GROUP_AMORPHOUS), .abilities = { ABILITY_WEAK_ARMOR, ABILITY_NONE, ABILITY_CURSED_BODY }, .bodyColor = BODY_COLOR_PURPLE, - .speciesName = _("Polteageis"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Polteageis", "Polteageist"), .cryId = CRY_POLTEAGEIST, .natDexNum = NATIONAL_DEX_POLTEAGEIST, .categoryName = _("Black Tea"), @@ -4486,7 +4486,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL), .abilities = { ABILITY_POWER_SPOT, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GRAY, - .speciesName = _("Stonjourne"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Stonjourne", "Stonjourner"), .cryId = CRY_STONJOURNER, .natDexNum = NATIONAL_DEX_STONJOURNER, .categoryName = _("Big Rock"), @@ -6789,4 +6789,4 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = #ifdef __INTELLISENSE__ }; -#endif \ No newline at end of file +#endif diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index 304f0560a2..b334c59110 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -129,7 +129,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FIELD, EGG_GROUP_GRASS), .abilities = { ABILITY_OVERGROW, ABILITY_NONE, ABILITY_PROTEAN }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("Meowscarad"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Meowscarad", "Meowscarada"), .cryId = CRY_MEOWSCARADA, .natDexNum = NATIONAL_DEX_MEOWSCARADA, .categoryName = _("Magician"), @@ -1466,7 +1466,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_INTIMIDATE, ABILITY_HUSTLE, ABILITY_GUTS }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("Sqawkabily"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Sqawkabily", "Squawkabilly"), .cryId = CRY_SQUAWKABILLY, .natDexNum = NATIONAL_DEX_SQUAWKABILLY, .categoryName = _("Parrot"), @@ -1519,7 +1519,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_INTIMIDATE, ABILITY_HUSTLE, ABILITY_GUTS }, .bodyColor = BODY_COLOR_BLUE, - .speciesName = _("Sqawkabily"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Sqawkabily", "Squawkabilly"), .cryId = CRY_SQUAWKABILLY, .natDexNum = NATIONAL_DEX_SQUAWKABILLY, .categoryName = _("Parrot"), @@ -1572,7 +1572,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_INTIMIDATE, ABILITY_HUSTLE, ABILITY_SHEER_FORCE }, .bodyColor = BODY_COLOR_YELLOW, - .speciesName = _("Sqawkabily"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Sqawkabily", "Squawkabilly"), .cryId = CRY_SQUAWKABILLY, .natDexNum = NATIONAL_DEX_SQUAWKABILLY, .categoryName = _("Parrot"), @@ -1625,7 +1625,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_FLYING), .abilities = { ABILITY_INTIMIDATE, ABILITY_HUSTLE, ABILITY_SHEER_FORCE }, .bodyColor = BODY_COLOR_WHITE, - .speciesName = _("Sqawkabily"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Sqawkabily", "Squawkabilly"), .cryId = CRY_SQUAWKABILLY, .natDexNum = NATIONAL_DEX_SQUAWKABILLY, .categoryName = _("Parrot"), @@ -2161,7 +2161,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_WATER_1, EGG_GROUP_FLYING), .abilities = { ABILITY_WIND_POWER, ABILITY_VOLT_ABSORB, ABILITY_COMPETITIVE }, .bodyColor = BODY_COLOR_YELLOW, - .speciesName = _("Kilowatrel"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Kilowatrel", "Kilowattrel"), .cryId = CRY_KILOWATTREL, .natDexNum = NATIONAL_DEX_KILOWATTREL, .categoryName = _("Frigatebird"), @@ -2482,7 +2482,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_GRASS), .abilities = { ABILITY_WIND_RIDER, ABILITY_NONE, ABILITY_INFILTRATOR }, .bodyColor = BODY_COLOR_BROWN, - .speciesName = _("Brmblghast"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Brmblghast", "Brambleghast"), .cryId = CRY_BRAMBLEGHAST, .natDexNum = NATIONAL_DEX_BRAMBLEGHAST, .categoryName = _("Tumbleweed"), @@ -4416,7 +4416,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_PINK, - .speciesName = _("ScreamTail"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("ScreamTail", "Scream Tail"), .cryId = CRY_SCREAM_TAIL, .natDexNum = NATIONAL_DEX_SCREAM_TAIL, .categoryName = _("Paradox"), @@ -4471,7 +4471,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_WHITE, - .speciesName = _("BruteBonet"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("BruteBonet", "Brute Bonnet"), .cryId = CRY_BRUTE_BONNET, .natDexNum = NATIONAL_DEX_BRUTE_BONNET, .categoryName = _("Paradox"), @@ -4528,7 +4528,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GRAY, - .speciesName = _("FluttrMane"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("FluttrMane", "Flutter Mane"), .cryId = CRY_FLUTTER_MANE, .natDexNum = NATIONAL_DEX_FLUTTER_MANE, .categoryName = _("Paradox"), @@ -4584,7 +4584,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_WHITE, - .speciesName = _("SlithrWing"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("SlithrWing", "Slither Wing"), .cryId = CRY_SLITHER_WING, .natDexNum = NATIONAL_DEX_SLITHER_WING, .categoryName = _("Paradox"), @@ -4638,7 +4638,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GRAY, - .speciesName = _("SndyShocks"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("SndyShocks", "Sandy Shocks"), .cryId = CRY_SANDY_SHOCKS, .natDexNum = NATIONAL_DEX_SANDY_SHOCKS, .categoryName = _("Paradox"), @@ -4693,7 +4693,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GRAY, - .speciesName = _("IronTreads"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronTreads", "Iron Treads"), .cryId = CRY_IRON_TREADS, .natDexNum = NATIONAL_DEX_IRON_TREADS, .categoryName = _("Paradox"), @@ -4748,7 +4748,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_RED, - .speciesName = _("IronBundle"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronBundle", "Iron Bundle"), .cryId = CRY_IRON_BUNDLE, .natDexNum = NATIONAL_DEX_IRON_BUNDLE, .categoryName = _("Paradox"), @@ -4858,7 +4858,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_BLUE, - .speciesName = _("IronJuguls"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronJuguls", "Iron Jugulis"), .cryId = CRY_IRON_JUGULIS, .natDexNum = NATIONAL_DEX_IRON_JUGULIS, .categoryName = _("Paradox"), @@ -4970,7 +4970,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("IronThorns"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronThorns", "Iron Thorns"), .cryId = CRY_IRON_THORNS, .natDexNum = NATIONAL_DEX_IRON_THORNS, .categoryName = _("Paradox"), @@ -5568,7 +5568,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_BLUE, - .speciesName = _("RoarngMoon"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("RoarngMoon", "Roaring Moon"), .cryId = CRY_ROARING_MOON, .natDexNum = NATIONAL_DEX_ROARING_MOON, .categoryName = _("Paradox"), @@ -5624,7 +5624,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_WHITE, - .speciesName = _("IronVliant"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronVliant", "Iron Valiant"), .cryId = CRY_IRON_VALIANT, .natDexNum = NATIONAL_DEX_IRON_VALIANT, .categoryName = _("Paradox"), @@ -5788,7 +5788,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_BLUE, - .speciesName = _("WalkngWake"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("WalkngWake", "Walking Wake"), .cryId = CRY_WALKING_WAKE, .natDexNum = NATIONAL_DEX_WALKING_WAKE, .categoryName = _("Paradox"), @@ -5842,7 +5842,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("IronLeaves"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronLeaves", "Iron Leaves"), .cryId = CRY_IRON_LEAVES, .natDexNum = NATIONAL_DEX_IRON_LEAVES, .categoryName = _("Paradox"), @@ -5896,7 +5896,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL, EGG_GROUP_AMORPHOUS), .abilities = { ABILITY_HOSPITALITY, ABILITY_NONE, ABILITY_HEATPROOF }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("Ptchageist"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Ptchageist", "Poltchageist"), .cryId = CRY_POLTCHAGEIST, .natDexNum = NATIONAL_DEX_POLTCHAGEIST, .categoryName = _("Matcha"), @@ -5949,7 +5949,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MINERAL, EGG_GROUP_AMORPHOUS), .abilities = { ABILITY_HOSPITALITY, ABILITY_NONE, ABILITY_HEATPROOF }, .bodyColor = BODY_COLOR_GREEN, - .speciesName = _("Ptchageist"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Ptchageist", "Poltchageist"), .cryId = CRY_POLTCHAGEIST, .natDexNum = NATIONAL_DEX_POLTCHAGEIST, .categoryName = _("Matcha"), @@ -6220,7 +6220,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_TOXIC_CHAIN, ABILITY_NONE, ABILITY_TECHNICIAN }, .bodyColor = BODY_COLOR_BLACK, - .speciesName = _("Fezndipiti"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("Fezndipiti", "Fezandipiti"), .cryId = CRY_FEZANDIPITI, .natDexNum = NATIONAL_DEX_FEZANDIPITI, .categoryName = _("Retainer"), @@ -6340,7 +6340,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_BROWN, - .speciesName = _("GouginFire"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("GouginFire", "Gouging Fire"), .cryId = CRY_GOUGING_FIRE, .natDexNum = NATIONAL_DEX_GOUGING_FIRE, .categoryName = _("Paradox"), @@ -6395,7 +6395,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_PROTOSYNTHESIS, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_YELLOW, - .speciesName = _("RagingBolt"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("RagingBolt", "Raging Bolt"), .cryId = CRY_RAGING_BOLT, .natDexNum = NATIONAL_DEX_RAGING_BOLT, .categoryName = _("Paradox"), @@ -6450,7 +6450,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED), .abilities = { ABILITY_QUARK_DRIVE, ABILITY_NONE, ABILITY_NONE }, .bodyColor = BODY_COLOR_GRAY, - .speciesName = _("IronBouldr"), + .speciesName = HANDLE_EXPANDED_SPECIES_NAME("IronBouldr", "Iron Boulder"), .cryId = CRY_IRON_BOULDER, .natDexNum = NATIONAL_DEX_IRON_BOULDER, .categoryName = _("Paradox"), diff --git a/src/party_menu.c b/src/party_menu.c index fc0a66c5a6..af88db790d 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -2435,6 +2435,11 @@ static void DisplayPartyPokemonBarDetail(u8 windowId, const u8 *str, u8 color, c AddTextPrinterParameterized3(windowId, FONT_SMALL, align[0], align[1], sFontColorTable[color], 0, str); } +static void DisplayPartyPokemonBarDetailToFit(u8 windowId, const u8 *str, u8 color, const u8 *align, u32 width) +{ + AddTextPrinterParameterized3(windowId, GetFontIdToFit(str, FONT_SMALL, 0, width), align[0], align[1], sFontColorTable[color], 0, str); +} + static void DisplayPartyPokemonNickname(struct Pokemon *mon, struct PartyMenuBox *menuBox, u8 c) { u8 nickname[POKEMON_NAME_LENGTH + 1]; @@ -2444,7 +2449,7 @@ static void DisplayPartyPokemonNickname(struct Pokemon *mon, struct PartyMenuBox if (c == 1) menuBox->infoRects->blitFunc(menuBox->windowId, menuBox->infoRects->dimensions[0] >> 3, menuBox->infoRects->dimensions[1] >> 3, menuBox->infoRects->dimensions[2] >> 3, menuBox->infoRects->dimensions[3] >> 3, FALSE); GetMonNickname(mon, nickname); - DisplayPartyPokemonBarDetail(menuBox->windowId, nickname, 0, menuBox->infoRects->dimensions); + DisplayPartyPokemonBarDetailToFit(menuBox->windowId, nickname, 0, menuBox->infoRects->dimensions, 50); } } diff --git a/src/pokedex.c b/src/pokedex.c index 02738980dd..0eb99d701e 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -2337,7 +2337,7 @@ static void CreatePokedexList(u8 dexMode, u8 order) } } -static void PrintMonDexNumAndName(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top) +static void PrintMonDexNum(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top) { u8 color[3]; @@ -2347,6 +2347,17 @@ static void PrintMonDexNumAndName(u8 windowId, u8 fontId, const u8 *str, u8 left AddTextPrinterParameterized4(windowId, fontId, left * 8, (top * 8) + 1, 0, 0, color, TEXT_SKIP_DRAW, str); } +static void PrintMonName(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top) +{ + u8 color[3]; + + color[0] = TEXT_COLOR_TRANSPARENT; + color[1] = TEXT_DYNAMIC_COLOR_6; + color[2] = TEXT_COLOR_LIGHT_GRAY; + fontId = GetFontIdToFit(str, fontId, 0, 50); + AddTextPrinterParameterized4(windowId, fontId, left * 8, (top * 8) + 1, 0, 0, color, TEXT_SKIP_DRAW, str); +} + // u16 ignored is passed but never used static void CreateMonListEntry(u8 position, u16 b, u16 ignored) { @@ -2453,7 +2464,7 @@ static void CreateMonDexNum(u16 entryNum, u8 left, u8 top, u16 unused) text[offset++] = CHAR_0 + ((dexNum % 1000) % 100) / 10; text[offset++] = CHAR_0 + ((dexNum % 1000) % 100) % 10; text[offset++] = EOS; - PrintMonDexNumAndName(0, FONT_NARROW, text, left, top); + PrintMonDexNum(0, FONT_NARROW, text, left, top); } static void CreateCaughtBall(bool16 owned, u8 x, u8 y, u16 unused) @@ -2473,7 +2484,7 @@ static u8 CreateMonName(u16 num, u8 left, u8 top) str = GetSpeciesName(num); else str = sText_TenDashes; - PrintMonDexNumAndName(0, FONT_NARROW, str, left, top); + PrintMonName(0, FONT_NARROW, str, left, top); return StringLength(str); } diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index a079a10c24..b60dfb7daf 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -4028,16 +4028,16 @@ static void PrintDisplayMonInfo(void) FillWindowPixelBuffer(WIN_DISPLAY_INFO, PIXEL_FILL(1)); if (sStorage->boxOption != OPTION_MOVE_ITEMS) { - AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_NORMAL, sStorage->displayMonNameText, 6, 0, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonSpeciesName, 6, 15, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_NORMAL, 0, 9*8 - 6), sStorage->displayMonNameText, 6, 0, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_SHORT, 0, 9*8 - 12), sStorage->displayMonSpeciesName, 6, 15, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 29, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonItemName, FONT_SMALL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 22), sStorage->displayMonItemName, 6, 43, TEXT_SKIP_DRAW, NULL); } else { AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonItemName, FONT_SMALL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 22), sStorage->displayMonItemName, 6, 0, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_NORMAL, sStorage->displayMonNameText, 6, 13, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonSpeciesName, 6, 28, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_NORMAL, 0, 9*8 - 6), sStorage->displayMonNameText, 6, 13, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonSpeciesName, FONT_SHORT, 0, 9*8 - 12), sStorage->displayMonSpeciesName, 6, 28, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 42, TEXT_SKIP_DRAW, NULL); } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 1989913fc2..5132218931 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2813,10 +2813,15 @@ static void PrintTextOnWindow(u8 windowId, const u8 *string, u8 x, u8 y, u8 line PrintTextOnWindowWithFont(windowId, string, x, y, lineSpacing, colorId, FONT_NORMAL); } +static void PrintTextOnWindowToFitPx(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId, u32 width) +{ + u32 fontId = GetFontIdToFit(string, FONT_NORMAL, 0, width); + PrintTextOnWindowWithFont(windowId, string, x, y, lineSpacing, colorId, fontId); +} + static void PrintTextOnWindowToFit(u8 windowId, const u8 *string, u8 x, u8 y, u8 lineSpacing, u8 colorId) { - u32 fontId = GetFontIdToFit(string, FONT_NORMAL, 0, WindowWidthPx(windowId)); - PrintTextOnWindowWithFont(windowId, string, x, y, lineSpacing, colorId, fontId); + PrintTextOnWindowToFitPx(windowId, string, x, y, lineSpacing, colorId, WindowWidthPx(windowId)); } static void PrintMonInfo(void) @@ -2833,7 +2838,6 @@ static void PrintMonInfo(void) static void PrintNotEggInfo(void) { - u8 strArray[16]; struct Pokemon *mon = &sMonSummaryScreen->currentMon; struct PokeSummary *summary = &sMonSummaryScreen->summary; u16 dexNum = SpeciesToPokedexNum(summary->species); @@ -2869,10 +2873,9 @@ static void PrintNotEggInfo(void) StringAppend(gStringVar1, gStringVar2); PrintTextOnWindow(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, gStringVar1, 24, 17, 0, 1); GetMonNickname(mon, gStringVar1); - PrintTextOnWindow(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME, gStringVar1, 0, 1, 0, 1); - strArray[0] = CHAR_SLASH; - StringCopy(&strArray[1], &GetSpeciesName(summary->species2)[0]); - PrintTextOnWindow(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, strArray, 0, 1, 0, 1); + PrintTextOnWindowToFitPx(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME, gStringVar1, 0, 1, 0, 1, 63); + PrintTextOnWindow(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, gText_Slash, 0, 1, 0, 1); + PrintTextOnWindowToFitPx(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, GetSpeciesName(summary->species2), 6, 1, 0, 1, 63); PrintGenderSymbol(mon, summary->species2); PutWindowTilemap(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME); PutWindowTilemap(PSS_LABEL_WINDOW_PORTRAIT_SPECIES); diff --git a/test/battle/ability/booster_energy.c b/test/battle/ability/booster_energy.c index 6d08659781..3d9b41d5aa 100644 --- a/test/battle/ability/booster_energy.c +++ b/test/battle/ability/booster_energy.c @@ -50,17 +50,17 @@ SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunl ABILITY_POPUP(opponent, ABILITY_DROUGHT); NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); - MESSAGE("RagingBolt used its Booster Energy to activate Protosynthesis!"); - MESSAGE("RagingBolt's Sp. Atk was heightened!"); + MESSAGE("Raging Bolt used its Booster Energy to activate Protosynthesis!"); + MESSAGE("Raging Bolt's Sp. Atk was heightened!"); } ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("The harsh sunlight activated RagingBolt's Protosynthesis!"); - MESSAGE("RagingBolt's Sp. Atk was heightened!"); + MESSAGE("The harsh sunlight activated Raging Bolt's Protosynthesis!"); + MESSAGE("Raging Bolt's Sp. Atk was heightened!"); MESSAGE("The sunlight faded."); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("RagingBolt used its Booster Energy to activate Protosynthesis!"); - MESSAGE("RagingBolt's Sp. Atk was heightened!"); + MESSAGE("Raging Bolt used its Booster Energy to activate Protosynthesis!"); + MESSAGE("Raging Bolt's Sp. Atk was heightened!"); } } @@ -82,17 +82,17 @@ SINGLE_BATTLE_TEST("Booster Energy activates Protosynthesis and increases highes } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("RagingBolt used its Booster Energy to activate Protosynthesis!"); + MESSAGE("Raging Bolt used its Booster Energy to activate Protosynthesis!"); if (attack == 110) - MESSAGE("RagingBolt's Attack was heightened!"); + MESSAGE("Raging Bolt's Attack was heightened!"); else if (defense == 110) - MESSAGE("RagingBolt's Defense was heightened!"); + MESSAGE("Raging Bolt's Defense was heightened!"); else if (speed == 110) - MESSAGE("RagingBolt's Speed was heightened!"); + MESSAGE("Raging Bolt's Speed was heightened!"); else if (spAttack == 110) - MESSAGE("RagingBolt's Sp. Atk was heightened!"); + MESSAGE("Raging Bolt's Sp. Atk was heightened!"); else if (spDefense == 110) - MESSAGE("RagingBolt's Sp. Def was heightened!"); + MESSAGE("Raging Bolt's Sp. Def was heightened!"); } THEN { EXPECT(player->item == ITEM_NONE); } diff --git a/test/battle/ability/hospitality.c b/test/battle/ability/hospitality.c index 74648b0c9a..dc81faf7de 100644 --- a/test/battle/ability/hospitality.c +++ b/test/battle/ability/hospitality.c @@ -18,12 +18,12 @@ DOUBLE_BATTLE_TEST("Hospitality user restores 25% of ally's health") } SCENE { if (health == 75) { ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); - MESSAGE("Wobbuffet drank down all the matcha that Ptchageist made!"); + MESSAGE("Wobbuffet drank down all the matcha that Poltchageist made!"); HP_BAR(playerRight, damage: -25); } else { NONE_OF { ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); - MESSAGE("Wobbuffet drank down all the matcha that Ptchageist made!"); + MESSAGE("Wobbuffet drank down all the matcha that Poltchageist made!"); HP_BAR(playerRight, damage: -25); } } @@ -42,9 +42,9 @@ DOUBLE_BATTLE_TEST("Hospitality user restores 25% of ally's health on switch-in" TURN { SWITCH(playerLeft, 2); } } SCENE { MESSAGE("Wobbuffet, that's enough! Come back!"); - MESSAGE("Go! Ptchageist!"); + MESSAGE("Go! Poltchageist!"); ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); - MESSAGE("Wobbuffet drank down all the matcha that Ptchageist made!"); + MESSAGE("Wobbuffet drank down all the matcha that Poltchageist made!"); HP_BAR(playerRight, damage: -25); } } @@ -63,8 +63,8 @@ DOUBLE_BATTLE_TEST("Hospitality ignores Substitute") } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, playerRight); MESSAGE("Wobbuffet, that's enough! Come back!"); - MESSAGE("Go! Ptchageist!"); + MESSAGE("Go! Poltchageist!"); ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); - MESSAGE("Wobbuffet drank down all the matcha that Ptchageist made!"); + MESSAGE("Wobbuffet drank down all the matcha that Poltchageist made!"); } } diff --git a/test/battle/ability/mirror_armor.c b/test/battle/ability/mirror_armor.c index 38ee8dab91..39c22a66c5 100644 --- a/test/battle/ability/mirror_armor.c +++ b/test/battle/ability/mirror_armor.c @@ -55,11 +55,11 @@ SINGLE_BATTLE_TEST("Mirror Armor triggers even if the attacking Pokemon also has } WHEN { TURN { MOVE(opponent, MOVE_LEER); } } SCENE { - MESSAGE("Foe Corviknigh used Leer!"); + MESSAGE("Foe Corviknight used Leer!"); ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - MESSAGE("Foe Corviknigh's Defense fell!"); + MESSAGE("Foe Corviknight's Defense fell!"); } THEN { EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); @@ -153,9 +153,9 @@ SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stat of the attacking Pokemon TURN { MOVE(player, MOVE_SCREECH); } TURN { MOVE(opponent, MOVE_LEER); } } SCENE { - MESSAGE("Corviknigh used Screech!"); - MESSAGE("Corviknigh used Screech!"); - MESSAGE("Corviknigh used Screech!"); + MESSAGE("Corviknight used Screech!"); + MESSAGE("Corviknight used Screech!"); + MESSAGE("Corviknight used Screech!"); MESSAGE("Foe Wynaut used Leer!"); ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR); NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); @@ -188,8 +188,8 @@ DOUBLE_BATTLE_TEST("Mirror Armor lowers Speed of the partner Pokemon after Court MESSAGE("Wobbuffet used Sticky Web!"); MESSAGE("Foe Wynaut used Court Change!"); MESSAGE("Foe Wynaut swapped the battle effects affecting each side!"); - MESSAGE("Go! Corviknigh!"); - MESSAGE("Corviknigh was caught in a Sticky Web!"); + MESSAGE("Go! Corviknight!"); + MESSAGE("Corviknight was caught in a Sticky Web!"); ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); MESSAGE("Wobbuffet's Speed fell!"); diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index 264548c207..3cb35164ec 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -17,8 +17,8 @@ SINGLE_BATTLE_TEST("Protosynthesis boosts the highest stat") } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player); ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("The harsh sunlight activated WalkngWake's Protosynthesis!"); - MESSAGE("WalkngWake's Sp. Atk was heightened!"); + MESSAGE("The harsh sunlight activated Walking Wake's Protosynthesis!"); + MESSAGE("Walking Wake's Sp. Atk was heightened!"); } } @@ -68,19 +68,19 @@ SINGLE_BATTLE_TEST("Protosynthesis ability pop up activates only once during the } SCENE { ABILITY_POPUP(opponent, ABILITY_DROUGHT); ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("The harsh sunlight activated WalkngWake's Protosynthesis!"); - MESSAGE("WalkngWake's Sp. Atk was heightened!"); + MESSAGE("The harsh sunlight activated Walking Wake's Protosynthesis!"); + MESSAGE("Walking Wake's Sp. Atk was heightened!"); NONE_OF { for (turns = 0; turns < 4; turns++) { ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("The harsh sunlight activated WalkngWake's Protosynthesis!"); - MESSAGE("WalkngWake's Sp. Atk was heightened!"); + MESSAGE("The harsh sunlight activated Walking Wake's Protosynthesis!"); + MESSAGE("Walking Wake's Sp. Atk was heightened!"); } } ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("The harsh sunlight activated WalkngWake's Protosynthesis!"); - MESSAGE("WalkngWake's Sp. Atk was heightened!"); + MESSAGE("The harsh sunlight activated Walking Wake's Protosynthesis!"); + MESSAGE("Walking Wake's Sp. Atk was heightened!"); } } @@ -95,7 +95,7 @@ SINGLE_BATTLE_TEST("Protosynthesis activates on switch-in") } SCENE { ABILITY_POPUP(opponent, ABILITY_DROUGHT); ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); - MESSAGE("The harsh sunlight activated RoarngMoon's Protosynthesis!"); - MESSAGE("RoarngMoon's Attack was heightened!"); + MESSAGE("The harsh sunlight activated Roaring Moon's Protosynthesis!"); + MESSAGE("Roaring Moon's Attack was heightened!"); } } diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 42e48c9e9c..a9ee2fe4d0 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -1347,7 +1347,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Centiferno traps both opponents in Fire Spin TURN { SWITCH(playerLeft, 2); } } SCENE { // turn 1 - MESSAGE("Centiskorc used G-Max Centiferno!"); + MESSAGE("Centiskorch used G-Max Centiferno!"); MESSAGE("Foe Wobbuffet is hurt by Fire Spin!"); HP_BAR(opponentLeft); MESSAGE("Foe Wynaut is hurt by Fire Spin!"); diff --git a/test/battle/move_effect/sticky_web.c b/test/battle/move_effect/sticky_web.c index 6c6f8f156c..e3c96ca2fa 100644 --- a/test/battle/move_effect/sticky_web.c +++ b/test/battle/move_effect/sticky_web.c @@ -123,8 +123,8 @@ DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - the ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, BATTLER_PLAYER); MESSAGE("A sticky web spreads out on the ground around the opposing team!"); - MESSAGE("Go! Corviknigh!"); - MESSAGE("Corviknigh was caught in a Sticky Web!"); + MESSAGE("Go! Corviknight!"); + MESSAGE("Corviknight was caught in a Sticky Web!"); ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, BATTLER_OPPONENT); if (opponentSetUpper == 0) { @@ -170,8 +170,8 @@ DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - no o MESSAGE("A sticky web spreads out on the ground around the opposing team!"); } - MESSAGE("Go! Corviknigh!"); - MESSAGE("Corviknigh was caught in a Sticky Web!"); + MESSAGE("Go! Corviknight!"); + MESSAGE("Corviknight was caught in a Sticky Web!"); ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); } THEN { @@ -219,8 +219,8 @@ DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - no o MESSAGE("2 sent out Pidgey!"); } - MESSAGE("Go! Corviknigh!"); - MESSAGE("Corviknigh was caught in a Sticky Web!"); + MESSAGE("Go! Corviknight!"); + MESSAGE("Corviknight was caught in a Sticky Web!"); ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR); NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); } THEN { diff --git a/test/text.c b/test/text.c index f42511ee92..19a0a88b40 100644 --- a/test/text.c +++ b/test/text.c @@ -269,3 +269,97 @@ TEST("Item names fit on Shop Screen") } EXPECT_LE(GetStringWidth(fontId, gItemsInfo[item].name, 0), widthPx); } + +TEST("Species names fit on Battle Screen HP box") +{ + u32 i, genderWidthPx; + const u32 fontId = FONT_SMALL_NARROWER, widthPx = 54; + u32 species = SPECIES_NONE; + genderWidthPx = GetStringWidth(fontId, COMPOUND_STRING("♂"), 0); + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + if (gSpeciesInfo[i].genderRatio != MON_GENDERLESS) + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0) - genderWidthPx, widthPx); + else + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Party Screen") +{ + u32 i; + const u32 fontId = FONT_SMALL_NARROWER, widthPx = 50; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Pokemon Summary Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 63; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Pokedex Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 50; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Pokemon Storage System") +{ + u32 i; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(FONT_NARROWER, gSpeciesInfo[species].speciesName, 0), 66); + EXPECT_LE(GetStringWidth(FONT_SHORT_NARROW, gSpeciesInfo[species].speciesName, 0), 60); +} + +TEST("Species names fit on Contest Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 50; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} From d913792f43907c83125f5015dcdac92b0c7302fd Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 6 Apr 2024 15:15:08 +0100 Subject: [PATCH 29/71] Enable GF species names by default --- include/config/test.h | 2 + src/battle_dome.c | 2 + src/contest.c | 24 +++-- src/contest_util.c | 12 ++- src/hall_of_fame.c | 14 ++- src/naming_screen.c | 7 +- src/pokedex.c | 15 +-- src/pokemon_storage_system.c | 8 +- src/pokemon_summary_screen.c | 4 +- src/pokenav_conditions.c | 2 + src/pokenav_conditions_search_results.c | 10 +- src/pokenav_ribbons_list.c | 9 +- src/pokenav_ribbons_summary.c | 2 +- test/text.c | 135 ++++++++++++++++++++++++ 14 files changed, 202 insertions(+), 44 deletions(-) diff --git a/include/config/test.h b/include/config/test.h index 8dba50f8a7..cba8012d36 100644 --- a/include/config/test.h +++ b/include/config/test.h @@ -5,5 +5,7 @@ #define B_EXPANDED_MOVE_NAMES TRUE #undef I_EXPANDED_ITEM_NAMES #define I_EXPANDED_ITEM_NAMES TRUE +#undef POKEMON_NAME_LENGTH +#define POKEMON_NAME_LENGTH 12 #endif // GUARD_CONFIG_TEST_H diff --git a/src/battle_dome.c b/src/battle_dome.c index 95490a54ea..ca105beb17 100644 --- a/src/battle_dome.c +++ b/src/battle_dome.c @@ -4256,6 +4256,7 @@ static void DisplayTrainerInfoOnCard(u8 flags, u8 trainerTourneyId) textPrinter.currentChar = GetSpeciesName(DOME_MONS[trainerTourneyId][i]); else textPrinter.currentChar = GetSpeciesName(gFacilityTrainerMons[DOME_MONS[trainerTourneyId][i]].species); + textPrinter.fontId = GetFontIdToFit(textPrinter.currentChar, FONT_SHORT, 0, 60); textPrinter.windowId = WIN_TRAINER_MON1_NAME + i + windowId; if (i == 1) @@ -4267,6 +4268,7 @@ static void DisplayTrainerInfoOnCard(u8 flags, u8 trainerTourneyId) CopyWindowToVram(WIN_TRAINER_MON1_NAME + i + windowId, COPYWIN_FULL); AddTextPrinter(&textPrinter, 0, NULL); } + textPrinter.fontId = FONT_SHORT; PutWindowTilemap(windowId + WIN_TRAINER_FLAVOR_TEXT); CopyWindowToVram(windowId + WIN_TRAINER_FLAVOR_TEXT, COPYWIN_FULL); diff --git a/src/contest.c b/src/contest.c index c063b4a3b4..be8bb3e92b 100644 --- a/src/contest.c +++ b/src/contest.c @@ -1085,6 +1085,12 @@ static const s8 sContestExcitementTable[CONTEST_CATEGORIES_COUNT][CONTEST_CATEGO } }; +static void CopyNicknameToFit(u8 *dest, u32 contestant) +{ + u8 *end = StringCopy(dest, gContestMons[contestant].nickname); + WrapFontIdToFit(dest, end, FONT_NORMAL, 60); +} + static void CopyMoveNameToFit(u8 *dest, u32 move) { u8 *end = StringCopy(dest, GetMoveName(move)); @@ -1905,7 +1911,7 @@ static void Task_DoAppeals(u8 taskId) else { ContestClearGeneralTextWindow(); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); if (eContestantStatus[contestant].currMove < MOVES_COUNT) StringCopy(gStringVar2, GetMoveName(eContestantStatus[contestant].currMove)); else @@ -2171,7 +2177,7 @@ static void Task_DoAppeals(u8 taskId) || eContestantStatus[contestant].turnSkipped) { ContestClearGeneralTextWindow(); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); StringExpandPlaceholders(gStringVar4, gText_MonCantAppealNextTurn); Contest_StartTextPrinter(gStringVar4, TRUE); } @@ -2213,7 +2219,7 @@ static void Task_DoAppeals(u8 taskId) { // Started combo ContestClearGeneralTextWindow(); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); StringExpandPlaceholders(gStringVar4, gText_JudgeLookedAtMonExpectantly); Contest_StartTextPrinter(gStringVar4, TRUE); DoJudgeSpeechBubble(JUDGE_SYMBOL_ONE_EXCLAMATION); @@ -2257,7 +2263,7 @@ static void Task_DoAppeals(u8 taskId) if (eContestantStatus[contestant].repeatedMove) { ContestClearGeneralTextWindow(); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); StringExpandPlaceholders(gStringVar4, gText_RepeatedAppeal); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tCounter = 0; @@ -2312,7 +2318,7 @@ static void Task_DoAppeals(u8 taskId) r3 = 0; ContestClearGeneralTextWindow(); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); eContest.applauseLevel += r3; if (eContest.applauseLevel < 0) eContest.applauseLevel = 0; @@ -2434,8 +2440,8 @@ static void Task_DoAppeals(u8 taskId) return; case APPEALSTATE_PRINT_CROWD_WATCHES_MSG: ContestClearGeneralTextWindow(); - StringCopy(gStringVar3, gContestMons[eContestExcitement.freezer].nickname); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar3, eContestExcitement.freezer); + CopyNicknameToFit(gStringVar1, contestant); CopyMoveNameToFit(gStringVar2, eContestantStatus[contestant].currMove); StringExpandPlaceholders(gStringVar4, gText_CrowdContinuesToWatchMon); Contest_StartTextPrinter(gStringVar4, TRUE); @@ -2461,7 +2467,7 @@ static void Task_DoAppeals(u8 taskId) if (eContestantStatus[contestant].hasJudgesAttention) eContestantStatus[contestant].hasJudgesAttention = FALSE; StartStopFlashJudgeAttentionEye(contestant); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); CopyMoveNameToFit(gStringVar2, eContestantStatus[contestant].currMove); StringExpandPlaceholders(gStringVar4, gText_MonWasTooNervousToMove); Contest_StartTextPrinter(gStringVar4, TRUE); @@ -2506,7 +2512,7 @@ static void Task_DoAppeals(u8 taskId) return; case APPEALSTATE_PRINT_SKIP_TURN_MSG: ContestClearGeneralTextWindow(); - StringCopy(gStringVar1, gContestMons[contestant].nickname); + CopyNicknameToFit(gStringVar1, contestant); StringExpandPlaceholders(gStringVar4, gText_MonWasWatchingOthers); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_WAIT_SKIP_TURN_MSG; diff --git a/src/contest_util.c b/src/contest_util.c index 7977f373c1..c90113c5ce 100644 --- a/src/contest_util.c +++ b/src/contest_util.c @@ -130,6 +130,7 @@ static void LoadContestResultsTitleBarTilemaps(void); static u8 GetNumPreliminaryPoints(u8, bool8); static s8 GetNumRound2Points(u8, bool8); static void AddContestTextPrinter(int, u8 *, int); +static void AddContestTextPrinterFitWidth(int, u8 *, int, int); static void AllocContestResults(void); static void FreeContestResults(void); static void LoadAllContestMonIcons(u8, u8); @@ -504,7 +505,7 @@ static void LoadContestMonName(u8 monIndex) str = StringCopy(gDisplayedStringBattle, gText_ColorDarkGray); StringCopy(str, mon->nickname); - AddContestTextPrinter(monIndex, gDisplayedStringBattle, 0); + AddContestTextPrinterFitWidth(monIndex, gDisplayedStringBattle, 0, 49); StringCopy(str, gText_Slash); StringAppend(str, mon->trainerName); AddContestTextPrinter(monIndex, gDisplayedStringBattle, 50); @@ -1916,12 +1917,12 @@ static void FreeContestResults(void) FreeMonSpritesGfx(); } -static void AddContestTextPrinter(int windowId, u8 *str, int x) +static void AddContestTextPrinterFitWidth(int windowId, u8 *str, int x, int widthPx) { struct TextPrinterTemplate textPrinter; textPrinter.currentChar = str; textPrinter.windowId = windowId; - textPrinter.fontId = FONT_NARROW; + textPrinter.fontId = GetFontIdToFit(str, FONT_NARROW, 0, widthPx); textPrinter.x = x; textPrinter.y = 2; textPrinter.currentX = x; @@ -1936,6 +1937,11 @@ static void AddContestTextPrinter(int windowId, u8 *str, int x) PutWindowTilemap(windowId); } +static void AddContestTextPrinter(int windowId, u8 *str, int x) +{ + AddContestTextPrinterFitWidth(windowId, str, x, DISPLAY_WIDTH); +} + void TryEnterContestMon(void) { u8 eligibility = GetContestEntryEligibility(&gPlayerParty[gContestMonPartyIndex]); diff --git a/src/hall_of_fame.c b/src/hall_of_fame.c index 2837931ea1..1c3b00f2ef 100644 --- a/src/hall_of_fame.c +++ b/src/hall_of_fame.c @@ -452,8 +452,8 @@ static void Task_Hof_InitMonData(u8 taskId) sHofMonPtr->mon[i].isShiny = GetMonData(&gPlayerParty[i], MON_DATA_IS_SHINY); sHofMonPtr->mon[i].personality = GetMonData(&gPlayerParty[i], MON_DATA_PERSONALITY); sHofMonPtr->mon[i].lvl = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL); - GetMonData(&gPlayerParty[i], MON_DATA_NICKNAME10, nickname); - for (j = 0; j < VANILLA_POKEMON_NAME_LENGTH; j++) + GetMonData(&gPlayerParty[i], MON_DATA_NICKNAME, nickname); + for (j = 0; j < POKEMON_NAME_LENGTH; j++) sHofMonPtr->mon[i].nickname[j] = nickname[j]; gTasks[taskId].tMonNumber++; } @@ -1167,11 +1167,15 @@ static void HallOfFame_PrintMonInfo(struct HallofFameMon* currMon, u8 unused1, u } else { - width = GetStringRightAlignXOffset(FONT_NORMAL, text, 0x80); - AddTextPrinterParameterized3(0, FONT_NORMAL, width, 1, sMonInfoTextColors, TEXT_SKIP_DRAW, text); + u32 fontId = GetFontIdToFit(text, FONT_NORMAL, 0, 66); + width = GetStringRightAlignXOffset(fontId, text, 0x80); + AddTextPrinterParameterized3(0, fontId, width, 1, sMonInfoTextColors, TEXT_SKIP_DRAW, text); text[0] = CHAR_SLASH; - stringPtr = StringCopy(text + 1, GetSpeciesName(currMon->species)); + text[1] = EXT_CTRL_CODE_BEGIN; + text[2] = EXT_CTRL_CODE_FONT; + text[3] = fontId; + stringPtr = StringCopy(text + 4, GetSpeciesName(currMon->species)); if (currMon->species != SPECIES_NIDORAN_M && currMon->species != SPECIES_NIDORAN_F) { diff --git a/src/naming_screen.c b/src/naming_screen.c index 848e5b1a48..d21130284e 100644 --- a/src/naming_screen.c +++ b/src/naming_screen.c @@ -1710,10 +1710,11 @@ static void DrawNormalTextEntryBox(void) static void DrawMonTextEntryBox(void) { - u8 buffer[32]; + u8 buffer[64]; - StringCopy(buffer, GetSpeciesName(sNamingScreen->monSpecies)); - StringAppendN(buffer, sNamingScreen->template->title, 15); + u8 *end = StringCopy(buffer, GetSpeciesName(sNamingScreen->monSpecies)); + WrapFontIdToFit(buffer, end, FONT_NORMAL, 128 - 64); + StringAppendN(end, sNamingScreen->template->title, 15); FillWindowPixelBuffer(sNamingScreen->windows[WIN_TEXT_ENTRY_BOX], PIXEL_FILL(1)); AddTextPrinterParameterized(sNamingScreen->windows[WIN_TEXT_ENTRY_BOX], FONT_NORMAL, buffer, 8, 1, 0, 0); PutWindowTilemap(sNamingScreen->windows[WIN_TEXT_ENTRY_BOX]); diff --git a/src/pokedex.c b/src/pokedex.c index 0eb99d701e..75f8d70720 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -2339,21 +2339,13 @@ static void CreatePokedexList(u8 dexMode, u8 order) static void PrintMonDexNum(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top) { - u8 color[3]; - - color[0] = TEXT_COLOR_TRANSPARENT; - color[1] = TEXT_DYNAMIC_COLOR_6; - color[2] = TEXT_COLOR_LIGHT_GRAY; + static const u8 color[3] = { TEXT_COLOR_TRANSPARENT, TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_LIGHT_GRAY }; AddTextPrinterParameterized4(windowId, fontId, left * 8, (top * 8) + 1, 0, 0, color, TEXT_SKIP_DRAW, str); } static void PrintMonName(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top) { - u8 color[3]; - - color[0] = TEXT_COLOR_TRANSPARENT; - color[1] = TEXT_DYNAMIC_COLOR_6; - color[2] = TEXT_COLOR_LIGHT_GRAY; + static const u8 color[3] = { TEXT_COLOR_TRANSPARENT, TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_LIGHT_GRAY }; fontId = GetFontIdToFit(str, fontId, 0, 50); AddTextPrinterParameterized4(windowId, fontId, left * 8, (top * 8) + 1, 0, 0, color, TEXT_SKIP_DRAW, str); } @@ -4682,7 +4674,7 @@ static void UNUSED UnusedPrintNum(u8 windowId, u16 num, u8 left, u8 top) static u8 PrintCryScreenSpeciesName(u8 windowId, u16 num, u8 left, u8 top) { - u8 str[POKEMON_NAME_LENGTH + 1]; + u8 str[POKEMON_NAME_BUFFER_SIZE]; u8 i; for (i = 0; i < ARRAY_COUNT(str); i++) @@ -4693,6 +4685,7 @@ static u8 PrintCryScreenSpeciesName(u8 windowId, u16 num, u8 left, u8 top) default: for (i = 0; GetSpeciesName(num)[i] != EOS && i < POKEMON_NAME_LENGTH; i++) str[i] = GetSpeciesName(num)[i]; + WrapFontIdToFit(str, str + i, FONT_NORMAL, 60); break; case 0: for (i = 0; i < 5; i++) diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c index b60dfb7daf..111d153d2f 100644 --- a/src/pokemon_storage_system.c +++ b/src/pokemon_storage_system.c @@ -4028,16 +4028,16 @@ static void PrintDisplayMonInfo(void) FillWindowPixelBuffer(WIN_DISPLAY_INFO, PIXEL_FILL(1)); if (sStorage->boxOption != OPTION_MOVE_ITEMS) { - AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_NORMAL, 0, 9*8 - 6), sStorage->displayMonNameText, 6, 0, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_SHORT, 0, 9*8 - 12), sStorage->displayMonSpeciesName, 6, 15, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_NORMAL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 6), sStorage->displayMonNameText, 6, 0, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_SHORT, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 12), sStorage->displayMonSpeciesName, 6, 15, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 29, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonItemName, FONT_SMALL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 22), sStorage->displayMonItemName, 6, 43, TEXT_SKIP_DRAW, NULL); } else { AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonItemName, FONT_SMALL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 22), sStorage->displayMonItemName, 6, 0, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_NORMAL, 0, 9*8 - 6), sStorage->displayMonNameText, 6, 13, TEXT_SKIP_DRAW, NULL); - AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonSpeciesName, FONT_SHORT, 0, 9*8 - 12), sStorage->displayMonSpeciesName, 6, 28, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonNameText, FONT_NORMAL, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 6), sStorage->displayMonNameText, 6, 13, TEXT_SKIP_DRAW, NULL); + AddTextPrinterParameterized(WIN_DISPLAY_INFO, GetFontIdToFit(sStorage->displayMonSpeciesName, FONT_SHORT, 0, WindowWidthPx(WIN_DISPLAY_INFO) - 12), sStorage->displayMonSpeciesName, 6, 28, TEXT_SKIP_DRAW, NULL); AddTextPrinterParameterized(WIN_DISPLAY_INFO, FONT_SHORT, sStorage->displayMonGenderLvlText, 10, 42, TEXT_SKIP_DRAW, NULL); } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 5132218931..f13e57ed8e 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2873,9 +2873,9 @@ static void PrintNotEggInfo(void) StringAppend(gStringVar1, gStringVar2); PrintTextOnWindow(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, gStringVar1, 24, 17, 0, 1); GetMonNickname(mon, gStringVar1); - PrintTextOnWindowToFitPx(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME, gStringVar1, 0, 1, 0, 1, 63); + PrintTextOnWindowToFitPx(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME, gStringVar1, 0, 1, 0, 1, WindowWidthPx(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME) - 9); PrintTextOnWindow(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, gText_Slash, 0, 1, 0, 1); - PrintTextOnWindowToFitPx(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, GetSpeciesName(summary->species2), 6, 1, 0, 1, 63); + PrintTextOnWindowToFitPx(PSS_LABEL_WINDOW_PORTRAIT_SPECIES, GetSpeciesName(summary->species2), 6, 1, 0, 1, WindowWidthPx(PSS_LABEL_WINDOW_PORTRAIT_SPECIES) - 9); PrintGenderSymbol(mon, summary->species2); PutWindowTilemap(PSS_LABEL_WINDOW_PORTRAIT_NICKNAME); PutWindowTilemap(PSS_LABEL_WINDOW_PORTRAIT_SPECIES); diff --git a/src/pokenav_conditions.c b/src/pokenav_conditions.c index 19cfe38b95..382ddde413 100644 --- a/src/pokenav_conditions.c +++ b/src/pokenav_conditions.c @@ -372,6 +372,8 @@ static u8 *CopyConditionMonNameGender(u8 *str, u16 listId, bool8 skipPadding) while (*str_ != EOS) (str_++); + str_ = WrapFontIdToFit(str, str_, FONT_NORMAL, 57); + *(str_++) = EXT_CTRL_CODE_BEGIN; *(str_++) = EXT_CTRL_CODE_SKIP; *(str_++) = 60; diff --git a/src/pokenav_conditions_search_results.c b/src/pokenav_conditions_search_results.c index 24fe966dc3..9afb2bb2d9 100644 --- a/src/pokenav_conditions_search_results.c +++ b/src/pokenav_conditions_search_results.c @@ -692,8 +692,9 @@ static void BufferSearchMonListItem(struct PokenavMonListItem * item, u8 *dest) { u8 gender; u8 level; - u8 *s; + u8 *s, *end; const u8 *genderStr; + u32 fontId; // Mon is in party if (item->boxId == TOTAL_BOXES_COUNT) @@ -712,8 +713,6 @@ static void BufferSearchMonListItem(struct PokenavMonListItem * item, u8 *dest) GetBoxMonData(mon, MON_DATA_NICKNAME, gStringVar3); } - StringGet_Nickname(gStringVar3); - dest = GetStringClearToWidth(dest, FONT_NORMAL, gStringVar3, 60); switch (gender) { default: @@ -726,6 +725,11 @@ static void BufferSearchMonListItem(struct PokenavMonListItem * item, u8 *dest) genderStr = sText_FemaleSymbol; break; } + end = StringGet_Nickname(gStringVar3); + fontId = GetFontIdToFit(gStringVar3, FONT_NORMAL, 0, 60); + WrapFontIdToFit(gStringVar3, end, FONT_NORMAL, 60); + dest = GetStringClearToWidth(dest, fontId, gStringVar3, 60); + s = StringCopy(gStringVar1, genderStr); *s++ = CHAR_SLASH; *s++ = CHAR_EXTRA_SYMBOL; diff --git a/src/pokenav_ribbons_list.c b/src/pokenav_ribbons_list.c index 72783b0972..834dc926ad 100644 --- a/src/pokenav_ribbons_list.c +++ b/src/pokenav_ribbons_list.c @@ -699,9 +699,10 @@ static void BufferRibbonMonInfoText(struct PokenavListItem * listItem, u8 *dest) { u8 gender; u8 level; - u8 *s; + u8 *s, *end; const u8 *genderStr; struct PokenavMonListItem * item = (struct PokenavMonListItem *)listItem; + u32 fontId; // Mon is in party if (item->boxId == TOTAL_BOXES_COUNT) @@ -720,8 +721,6 @@ static void BufferRibbonMonInfoText(struct PokenavListItem * listItem, u8 *dest) GetBoxMonData(mon, MON_DATA_NICKNAME, gStringVar3); } - StringGet_Nickname(gStringVar3); - dest = GetStringClearToWidth(dest, FONT_NORMAL, gStringVar3, 60); switch (gender) { default: @@ -734,6 +733,10 @@ static void BufferRibbonMonInfoText(struct PokenavListItem * listItem, u8 *dest) genderStr = sText_FemaleSymbol; break; } + end = StringGet_Nickname(gStringVar3); + fontId = GetFontIdToFit(gStringVar3, FONT_NORMAL, 0, 60); + WrapFontIdToFit(gStringVar3, end, FONT_NORMAL, 60); + dest = GetStringClearToWidth(dest, fontId, gStringVar3, 60); s = StringCopy(gStringVar1, genderStr); *s++ = CHAR_SLASH; diff --git a/src/pokenav_ribbons_summary.c b/src/pokenav_ribbons_summary.c index 1ff55dc187..bba1c3600c 100644 --- a/src/pokenav_ribbons_summary.c +++ b/src/pokenav_ribbons_summary.c @@ -878,7 +878,6 @@ static void PrintRibbbonsSummaryMonInfo(struct Pokenav_RibbonsSummaryMenu *menu) FillWindowPixelBuffer(windowId, PIXEL_FILL(1)); GetMonNicknameLevelGender(gStringVar3, &level, &gender); - AddTextPrinterParameterized(windowId, FONT_NORMAL, gStringVar3, 0, 1, TEXT_SKIP_DRAW, NULL); switch (gender) { case MON_MALE: @@ -891,6 +890,7 @@ static void PrintRibbbonsSummaryMonInfo(struct Pokenav_RibbonsSummaryMenu *menu) genderTxt = sGenderlessIconString; break; } + AddTextPrinterParameterized(windowId, GetFontIdToFit(gStringVar3, FONT_NORMAL, 0, 60), gStringVar3, 0, 1, TEXT_SKIP_DRAW, NULL); txtPtr = StringCopy(gStringVar1, genderTxt); *(txtPtr++) = CHAR_SLASH; diff --git a/test/text.c b/test/text.c index 19a0a88b40..5b461f39e4 100644 --- a/test/text.c +++ b/test/text.c @@ -334,6 +334,21 @@ TEST("Species names fit on Pokedex Screen") EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); } +TEST("Species names fit on Pokedex Screen - Cries") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 60; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + TEST("Species names fit on Pokemon Storage System") { u32 i; @@ -363,3 +378,123 @@ TEST("Species names fit on Contest Screen") } EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); } + +TEST("Species names fit on Contest Screen - Rankings") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 49; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Battle Dome Screen") +{ + u32 i; + const u32 fontId = FONT_SHORT_NARROW, widthPx = 60; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Hall of Fame") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 66; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on Naming Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 64; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on PokeNav Condition Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 57; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on PokeNav Condition Search Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 60; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on PokeNav Ribbon Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 60; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} + +TEST("Species names fit on PokeNav Ribbon List Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 60; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + { + PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } + } + } + EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); +} From 30efba0ccdfe5449f1fa17fa5205d949a5f83068 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sat, 20 Apr 2024 17:14:37 +0100 Subject: [PATCH 30/71] Support GF abilities names --- src/battle_interface.c | 4 +++- test/text.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index df96fc1b17..e7734a9f43 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3038,6 +3038,7 @@ static const s16 sAbilityPopUpCoordsSingles[MAX_BATTLERS_COUNT][2] = static u8* AddTextPrinterAndCreateWindowOnAbilityPopUp(const u8 *str, u32 x, u32 y, u32 color1, u32 color2, u32 color3, u32 *windowId) { + u32 fontId; u8 color[3] = {color1, color2, color3}; struct WindowTemplate winTemplate = {0}; winTemplate.width = POPUP_WINDOW_WIDTH; @@ -3046,7 +3047,8 @@ static u8* AddTextPrinterAndCreateWindowOnAbilityPopUp(const u8 *str, u32 x, u32 *windowId = AddWindow(&winTemplate); FillWindowPixelBuffer(*windowId, PIXEL_FILL(color1)); - AddTextPrinterParameterized4(*windowId, FONT_SMALL, x, y, 0, 0, color, TEXT_SKIP_DRAW, str); + fontId = GetFontIdToFit(str, FONT_SMALL, 0, 76); + AddTextPrinterParameterized4(*windowId, fontId, x, y, 0, 0, color, TEXT_SKIP_DRAW, str); return (u8 *)(GetWindowAttribute(*windowId, WINDOW_TILE_DATA)); } diff --git a/test/text.c b/test/text.c index 5b461f39e4..9766433a8e 100644 --- a/test/text.c +++ b/test/text.c @@ -2,6 +2,7 @@ #include "test/test.h" #include "item.h" #include "text.h" +#include "constants/abilities.h" #include "constants/items.h" #include "constants/moves.h" @@ -498,3 +499,27 @@ TEST("Species names fit on PokeNav Ribbon List Screen") } EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx); } + +TEST("Ability names fit on Pokemon Summary Screen") +{ + u32 i; + const u32 fontId = FONT_NORMAL, widthPx = 144; + u32 ability = ABILITY_NONE; + for (i = 1; i < ABILITIES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gAbilitiesInfo[i].name) { ability = i; } + } + EXPECT_LE(GetStringWidth(fontId, gAbilitiesInfo[ability].name, 0), widthPx); +} + +TEST("Ability names fit on Ability Pop-Up") +{ + u32 i; + const u32 fontId = FONT_SMALL_NARROWER, widthPx = 76; + u32 ability = ABILITY_NONE; + for (i = 1; i < ABILITIES_COUNT; i++) + { + PARAMETRIZE_LABEL("%S", gAbilitiesInfo[i].name) { ability = i; } + } + EXPECT_LE(GetStringWidth(fontId, gAbilitiesInfo[ability].name, 0), widthPx); +} From 073f12be18c47b33ec0ac7c93eb6534240f0e851 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 25 Apr 2024 06:51:02 +0100 Subject: [PATCH 31/71] Expanded type names --- include/config/battle.h | 1 + include/constants/global.h | 2 +- src/battle_main.c | 14 ++++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index f2c17f91f6..16f8586614 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -39,6 +39,7 @@ #define B_MULTIPLE_TARGETS_DMG GEN_LATEST // In Gen4+, damage dealt by moves that hit multiple targets at once is reduced to 75%. Before, it was 50%. // Type settings +#define B_EXPANDED_TYPE_NAMES FALSE // If TRUE, type names are increased from 6 characters to 8 characters. #define B_GHOSTS_ESCAPE GEN_LATEST // In Gen6+, abilities like Shadow Tag or moves like Mean Look fail on Ghost-type Pokémon. They can also escape any Wild Battle. #define B_PARALYZE_ELECTRIC GEN_LATEST // In Gen6+, Electric-type Pokémon can't be paralyzed. #define B_POWDER_GRASS GEN_LATEST // In Gen6+, Grass-type Pokémon are immune to powder and spore moves. diff --git a/include/constants/global.h b/include/constants/global.h index 4555f08f21..8b91af77fa 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -117,7 +117,7 @@ #define WONDER_NEWS_TEXT_LENGTH 40 #define WONDER_CARD_BODY_TEXT_LINES 4 #define WONDER_NEWS_BODY_TEXT_LINES 10 -#define TYPE_NAME_LENGTH 6 +#define TYPE_NAME_LENGTH ((B_EXPANDED_TYPE_NAMES == TRUE) ? 8 : 6) #define ABILITY_NAME_LENGTH ((B_EXPANDED_ABILITY_NAMES == TRUE) ? 16 : 12) #define TRAINER_NAME_LENGTH 10 diff --git a/src/battle_main.c b/src/battle_main.c index eba4916ef4..30154ef172 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -295,6 +295,12 @@ const struct OamData gOamData_BattleSpritePlayerSide = static const s8 sCenterToCornerVecXs[8] ={-32, -16, -16, -32, -32}; +#if B_EXPANDED_TYPE_NAMES == TRUE +#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__)) +#else +#define HANDLE_EXPANDED_TYPE_NAME(_name) _(_name) +#endif + // .generic is large enough that the text for TYPE_ELECTRIC will exceed TEXT_BUFF_ARRAY_COUNT. const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = { @@ -314,7 +320,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = }, [TYPE_FIGHTING] = { - .name = _("Fight"), + .name = HANDLE_EXPANDED_TYPE_NAME("Fight", "Fighting"), .generic = _("a FIGHTING move"), .palette = 13, .zMove = MOVE_ALL_OUT_PUMMELING, @@ -496,7 +502,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = }, [TYPE_ELECTRIC] = { - .name = _("Electr"), + .name = HANDLE_EXPANDED_TYPE_NAME("Electr", "Electric"), .generic = _("an ELECTRIC move"), .palette = 13, .zMove = MOVE_GIGAVOLT_HAVOC, @@ -512,7 +518,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = }, [TYPE_PSYCHIC] = { - .name = _("Psychc"), + .name = HANDLE_EXPANDED_TYPE_NAME("Psychc", "Psychic"), .generic = _("a PSYCHIC move"), .palette = 14, .zMove = MOVE_SHATTERED_PSYCHE, @@ -592,7 +598,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = }, [TYPE_STELLAR] = { - .name = _("Stellr"), + .name = HANDLE_EXPANDED_TYPE_NAME("Stellr", "Stellar"), .generic = _("a STELLAR move"), .palette = 15, .zMove = MOVE_BREAKNECK_BLITZ, From 664fe905f64f1219a6d2be385379ab8719ac22ce Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 25 Apr 2024 07:05:51 +0100 Subject: [PATCH 32/71] Enable GF type names by default --- include/config/battle.h | 2 +- include/config/test.h | 2 ++ src/battle_controller_player.c | 8 +++----- src/pokedex.c | 17 ++++++++++------- test/battle/terrain/electric.c | 2 +- test/battle/terrain/psychic.c | 2 +- test/text.c | 25 +++++++++++++++++++++++++ 7 files changed, 43 insertions(+), 15 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index 16f8586614..b6c55e8d24 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -39,7 +39,7 @@ #define B_MULTIPLE_TARGETS_DMG GEN_LATEST // In Gen4+, damage dealt by moves that hit multiple targets at once is reduced to 75%. Before, it was 50%. // Type settings -#define B_EXPANDED_TYPE_NAMES FALSE // If TRUE, type names are increased from 6 characters to 8 characters. +#define B_EXPANDED_TYPE_NAMES TRUE // If TRUE, type names are increased from 6 characters to 8 characters. #define B_GHOSTS_ESCAPE GEN_LATEST // In Gen6+, abilities like Shadow Tag or moves like Mean Look fail on Ghost-type Pokémon. They can also escape any Wild Battle. #define B_PARALYZE_ELECTRIC GEN_LATEST // In Gen6+, Electric-type Pokémon can't be paralyzed. #define B_POWDER_GRASS GEN_LATEST // In Gen6+, Grass-type Pokémon are immune to powder and spore moves. diff --git a/include/config/test.h b/include/config/test.h index cba8012d36..4101f7a559 100644 --- a/include/config/test.h +++ b/include/config/test.h @@ -7,5 +7,7 @@ #define I_EXPANDED_ITEM_NAMES TRUE #undef POKEMON_NAME_LENGTH #define POKEMON_NAME_LENGTH 12 +#undef B_EXPANDED_TYPE_NAMES +#define B_EXPANDED_TYPE_NAMES TRUE #endif // GUARD_CONFIG_TEST_H diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 10a91a1485..b86da34d24 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1745,16 +1745,13 @@ static void MoveSelectionDisplayPpNumber(u32 battler) static void MoveSelectionDisplayMoveType(u32 battler) { - u8 *txtPtr; + u8 *txtPtr, *end; u8 type; u32 speciesId; struct Pokemon *mon; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); - *(txtPtr)++ = EXT_CTRL_CODE_BEGIN; - *(txtPtr)++ = EXT_CTRL_CODE_FONT; - *(txtPtr)++ = FONT_NORMAL; if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) { @@ -1771,7 +1768,8 @@ static void MoveSelectionDisplayMoveType(u32 battler) else type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; - StringCopy(txtPtr, gTypesInfo[type].name); + end = StringCopy(txtPtr, gTypesInfo[type].name); + PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE); } diff --git a/src/pokedex.c b/src/pokedex.c index 75f8d70720..7c39b2502c 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -5008,13 +5008,16 @@ static u8 LoadSearchMenu(void) return CreateTask(Task_LoadSearchMenu, 0); } +static void PrintSearchTextToFit(const u8 *str, u32 x, u32 y, u32 width) +{ + static const u8 color[3] = { TEXT_COLOR_TRANSPARENT, TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_DARK_GRAY }; + u32 fontId = GetFontIdToFit(str, FONT_NORMAL, 0, width); + AddTextPrinterParameterized4(0, fontId, x, y, 0, 0, color, TEXT_SKIP_DRAW, str); +} + static void PrintSearchText(const u8 *str, u32 x, u32 y) { - u8 color[3]; - - color[0] = TEXT_COLOR_TRANSPARENT; - color[1] = TEXT_DYNAMIC_COLOR_6; - color[2] = TEXT_COLOR_DARK_GRAY; + static const u8 color[3] = { TEXT_COLOR_TRANSPARENT, TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_DARK_GRAY }; AddTextPrinterParameterized4(0, FONT_NORMAL, x, y, 0, 0, color, TEXT_SKIP_DRAW, str); } @@ -5634,10 +5637,10 @@ static void PrintSelectedSearchParameters(u8 taskId) PrintSearchText(sDexSearchColorOptions[searchParamId].title, 0x2D, 0x21); searchParamId = gTasks[taskId].tCursorPos_TypeLeft + gTasks[taskId].tScrollOffset_TypeLeft; - PrintSearchText(sDexSearchTypeOptions[searchParamId].title, 0x2D, 0x31); + PrintSearchTextToFit(sDexSearchTypeOptions[searchParamId].title, 0x2D, 0x31, 38); searchParamId = gTasks[taskId].tCursorPos_TypeRight + gTasks[taskId].tScrollOffset_TypeRight; - PrintSearchText(sDexSearchTypeOptions[searchParamId].title, 0x5D, 0x31); + PrintSearchTextToFit(sDexSearchTypeOptions[searchParamId].title, 0x5D, 0x31, 38); searchParamId = gTasks[taskId].tCursorPos_Order + gTasks[taskId].tScrollOffset_Order; PrintSearchText(sDexOrderOptions[searchParamId].title, 0x2D, 0x41); diff --git a/test/battle/terrain/electric.c b/test/battle/terrain/electric.c index eb7a112bb5..2227afdcd9 100644 --- a/test/battle/terrain/electric.c +++ b/test/battle/terrain/electric.c @@ -32,7 +32,7 @@ SINGLE_BATTLE_TEST("Electric Terrain activates Electric Seed and Mimicry") ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Using Electric Seed, the Defense of Wobbuffet rose!"); ABILITY_POPUP(opponent); - MESSAGE("Foe Stunfisk's type changed to Electr!"); + MESSAGE("Foe Stunfisk's type changed to Electric!"); } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_ELECTRIC); } diff --git a/test/battle/terrain/psychic.c b/test/battle/terrain/psychic.c index 1881db6297..0c13122fa6 100644 --- a/test/battle/terrain/psychic.c +++ b/test/battle/terrain/psychic.c @@ -31,7 +31,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain activates Psychic Seed and Mimicry") ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Using Psychic Seed, the Sp. Def of Wobbuffet rose!"); ABILITY_POPUP(opponent); - MESSAGE("Foe Stunfisk's type changed to Psychc!"); + MESSAGE("Foe Stunfisk's type changed to Psychic!"); } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_PSYCHIC); } diff --git a/test/text.c b/test/text.c index 9766433a8e..e8eb4f7f24 100644 --- a/test/text.c +++ b/test/text.c @@ -1,5 +1,6 @@ #include "global.h" #include "test/test.h" +#include "battle_main.h" #include "item.h" #include "text.h" #include "constants/abilities.h" @@ -523,3 +524,27 @@ TEST("Ability names fit on Ability Pop-Up") } EXPECT_LE(GetStringWidth(fontId, gAbilitiesInfo[ability].name, 0), widthPx); } + +TEST("Type names fit on Battle Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 39; + u32 type = TYPE_NORMAL; + for (i = 0; i < NUMBER_OF_MON_TYPES; i++) + { + PARAMETRIZE_LABEL("%S", gTypesInfo[i].name) { type = i; } + } + EXPECT_LE(GetStringWidth(fontId, gTypesInfo[type].name, 0), widthPx); +} + +TEST("Type names fit on Pokedex Search Screen") +{ + u32 i; + const u32 fontId = FONT_NARROWER, widthPx = 38; + u32 type = TYPE_NORMAL; + for (i = 0; i < NUMBER_OF_MON_TYPES; i++) + { + PARAMETRIZE_LABEL("%S", gTypesInfo[i].name) { type = i; } + } + EXPECT_LE(GetStringWidth(fontId, gTypesInfo[type].name, 0), widthPx); +} From 4935b2b3ad4363e0589ea2e5ea4e90cee12d5a3f Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Thu, 25 Apr 2024 23:24:10 +0200 Subject: [PATCH 33/71] Update pokedex_plus_hgss.c (#4445) --- src/pokedex_plus_hgss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index debf4d7fe0..abc1ba410a 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -385,7 +385,7 @@ struct PokemonStats u8 eggGroup1; u8 eggGroup2; u8 eggCycles; - u16 expYield; + u16 expYield; u8 friendship; u16 ability0; u16 ability1; From ec37b2170f1fa1e2b27f2329592c1275484398a0 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:29:26 +0200 Subject: [PATCH 34/71] Fixes Hospitality triggering on a fainted mon (#4450) --- src/battle_util.c | 5 ++++- test/battle/ability/hospitality.c | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index c68959a8ff..498c8c0abb 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4724,7 +4724,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_HOSPITALITY: partner = BATTLE_PARTNER(battler); - if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() && gBattleMons[partner].hp < gBattleMons[partner].maxHP) + if (!gSpecialStatuses[battler].switchInAbilityDone + && IsDoubleBattle() + && gBattleMons[partner].hp < gBattleMons[partner].maxHP + && IsBattlerAlive(partner)) { gBattlerTarget = partner; gBattlerAttacker = battler; diff --git a/test/battle/ability/hospitality.c b/test/battle/ability/hospitality.c index 74648b0c9a..c40512d5be 100644 --- a/test/battle/ability/hospitality.c +++ b/test/battle/ability/hospitality.c @@ -68,3 +68,24 @@ DOUBLE_BATTLE_TEST("Hospitality ignores Substitute") MESSAGE("Wobbuffet drank down all the matcha that Ptchageist made!"); } } + +DOUBLE_BATTLE_TEST("Hospitality does not trigger if there is no ally on the field") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_POLTCHAGEIST) { Ability(ABILITY_HOSPITALITY); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_BLIZZARD); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BLIZZARD, opponentLeft); + HP_BAR(playerLeft); + MESSAGE("Wobbuffet fainted!"); + HP_BAR(playerRight); + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Go! Ptchageist!"); + NOT ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); + } +} From 2648618c048b56473b5d3167e6d8acc6ecaba121 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:54:23 -0400 Subject: [PATCH 35/71] ally switch fix elevation shadow and direction on opposing side (#4451) Co-authored-by: ghoulslash --- include/battle_gfx_sfx_util.h | 1 + src/battle_anim_effects_1.c | 19 +++++++++++++++++-- src/battle_gfx_sfx_util.c | 35 +++++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/include/battle_gfx_sfx_util.h b/include/battle_gfx_sfx_util.h index 95c97cabaa..968f8d48dc 100644 --- a/include/battle_gfx_sfx_util.h +++ b/include/battle_gfx_sfx_util.h @@ -31,6 +31,7 @@ void BattleStopLowHpSound(void); u8 GetMonHPBarLevel(struct Pokemon *mon); void HandleBattleLowHpMusicChange(void); void SetBattlerSpriteAffineMode(u8 affineMode); +void CreateEnemyShadowSprite(u32 battler); void LoadAndCreateEnemyShadowSprites(void); void SpriteCB_SetInvisible(struct Sprite *sprite); void SetBattlerShadowSpriteCallback(u8 battler, u16 species); diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index abcdd44c35..7ce0e0c36c 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -6523,6 +6523,11 @@ void PrepareDoubleTeamAnim(u32 taskId, u32 animBattler, bool32 forAllySwitch) gSprites[spriteId].sBattlerFlank = (animBattler != ANIM_ATTACKER); else gSprites[spriteId].sBattlerFlank = (animBattler == ANIM_ATTACKER); + + // correct direction on opponent side + if (GetBattlerSide(gBattleAnimAttacker) == B_SIDE_OPPONENT) + gSprites[spriteId].sBattlerFlank ^= 1; + gSprites[spriteId].callback = AnimDoubleTeam; task->tBlendSpritesCount++; } @@ -6548,11 +6553,21 @@ static inline void SwapStructData(void *s1, void *s2, void *data, u32 size) static void ReloadBattlerSprites(u32 battler, struct Pokemon *party) { - BattleLoadMonSpriteGfx(&party[gBattlerPartyIndexes[battler]], battler); + struct Pokemon *mon = &party[gBattlerPartyIndexes[battler]]; + BattleLoadMonSpriteGfx(mon, battler); CreateBattlerSprite(battler); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &party[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); // If battler is mega evolved / primal reversed, hide the sprite until the move animation finishes. MegaIndicator_SetVisibilities(gHealthboxSpriteIds[battler], TRUE); + + // Try to recreate shadow sprite + if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId < MAX_SPRITES) + { + DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId]); + gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId = MAX_SPRITES; + CreateEnemyShadowSprite(battler); + SetBattlerShadowSpriteCallback(battler, GetMonData(mon, MON_DATA_SPECIES)); + } } static void AnimTask_AllySwitchDataSwap(u8 taskId) diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 0a4eaa07dd..91861b3b4a 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -1108,27 +1108,36 @@ void SetBattlerSpriteAffineMode(u8 affineMode) #define tBattlerId data[0] -void LoadAndCreateEnemyShadowSprites(void) +void CreateEnemyShadowSprite(u32 battler) { - u8 battler; - - LoadCompressedSpriteSheet(&gSpriteSheet_EnemyShadow); - - battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId = CreateSprite(&gSpriteTemplate_EnemyShadow, GetBattlerSpriteCoord(battler, BATTLER_COORD_X), GetBattlerSpriteCoord(battler, BATTLER_COORD_Y) + 29, 0xC8); - gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId].data[0] = battler; + if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId < MAX_SPRITES) + gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId].data[0] = battler; +} +void LoadAndCreateEnemyShadowSprites(void) +{ + u8 battler; + u32 i; + + LoadCompressedSpriteSheet(&gSpriteSheet_EnemyShadow); + + // initialize shadow sprite ids + for (i = 0; i < gBattlersCount; i++) + { + gBattleSpritesDataPtr->healthBoxesData[i].shadowSpriteId = MAX_SPRITES; + } + + battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + CreateEnemyShadowSprite(battler); + if (IsDoubleBattle()) { battler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); - gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId = CreateSprite(&gSpriteTemplate_EnemyShadow, - GetBattlerSpriteCoord(battler, BATTLER_COORD_X), - GetBattlerSpriteCoord(battler, BATTLER_COORD_Y) + 29, - 0xC8); - gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId].data[0] = battler; + CreateEnemyShadowSprite(battler); } } @@ -1169,6 +1178,8 @@ void SetBattlerShadowSpriteCallback(u8 battler, u16 species) // The player's shadow is never seen. if (GetBattlerSide(battler) == B_SIDE_PLAYER || gBattleScripting.monCaught) return; + if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId >= MAX_SPRITES) + return; if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies != SPECIES_NONE) species = gBattleSpritesDataPtr->battlerData[battler].transformSpecies; From 7355eb99a5bca70aa7e724a341d54bf8e4f8adc4 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 27 Apr 2024 13:36:29 +0200 Subject: [PATCH 36/71] Fix infinite loop caused by leftovers with AI_FLAG_SMART_CHOICES (#4453) --- src/battle_ai_switch_items.c | 2 +- test/battle/ai.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index f08251e2a5..273fb2c998 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1535,7 +1535,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler) s32 currentHP = startingHP; // No damage being dealt - if ((damageTaken + statusDamage + recurringDamage < recurringHealing) || damageTaken + statusDamage + recurringDamage == 0) + if ((damageTaken + statusDamage + recurringDamage <= recurringHealing) || damageTaken + statusDamage + recurringDamage == 0) return startingHP; // Mon fainted to hazards diff --git a/test/battle/ai.c b/test/battle/ai.c index 0597925356..0d890da466 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -548,6 +548,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculati AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is zero to avoid an infinite loop") { GIVEN { + ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); PLAYER(SPECIES_BULBASAUR) { Level(5); Moves(MOVE_SWORDS_DANCE, MOVE_WHIRLWIND, MOVE_SAND_ATTACK, MOVE_TAIL_WHIP); } // Scenario courtesy of Duke, who triggered the bug in the first place @@ -561,6 +562,23 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculati } } +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Avoid infinite loop if damage taken is equal to recurring healing") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); + PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); } + // Scenario courtesy of Duke, who triggered the bug in the first place + OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); } + OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); } + OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); } + OPPONENT(SPECIES_NOSEPASS) { Level(5); Moves(MOVE_DOUBLE_EDGE); } + OPPONENT(SPECIES_HOUNDSTONE) { Level(5); Moves(MOVE_NIGHT_SHADE, MOVE_BODY_PRESS, MOVE_WILL_O_WISP, MOVE_PROTECT); Item(ITEM_LEFTOVERS); } + } WHEN { + TURN { MOVE(player, MOVE_FAKE_OUT); EXPECT_MOVES(opponent, MOVE_FAKE_OUT); } + } +} + AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemon which is slower and gets 1HKOed after fainting") { bool32 alakazamFirst; From 64b28124fb33f8c340b4ac312d99486b0a2bc902 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 27 Apr 2024 12:35:29 -0500 Subject: [PATCH 37/71] Add Tera Starstorm move + make Tera Blast displayed type reflect current type due to tera state (#4447) * Add Tera Starstorm move + make Tera Blast/Tera Starstorm displayed type reflect tera type * Ooops * Curse tests --- include/constants/battle_move_effects.h | 1 + src/battle_controller_player.c | 37 ++++++++++++------------- src/battle_gfx_sfx_util.c | 12 +------- src/battle_main.c | 7 ++++- src/battle_script_commands.c | 3 +- src/battle_util.c | 24 ++++++++-------- src/data/battle_move_effects.h | 6 ++++ src/data/moves_info.h | 6 ++-- src/data/pokemon/form_species_tables.h | 2 +- test/battle/move_effect/curse.c | 36 ++++++++++++++++++++++++ 10 files changed, 86 insertions(+), 48 deletions(-) create mode 100644 test/battle/move_effect/curse.c diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index b01685fbb7..abb1a9ab0f 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -351,6 +351,7 @@ enum { EFFECT_LAST_RESPECTS, EFFECT_TIDY_UP, EFFECT_TERA_BLAST, + EFFECT_TERA_STARSTORM, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index b86da34d24..be6a63e00b 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -693,17 +693,8 @@ static void HandleInputChooseMove(u32 battler) if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); - if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_CURSE) - { - if (moveInfo->monType1 != TYPE_GHOST && moveInfo->monType2 != TYPE_GHOST && moveInfo->monType3 != TYPE_GHOST) - moveTarget = MOVE_TARGET_USER; - else - moveTarget = MOVE_TARGET_SELECTED; - } - else - { - moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[gMoveSelectionCursor[battler]]); - } + + moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[gMoveSelectionCursor[battler]]); if (gBattleStruct->zmove.viewing) { @@ -931,6 +922,7 @@ static void HandleInputChooseMove(u32 battler) { gBattleStruct->tera.playerSelect ^= 1; ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, gBattleStruct->tera.playerSelect); + MoveSelectionDisplayMoveType(battler); // For Tera Blast / Tera Starstorm PlaySE(SE_SELECT); } } @@ -1748,25 +1740,32 @@ static void MoveSelectionDisplayMoveType(u32 battler) u8 *txtPtr, *end; u8 type; u32 speciesId; - struct Pokemon *mon; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); - if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) + type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; + + if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST) { - mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]]; - speciesId = GetMonData(mon, MON_DATA_SPECIES); + if (gBattleStruct->tera.playerSelect || IsTerastallized(battler)) + type = GetBattlerTeraType(battler); + } + else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) + { + speciesId = gBattleMons[battler].species; if (speciesId == SPECIES_OGERPON_WELLSPRING_MASK || speciesId == SPECIES_OGERPON_WELLSPRING_MASK_TERA || speciesId == SPECIES_OGERPON_HEARTHFLAME_MASK || speciesId == SPECIES_OGERPON_HEARTHFLAME_MASK_TERA || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK_TERA) type = gBattleMons[battler].type2; - else - type = gMovesInfo[MOVE_IVY_CUDGEL].type; } - else - type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; + else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM) + { + if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR + || (gBattleStruct->tera.playerSelect && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL)) + type = TYPE_STELLAR; + } end = StringCopy(txtPtr, gTypesInfo[type].name); PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25); diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 91861b3b4a..8cc5afb424 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -263,17 +263,7 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler) } } - if (moveInfo->moves[chosenMoveId] == MOVE_CURSE) - { - if (moveInfo->monType1 != TYPE_GHOST && moveInfo->monType2 != TYPE_GHOST && moveInfo->monType3 != TYPE_GHOST) - moveTarget = MOVE_TARGET_USER; - else - moveTarget = MOVE_TARGET_SELECTED; - } - else - { - moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]); - } + moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]); if (moveTarget & MOVE_TARGET_USER) chosenMoveId |= (battler << 8); diff --git a/src/battle_main.c b/src/battle_main.c index 30154ef172..d7bbb764bf 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5341,7 +5341,7 @@ static void PopulateArrayWithBattlers(u8 *battlers) static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) - && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst + && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst || gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera)) { u32 i, battler; @@ -6072,6 +6072,10 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) { gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET; } + else if (gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR) + { + gBattleStruct->dynamicMoveType = TYPE_STELLAR | F_DYNAMIC_TYPE_SET; + } attackerAbility = GetBattlerAbility(battlerAtk); @@ -6081,6 +6085,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) + && !(gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR) && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ce8a163505..bfbb2b3ca0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16735,7 +16735,8 @@ void BS_AllySwitchFailChance(void) void BS_SetPhotonGeyserCategory(void) { NATIVE_ARGS(); - if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))) + if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker)) + && !(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && gBattleMons[gBattlerAttacker].species != SPECIES_TERAPAGOS_STELLAR)) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_util.c b/src/battle_util.c index 6e82195b34..6d6ce38a7c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8043,10 +8043,6 @@ u32 GetMoveTarget(u16 move, u8 setTarget) else moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); - // Special cases - if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) - moveTarget = MOVE_TARGET_USER; - switch (moveTarget) { case MOVE_TARGET_SELECTED: @@ -10104,6 +10100,8 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(1.0); if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) mod = UQ_4_12(2.0); + if (moveType == TYPE_STELLAR && IsTerastallized(battlerDef)) + mod = UQ_4_12(2.0); // B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon if (gBattleWeather & B_WEATHER_STRONG_WINDS && WEATHER_HAS_EFFECT) @@ -10220,16 +10218,12 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, { uq4_12_t modifier = UQ_4_12(1.0); - if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY && moveType != TYPE_STELLAR) + if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) { modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); if (gMovesInfo[move].effect == EFFECT_TWO_TYPED_MOVE) modifier = CalcTypeEffectivenessMultiplierInternal(move, gMovesInfo[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); } - else if (moveType == TYPE_STELLAR) - { - modifier = IsTerastallized(battlerDef) ? UQ_4_12(2.0) : UQ_4_12(1.0); - } if (recordAbilities) UpdateMoveResultFlags(modifier); @@ -11173,11 +11167,17 @@ bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) // Possible return values are defined in battle.h following MOVE_TARGET_SELECTED u32 GetBattlerMoveTargetType(u32 battler, u32 move) { - if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE + if (move == MOVE_CURSE + && !IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) + return MOVE_TARGET_USER; + else if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE && IsBattlerTerrainAffected(battler, STATUS_FIELD_PSYCHIC_TERRAIN)) return MOVE_TARGET_BOTH; - else - return gMovesInfo[move].target; + else if (gMovesInfo[move].effect == EFFECT_TERA_STARSTORM + && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) + return MOVE_TARGET_BOTH; + + return gMovesInfo[move].target; } bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move) diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index de8d3f9ec8..29acfc4630 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2236,4 +2236,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectPhotonGeyser, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_TERA_STARSTORM] = + { + .battleScript = BattleScript_EffectPhotonGeyser, + .battleTvScore = 0, // TODO: Assign points + }, }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index ed7b80ce95..0899e55a86 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -19563,12 +19563,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Damages all opponents if user is\n" "Stellar form Terapagos."), - .effect = EFFECT_PLACEHOLDER, //EFFECT_TERA_STARSTORM + .effect = EFFECT_TERA_STARSTORM, .power = 120, - .type = TYPE_NORMAL, // Stellar type if used by Terapagos-Stellar + .type = TYPE_NORMAL, .accuracy = 100, .pp = 5, - .target = MOVE_TARGET_SELECTED, // MOVE_TARGET_BOTH if used by Terapagos-Stellar + .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .assistBanned = TRUE, diff --git a/src/data/pokemon/form_species_tables.h b/src/data/pokemon/form_species_tables.h index 9ae2c3ebdb..af35503fe3 100644 --- a/src/data/pokemon/form_species_tables.h +++ b/src/data/pokemon/form_species_tables.h @@ -2169,7 +2169,7 @@ static const u16 sOgerponFormSpeciesIdTable[] = { static const u16 sTerapagosFormSpeciesIdTable[] = { SPECIES_TERAPAGOS_NORMAL, SPECIES_TERAPAGOS_TERASTAL, -#if P_TERA_FORMS +#if P_TERA_FORMS SPECIES_TERAPAGOS_STELLAR, #endif FORM_SPECIES_END, diff --git a/test/battle/move_effect/curse.c b/test/battle/move_effect/curse.c new file mode 100644 index 0000000000..5fe17d3561 --- /dev/null +++ b/test/battle/move_effect/curse.c @@ -0,0 +1,36 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_CURSE].effect == EFFECT_CURSE); +} + +SINGLE_BATTLE_TEST("Curse lowers Speed, raises Attack, and raises Defense when used by non-Ghost-types") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CURSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player); + MESSAGE("Wobbuffet's Speed fell!"); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Defense rose!"); + } +} + +SINGLE_BATTLE_TEST("Curse cuts the user's HP in half when used by Ghost-types") +{ + GIVEN { + PLAYER(SPECIES_MISDREAVUS); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CURSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player); + HP_BAR(player, hp: maxHP / 2); + } +} From fc9d5c289a8a042d3ec34672ad41f8903ea661f0 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 27 Apr 2024 12:35:42 -0500 Subject: [PATCH 38/71] Embody Aspect fixes (#4439) * Embody Aspect fixes * Fix tests * Update battle_util.c --- include/constants/abilities.h | 8 ++-- src/battle_util.c | 20 +++++----- src/data/abilities.h | 8 ++-- .../pokemon/species_info/gen_9_families.h | 8 ++-- test/battle/ability/embody_aspect.c | 38 +++++++++---------- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/include/constants/abilities.h b/include/constants/abilities.h index 3cced68bb1..8568206137 100644 --- a/include/constants/abilities.h +++ b/include/constants/abilities.h @@ -326,10 +326,10 @@ #define ABILITY_MYCELIUM_MIGHT 298 #define ABILITY_HOSPITALITY 299 #define ABILITY_MINDS_EYE 300 -#define ABILITY_EMBODY_ASPECT_TEAL 301 -#define ABILITY_EMBODY_ASPECT_HEARTHFLAME 302 -#define ABILITY_EMBODY_ASPECT_WELLSPRING 303 -#define ABILITY_EMBODY_ASPECT_CORNERSTONE 304 +#define ABILITY_EMBODY_ASPECT_TEAL_MASK 301 +#define ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK 302 +#define ABILITY_EMBODY_ASPECT_WELLSPRING_MASK 303 +#define ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK 304 #define ABILITY_TOXIC_CHAIN 305 #define ABILITY_SUPERSWEET_SYRUP 306 #define ABILITY_TERA_SHIFT 307 diff --git a/src/battle_util.c b/src/battle_util.c index 498c8c0abb..e4885c665b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4737,22 +4737,22 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - case ABILITY_EMBODY_ASPECT_TEAL: - case ABILITY_EMBODY_ASPECT_HEARTHFLAME: - case ABILITY_EMBODY_ASPECT_WELLSPRING: - case ABILITY_EMBODY_ASPECT_CORNERSTONE: + case ABILITY_EMBODY_ASPECT_TEAL_MASK: + case ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK: + case ABILITY_EMBODY_ASPECT_WELLSPRING_MASK: + case ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK: if (!gSpecialStatuses[battler].switchInAbilityDone) { - u32 stat = STAT_SPATK; + u32 stat; - if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_TEAL) - stat = STAT_SPATK; - else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_HEARTHFLAME) + if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK) stat = STAT_ATK; - else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_WELLSPRING) + else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_WELLSPRING_MASK) stat = STAT_SPDEF; - else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_CORNERSTONE) + else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK) stat = STAT_DEF; + else //ABILITY_EMBODY_ASPECT_TEAL_MASK + stat = STAT_SPEED; if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL)) break; diff --git a/src/data/abilities.h b/src/data/abilities.h index 8c26934d9a..6905d67617 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -2472,7 +2472,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .breakable = TRUE, }, - [ABILITY_EMBODY_ASPECT_TEAL] = + [ABILITY_EMBODY_ASPECT_TEAL_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), @@ -2487,7 +2487,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .failsOnImposter = TRUE, }, - [ABILITY_EMBODY_ASPECT_HEARTHFLAME] = + [ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), @@ -2502,7 +2502,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .failsOnImposter = TRUE, }, - [ABILITY_EMBODY_ASPECT_WELLSPRING] = + [ABILITY_EMBODY_ASPECT_WELLSPRING_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), @@ -2517,7 +2517,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .failsOnImposter = TRUE, }, - [ABILITY_EMBODY_ASPECT_CORNERSTONE] = + [ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index d858ec352c..98ae37e90b 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -6314,10 +6314,10 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = [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), #if P_TERA_FORMS - [SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL, BODY_COLOR_GREEN, 1), - [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING, BODY_COLOR_BLUE, 0), - [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME, BODY_COLOR_RED, 0), - [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE, BODY_COLOR_GRAY, 0), + [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), #endif //P_TERA_FORMS #endif //P_FAMILY_OGERPON diff --git a/test/battle/ability/embody_aspect.c b/test/battle/ability/embody_aspect.c index 8b37dd1a74..728ba8cb94 100644 --- a/test/battle/ability/embody_aspect.c +++ b/test/battle/ability/embody_aspect.c @@ -2,14 +2,14 @@ #include "test/battle.h" -SINGLE_BATTLE_TEST("Embodoy Aspect raises a stat depending on the users form by one stage") +SINGLE_BATTLE_TEST("Embody Aspect raises a stat depending on the users form by one stage") { u16 species, ability; - PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_TEAL; } - PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_HEARTHFLAME; } - PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_WELLSPRING; } - PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_CORNERSTONE; } + PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_TEAL_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK; } GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -19,32 +19,32 @@ SINGLE_BATTLE_TEST("Embodoy Aspect raises a stat depending on the users form by } SCENE { ABILITY_POPUP(opponent, ability); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - if (ability == ABILITY_EMBODY_ASPECT_TEAL) - MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Atk!"); - else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME) + if (ability == ABILITY_EMBODY_ASPECT_TEAL_MASK) + MESSAGE("Foe Ogerpon's Embody Aspect raised its Speed!"); + else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK) MESSAGE("Foe Ogerpon's Embody Aspect raised its Attack!"); - else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING) + else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING_MASK) MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Def!"); - else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE) + else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK) MESSAGE("Foe Ogerpon's Embody Aspect raised its Defense!"); } THEN { - if (ability == ABILITY_EMBODY_ASPECT_TEAL) - EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); - else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME) + if (ability == ABILITY_EMBODY_ASPECT_TEAL_MASK) + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK) EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); - else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING) + else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING_MASK) EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); - else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE) + else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK) EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); } } -SINGLE_BATTLE_TEST("Embodoy Aspect activates when it's no longer effected by Neutralizing Gas") +SINGLE_BATTLE_TEST("Embody Aspect activates when it's no longer effected by Neutralizing Gas") { GIVEN { PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_OGERPON_TEAL_MASK_TERA) { Ability(ABILITY_EMBODY_ASPECT_TEAL); } + OPPONENT(SPECIES_OGERPON_TEAL_MASK_TERA) { Ability(ABILITY_EMBODY_ASPECT_TEAL_MASK); } } WHEN { TURN { SWITCH(player, 1); } } SCENE { @@ -52,8 +52,8 @@ SINGLE_BATTLE_TEST("Embodoy Aspect activates when it's no longer effected by Neu MESSAGE("Neutralizing Gas filled the area!"); MESSAGE("Weezing, that's enough! Come back!"); MESSAGE("The effects of Neutralizing Gas wore off!"); - ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL); + ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL_MASK); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Atk!"); + MESSAGE("Foe Ogerpon's Embody Aspect raised its Speed!"); } } From d58e6517f85eb5717649acfc2739890571b522c6 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 27 Apr 2024 12:52:31 -0500 Subject: [PATCH 39/71] Add abilities Tera Shift, Tera Shell, and Teraform Zero (#4418) * Add abilities Tera Shift, Tera Shell, and Teraform Zero * Address reviews * Tests and misc cleanup * Fix alignments * Update battle_script_commands.c * New tests and fix behavior * Address more reviews * Update battle_util.c --- asm/macros/battle_script.inc | 4 ++ data/battle_scripts_1.s | 49 +++++++++++++ include/battle.h | 1 + include/battle_script_commands.h | 1 + include/battle_scripts.h | 5 ++ include/constants/battle.h | 2 +- include/constants/battle_string_ids.h | 13 +++- include/constants/form_change_types.h | 2 +- src/battle_message.c | 13 ++++ src/battle_script_commands.c | 55 ++++++++++++++- src/battle_util.c | 30 +++++++- src/data/pokemon/form_change_tables.h | 2 +- test/battle/ability/gastro_acid.c | 2 +- test/battle/ability/tera_shell.c | 99 +++++++++++++++++++++++++++ test/battle/ability/tera_shift.c | 35 ++++++++++ test/battle/ability/zero_to_hero.c | 2 +- 16 files changed, 307 insertions(+), 8 deletions(-) create mode 100644 test/battle/ability/tera_shell.c create mode 100644 test/battle/ability/tera_shift.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 4edc76841c..34f29950bd 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1638,6 +1638,10 @@ .4byte \failInstr .endm + .macro removeweather + callnative BS_RemoveWeather + .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 c5023db393..24ce3405eb 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7078,6 +7078,29 @@ BattleScript_AttackerFormChangeEnd3NoPopup:: call BattleScript_AttackerFormChangeNoPopup end3 +BattleScript_AttackerFormChangeWithString:: + pause 5 + copybyte gBattlerAbility, gBattlerAttacker + call BattleScript_AbilityPopUp + flushtextbox +BattleScript_AttackerFormChangeWithStringNoPopup:: + handleformchange BS_ATTACKER, 0 + handleformchange BS_ATTACKER, 1 + playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE + waitanimation + handleformchange BS_ATTACKER, 2 + printstring STRINGID_PKMNTRANSFORMED + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_AttackerFormChangeWithStringEnd3:: + call BattleScript_AttackerFormChangeWithString + end3 + +BattleScript_AttackerFormChangeWithStringEnd3NoPopup:: + call BattleScript_AttackerFormChangeWithStringNoPopup + end3 + BattleScript_AttackerFormChangeMoveEffect:: waitmessage 1 handleformchange BS_ATTACKER, 0 @@ -8356,6 +8379,13 @@ BattleScript_ProteanActivates:: waitmessage B_WAIT_TIME_LONG return +BattleScript_TeraShellDistortingTypeMatchups:: + pause B_WAIT_TIME_SHORTEST + call BattleScript_AbilityPopUp + printstring STRINGID_PKMNMADESHELLGLEAM + waitmessage B_WAIT_TIME_LONG + return + BattleScript_CursedBodyActivates:: call BattleScript_AbilityPopUp printstring STRINGID_CUSEDBODYDISABLED @@ -9261,6 +9291,25 @@ BattleScript_AnnounceAirLockCloudNine:: call BattleScript_ActivateWeatherAbilities end3 +BattleScript_ActivateTeraformZero:: + call BattleScript_AbilityPopUp + waitmessage B_WAIT_TIME_LONG + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_ANY, BattleScript_ActivateTeraformZero_RemoveWeather + jumpifhalfword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZero_RemoveTerrain + goto BattleScript_ActivateTeraformZero_End +BattleScript_ActivateTeraformZero_RemoveWeather: + removeweather + printfromtable gWeatherEndsStringIds + waitmessage B_WAIT_TIME_LONG + jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZero_End +BattleScript_ActivateTeraformZero_RemoveTerrain: + removeterrain + playanimation BS_ATTACKER, B_ANIM_RESTORE_BG + printfromtable gTerrainStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_ActivateTeraformZero_End: + end3 + BattleScript_QuickClawActivation:: flushtextbox playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT diff --git a/include/battle.h b/include/battle.h index c5d08bb584..cdcb1b1742 100644 --- a/include/battle.h +++ b/include/battle.h @@ -816,6 +816,7 @@ struct BattleStruct u8 quickClawRandom[MAX_BATTLERS_COUNT]; u8 quickDrawRandom[MAX_BATTLERS_COUNT]; u8 boosterEnergyActivates; + u8 distortedTypeMatchups; }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 540390bb53..fcf69e53dc 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -52,6 +52,7 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem); u8 GetCatchingBattler(void); u32 GetHighestStatId(u32 battlerId); bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType); +bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef); bool32 IsMoveNotAllowedInSkyBattles(u32 move); bool32 DoSwitchInAbilities(u32 battlerId); u8 GetFirstFaintedPartyIndex(u8 battlerId); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index e4e9ca4307..7ab27aa117 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -364,6 +364,8 @@ extern const u8 BattleScript_DancerActivates[]; extern const u8 BattleScript_AftermathDmg[]; extern const u8 BattleScript_AttackerFormChange[]; extern const u8 BattleScript_AttackerFormChangeEnd3[]; +extern const u8 BattleScript_AttackerFormChangeWithString[]; +extern const u8 BattleScript_AttackerFormChangeWithStringEnd3[]; extern const u8 BattleScript_TargetFormChange[]; extern const u8 BattleScript_AnticipationActivates[]; extern const u8 BattleScript_SlowStartEnds[]; @@ -382,6 +384,7 @@ extern const u8 BattleScript_CheekPouchActivates[]; extern const u8 BattleScript_TotemVar[]; extern const u8 BattleScript_TotemFlaredToLife[]; extern const u8 BattleScript_AnnounceAirLockCloudNine[]; +extern const u8 BattleScript_ActivateTeraformZero[]; extern const u8 BattleScript_BattlerAbilityStatRaiseOnSwitchIn[]; extern const u8 BattleScript_CottonDownActivates[]; extern const u8 BattleScript_BallFetch[]; @@ -436,6 +439,7 @@ extern const u8 BattleScript_PastelVeilActivates[]; extern const u8 BattleScript_MimicryActivatesEnd3[]; extern const u8 BattleScript_ApplyMimicry[]; extern const u8 BattleScript_AttackerFormChangeEnd3NoPopup[]; +extern const u8 BattleScript_AttackerFormChangeWithStringEnd3NoPopup[]; extern const u8 BattleScript_AttackerFormChangeMoveEffect[]; extern const u8 BattleScript_BothCanNoLongerEscape[]; extern const u8 BattleScript_OctolockEndTurn[]; @@ -502,6 +506,7 @@ extern const u8 BattleScript_AromaVeilProtectsRet[]; extern const u8 BattleScript_LowerAtkSpAtk[]; extern const u8 BattleScript_Terastallization[]; extern const u8 BattleScript_BoosterEnergyEnd2[]; +extern const u8 BattleScript_TeraShellDistortingTypeMatchups[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index 30f304144f..4ca7eabdca 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -299,7 +299,7 @@ #define B_WEATHER_HAIL_PERMANENT (1 << 10) #define B_WEATHER_HAIL (B_WEATHER_HAIL_TEMPORARY | B_WEATHER_HAIL_PERMANENT) #define B_WEATHER_STRONG_WINDS (1 << 11) -#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS | B_WEATHER_SNOW) +#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS | B_WEATHER_SNOW | B_WEATHER_FOG) #define B_WEATHER_PRIMAL_ANY (B_WEATHER_RAIN_PRIMAL | B_WEATHER_SUN_PRIMAL | B_WEATHER_STRONG_WINDS) #define B_WEATHER_SNOW_TEMPORARY (1 << 12) #define B_WEATHER_SNOW_PERMANENT (1 << 13) diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index dfb5c5e2d4..4167522f18 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -712,8 +712,9 @@ #define STRINGID_FOGCREPTUP 710 #define STRINGID_FOGISDEEP 711 #define STRINGID_FOGLIFTED 712 +#define STRINGID_PKMNMADESHELLGLEAM 713 -#define BATTLESTRINGS_COUNT 713 +#define BATTLESTRINGS_COUNT 714 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -767,6 +768,16 @@ #define B_MSG_STARTED_SNOW 6 #define B_MSG_STARTED_FOG 7 +// gWeatherEndsStringIds +#define B_MSG_WEATHER_END_RAIN 0 +#define B_MSG_WEATHER_END_SANDSTORM 1 +#define B_MSG_WEATHER_END_SUN 2 +#define B_MSG_WEATHER_END_HAIL 3 +#define B_MSG_WEATHER_END_STRONG_WINDS 4 +#define B_MSG_WEATHER_END_SNOW 5 +#define B_MSG_WEATHER_END_FOG 6 +#define B_MSG_WEATHER_END_COUNT 7 + // gRainContinuesStringIds #define B_MSG_RAIN_CONTINUES 0 #define B_MSG_DOWNPOUR_CONTINUES 1 diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 51325baf25..d23dc0d2bd 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -61,7 +61,7 @@ #define FORM_CHANGE_END_BATTLE_TERRAIN 8 // Form change that activates when the Pokémon is switched out in battle. -// - No parameters. +// param1: ability to check, optional #define FORM_CHANGE_BATTLE_SWITCH 9 // Form change that activates when the Pokémon's HP % passes a certain threshold. diff --git a/src/battle_message.c b/src/battle_message.c index b713d846ad..d630520953 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -724,6 +724,7 @@ static const u8 sText_ASandstormKickedUp[] = _("A sandstorm kicked up!"); static const u8 sText_PkmnsWillPerishIn3Turns[] = _("Both Pokémon will perish\nin three turns!"); static const u8 sText_AbilityRaisedStatDrastically[] = _("{B_DEF_ABILITY} raised {B_DEF_NAME_WITH_PREFIX}'s\n{B_BUFF1} drastically!"); static const u8 sText_AsOneEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} has two Abilities!"); +static const u8 sText_PkmnMadeShellGleam[] = _("{B_DEF_NAME_WITH_PREFIX} made its shell gleam!\nIt's distorting type matchups!"); static const u8 sText_CuriousMedicineEnters[] = _("{B_EFF_NAME_WITH_PREFIX}'s\nstat changes were reset!"); static const u8 sText_CanActFaster[] = _("{B_ATK_NAME_WITH_PREFIX} can act faster,\nthanks to {B_BUFF1}!"); static const u8 sText_MicleBerryActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} boosted the accuracy of its\nnext move using {B_LAST_ITEM}!"); @@ -964,6 +965,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_CANACTFASTERTHANKSTO - BATTLESTRINGS_TABLE_START] = sText_CanActFaster, [STRINGID_CURIOUSMEDICINEENTERS - BATTLESTRINGS_TABLE_START] = sText_CuriousMedicineEnters, [STRINGID_ASONEENTERS - BATTLESTRINGS_TABLE_START] = sText_AsOneEnters, + [STRINGID_PKMNMADESHELLGLEAM - BATTLESTRINGS_TABLE_START] = sText_PkmnMadeShellGleam, [STRINGID_ABILITYRAISEDSTATDRASTICALLY - BATTLESTRINGS_TABLE_START] = sText_AbilityRaisedStatDrastically, [STRINGID_PKMNSWILLPERISHIN3TURNS - BATTLESTRINGS_TABLE_START] = sText_PkmnsWillPerishIn3Turns, [STRINGID_ASANDSTORMKICKEDUP - BATTLESTRINGS_TABLE_START] = sText_ASandstormKickedUp, @@ -1684,6 +1686,17 @@ const u16 gMoveWeatherChangeStringIds[] = [B_MSG_STARTED_FOG] = STRINGID_FOGCREPTUP, // Unused, can use for custom moves that set fog }; +const u16 gWeatherEndsStringIds[B_MSG_WEATHER_END_COUNT] = +{ + [B_MSG_WEATHER_END_RAIN] = STRINGID_RAINSTOPPED, + [B_MSG_WEATHER_END_SANDSTORM] = STRINGID_SANDSTORMSUBSIDED, + [B_MSG_WEATHER_END_SUN] = STRINGID_SUNLIGHTFADED, + [B_MSG_WEATHER_END_HAIL] = STRINGID_HAILSTOPPED, + [B_MSG_WEATHER_END_STRONG_WINDS] = STRINGID_STRONGWINDSDISSIPATED, + [B_MSG_WEATHER_END_SNOW] = STRINGID_SNOWSTOPPED, + [B_MSG_WEATHER_END_FOG] = STRINGID_FOGLIFTED, +}; + const u16 gSandStormHailSnowContinuesStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMRAGES, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bfbb2b3ca0..405deafaf3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -330,6 +330,7 @@ static bool8 IsFinalStrikeEffect(u16 move); static void TryUpdateRoundTurnOrder(void); static bool32 ChangeOrderTargetAfterAttacker(void); void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler); +static void RemoveAllWeather(void); static void RemoveAllTerrains(void); static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate); static bool8 CanBurnHitThaw(u16 move); @@ -1233,6 +1234,19 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType) return FALSE; } +bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef) +{ + if (!(gBattleStruct->distortedTypeMatchups & gBitTable[battlerDef]) + && GetBattlerAbility(battlerDef) == ABILITY_TERA_SHELL + && gBattleMons[battlerDef].species == SPECIES_TERAPAGOS_TERASTAL + && !IS_MOVE_STATUS(move) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP) + return TRUE; + + return FALSE; +} + bool32 IsMoveNotAllowedInSkyBattles(u32 move) { return ((gBattleStruct->isSkyBattle) && (gMovesInfo[gCurrentMove].skyBattleBanned)); @@ -1859,6 +1873,14 @@ static void Cmd_ppreduce(void) gHitMarker &= ~HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = cmd->nextInstr; + + if (ShouldTeraShellDistortTypeMatchups(gCurrentMove, gBattlerTarget)) + { + gBattleStruct->distortedTypeMatchups |= gBitTable[gBattlerTarget]; + gBattlerAbility = gBattlerTarget; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_TeraShellDistortingTypeMatchups; + } } // The chance is 1/N for each stage. @@ -6326,6 +6348,7 @@ static void Cmd_moveend(void) gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; gBattleStruct->poisonPuppeteerConfusion = FALSE; + gBattleStruct->distortedTypeMatchups = 0; gBattleScripting.moveendState++; break; case MOVEEND_COUNT: @@ -8397,6 +8420,30 @@ bool32 CanUseLastResort(u8 battler) return (knownMovesCount >= 2 && usedMovesCount >= knownMovesCount - 1); } +static void RemoveAllWeather(void) +{ + gWishFutureKnock.weatherDuration = 0; + + if (gBattleWeather & B_WEATHER_RAIN) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_RAIN; + else if(gBattleWeather & B_WEATHER_SANDSTORM) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SANDSTORM; + else if(gBattleWeather & B_WEATHER_SUN) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SUN; + else if(gBattleWeather & B_WEATHER_HAIL) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_HAIL; + else if(gBattleWeather & B_WEATHER_STRONG_WINDS) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_STRONG_WINDS; + else if(gBattleWeather & B_WEATHER_SNOW) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SNOW; + else if(gBattleWeather & B_WEATHER_FOG) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_FOG; + else + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_COUNT; // failsafe + + gBattleWeather = 0; // remove the weather +} + static void RemoveAllTerrains(void) { gFieldTimers.terrainTimer = 0; @@ -16825,7 +16872,6 @@ void BS_TryGulpMissile(void) gBattlescriptCurrInstr = cmd->nextInstr; } - void BS_TryQuash(void) { NATIVE_ARGS(const u8 *failInstr); @@ -16857,3 +16903,10 @@ void BS_TryQuash(void) } gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_RemoveWeather(void) +{ + NATIVE_ARGS(); + RemoveAllWeather(); + gBattlescriptCurrInstr = cmd->nextInstr; +} diff --git a/src/battle_util.c b/src/battle_util.c index 6d6ce38a7c..6a3f43f9d7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4619,6 +4619,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_TERAFORM_ZERO: + if (!gSpecialStatuses[battler].switchInAbilityDone + && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) + { + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + BattleScriptPushCursorAndCallback(BattleScript_ActivateTeraformZero); + effect++; + } case ABILITY_SCHOOLING: if (gBattleMons[battler].level < 20) break; @@ -4805,6 +4813,18 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_TERA_SHIFT: + if (!gSpecialStatuses[battler].switchInAbilityDone + && gBattleMons[battler].species == SPECIES_TERAPAGOS_NORMAL + && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH)) + { + gBattlerAttacker = battler; + gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT; + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeWithStringEnd3); + effect++; + } + break; } break; case ABILITYEFFECT_ENDTURN: // 1 @@ -10110,6 +10130,13 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(1.0); } + if (gBattleStruct->distortedTypeMatchups & gBitTable[battlerDef]) + { + mod = UQ_4_12(0.5); + if (recordAbilities) + RecordAbilityBattle(battlerDef, GetBattlerAbility(battlerDef)); + } + *modifier = uq4_12_multiply(*modifier, mod); } @@ -10537,7 +10564,8 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) targetSpecies = formChanges[i].targetSpecies; break; case FORM_CHANGE_BATTLE_SWITCH: - targetSpecies = formChanges[i].targetSpecies; + if (formChanges[i].param1 == GetBattlerAbility(battler) || formChanges[i].param1 == ABILITY_NONE) + targetSpecies = formChanges[i].targetSpecies; break; case FORM_CHANGE_BATTLE_HP_PERCENT: if (formChanges[i].param1 == GetBattlerAbility(battler)) diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 5edd6f42b5..f8edc496de 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -1268,7 +1268,7 @@ static const struct FormChange sOgerponFormChangeTable[] = { #if P_FAMILY_TERAPAGOS static const struct FormChange sTerapagosFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_TERAPAGOS_TERASTAL}, //needs to be tied to the ability + {FORM_CHANGE_BATTLE_SWITCH, SPECIES_TERAPAGOS_TERASTAL, ABILITY_TERA_SHIFT}, #if P_TERA_FORMS //{FORM_CHANGE_TERASTALLIZATION, SPECIES_TERAPAGOS_STELLAR}, #endif diff --git a/test/battle/ability/gastro_acid.c b/test/battle/ability/gastro_acid.c index a9fba14f89..2f5b04dab9 100644 --- a/test/battle/ability/gastro_acid.c +++ b/test/battle/ability/gastro_acid.c @@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Gastro Acid fails if target has a banned ability") PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } - // Needs confirmation since those abilities can be surpressed by Neutralizing Gas + // Needs confirmation since those abilities can be suppressed by Neutralizing Gas // PARAMETRIZE { species = SPECIES_IRON_MOTH; ability = ABILITY_QUARK_DRIVE; } // PARAMETRIZE { species = SPECIES_WALKING_WAKE; ability = ABILITY_PROTOSYNTHESIS; } PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; } diff --git a/test/battle/ability/tera_shell.c b/test/battle/ability/tera_shell.c new file mode 100644 index 0000000000..138a56f9b4 --- /dev/null +++ b/test/battle/ability/tera_shell.c @@ -0,0 +1,99 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Tera Shell makes all moves against Terapagos not very effective when at full HP") +{ + u16 hp; + PARAMETRIZE { hp = 100; } + PARAMETRIZE { hp = 99; } + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL) { Ability(ABILITY_TERA_SHELL); HP(hp); MaxHP(100);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + if (hp == 100) { + MESSAGE("Foe Wobbuffet used Tackle!"); + ABILITY_POPUP(player, ABILITY_TERA_SHELL); + MESSAGE("Terapagos made its shell gleam! It's distorting type matchups!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player); + MESSAGE("It's not very effective…"); + } + else { + NONE_OF { + ABILITY_POPUP(player, ABILITY_TERA_SHELL); + MESSAGE("Terapagos made its shell gleam! It's distorting type matchups!"); + MESSAGE("It's not very effective…"); + } + } + } +} + +SINGLE_BATTLE_TEST("Tera Shell makes all hits of multi-hit moves against Terapagos not very effective") +{ + s16 firstHit; + s16 secondHit; + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL) { Ability(ABILITY_TERA_SHELL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_DOUBLE_HIT); } + } SCENE { + MESSAGE("Foe Wobbuffet used Double Hit!"); + ABILITY_POPUP(player, ABILITY_TERA_SHELL); + MESSAGE("Terapagos made its shell gleam! It's distorting type matchups!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_HIT, opponent); + HP_BAR(player, captureDamage: &firstHit); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_HIT, opponent); + HP_BAR(player, captureDamage: &secondHit); + MESSAGE("It's not very effective…"); + } THEN { + EXPECT_EQ(firstHit, secondHit); + } +} + +DOUBLE_BATTLE_TEST("Tera Shell only makes the first hit of a double battle turn not very effective") +{ + s16 firstHit; + s16 secondHit; + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL) { Ability(ABILITY_TERA_SHELL); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_TERA_SHELL); + MESSAGE("Terapagos made its shell gleam! It's distorting type matchups!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft); + HP_BAR(playerLeft, captureDamage: &firstHit); + MESSAGE("It's not very effective…"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + HP_BAR(playerLeft, captureDamage: &secondHit); + NOT MESSAGE("It's not very effective…"); + } THEN { + EXPECT_MUL_EQ(firstHit, Q_4_12(2.0), secondHit); + } +} + +DOUBLE_BATTLE_TEST("Tera Shell only makes the first hit against Terapagos from a multi-target move not very effective") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL) { Ability(ABILITY_TERA_SHELL); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_BLIZZARD); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_TERA_SHELL); + MESSAGE("Terapagos made its shell gleam! It's distorting type matchups!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BLIZZARD, opponentLeft); + HP_BAR(playerLeft); + MESSAGE("It's not very effective…"); + HP_BAR(playerRight); + NOT MESSAGE("It's not very effective…"); + } +} diff --git a/test/battle/ability/tera_shift.c b/test/battle/ability/tera_shift.c new file mode 100644 index 0000000000..9149160cd1 --- /dev/null +++ b/test/battle/ability/tera_shift.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Tera Shift transforms Terapagos into its Terastal form on switch in") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_NORMAL) { Ability(ABILITY_TERA_SHIFT); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { ; } + } SCENE { + ABILITY_POPUP(player, ABILITY_TERA_SHIFT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + MESSAGE("Terapagos transformed!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_TERAPAGOS_TERASTAL); + } +} + +SINGLE_BATTLE_TEST("Tera Shift can't be suppressed by Neutralizing Gas") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_NORMAL) { Ability(ABILITY_TERA_SHIFT); } + OPPONENT(SPECIES_KOFFING) { Ability(ABILITY_NEUTRALIZING_GAS); } + } WHEN { + TURN { ; } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); + ABILITY_POPUP(player, ABILITY_TERA_SHIFT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + MESSAGE("Terapagos transformed!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_TERAPAGOS_TERASTAL); + } +} diff --git a/test/battle/ability/zero_to_hero.c b/test/battle/ability/zero_to_hero.c index 4a186661a6..4f8cfc8a65 100644 --- a/test/battle/ability/zero_to_hero.c +++ b/test/battle/ability/zero_to_hero.c @@ -20,7 +20,7 @@ SINGLE_BATTLE_TEST("Zero to Hero transforms Palafin when it switches out") } THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); } } -SINGLE_BATTLE_TEST("Zero to Hero can't be surpressed by Neutralizing Gas") +SINGLE_BATTLE_TEST("Zero to Hero can't be suppressed by Neutralizing Gas") { GIVEN { PLAYER(SPECIES_PALAFIN_ZERO) { Ability(ABILITY_ZERO_TO_HERO); } From 3ac67acc63742147b74ec1b14a6ef4221323150d Mon Sep 17 00:00:00 2001 From: Cafei <46283144+cafei-uh@users.noreply.github.com> Date: Sun, 28 Apr 2024 19:05:23 +0400 Subject: [PATCH 40/71] Fix Tatsugiri Droopy back sprite (#4455) --- graphics/pokemon/tatsugiri/droopy/back.png | Bin 400 -> 386 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/graphics/pokemon/tatsugiri/droopy/back.png b/graphics/pokemon/tatsugiri/droopy/back.png index 6d998537f2036c86d1014d091d32d4a1d7167089..381ae4278ee9426eb96a914a3680c0c83a528afa 100644 GIT binary patch delta 313 zcmV-90mlB21A+sPBnkm@Qb$4nuFf3kks&gF0Srk*K~z|U?Uca|gdhw=x7Z5x|37$J zKqbOb)a;Epn#^-L6h5D+si~>IYZblmQ()#72ns<7{FtDCNPt84{R%*IyAbg%An<8- zZdn|{C?f+(e~vLU39Uc;I?wtXH2vDjYK+A&{ehJVQm<9Cx zWcd35z_hmkaM*+Sz}0?rlNKA-!&`(lzyU#APyYab6nBW!?M*6(Clhhj4Nv%kBbbT`vTClcuJorXK4Hy-E)G;FN7e00000 LNkvXXu0mjff0Ka; delta 329 zcmZo-p1?doWul>eJp)fkkYDhBBw&A6(V2mPk>AtBF(jh(?UaLjM-(_*#2J%+{g>af zV4LYx(OYTx=aza-uH@^evGWFk`f!oGAB&xih;#UPa{idHpuwlX`TJik1+Lv4FZ2{1 zX7V!{3RFmnUig*G6Rs*#|6{8H`@&o7&y}k=OzZ2qY8g^qc8Lpgu4`f1z|p&3K#DJK z$4o&k1+fF`jGGv4uqsFiaG0}wVp#Xjm}yqSitQC&)0^rT)&wR!a%>3u%uvL1RJ@MU zKwP4Z*TDAPhx5!0nexmJHmDaL{90Ff`b~$ezC^bH8%x3p?RRz!7Uu<~^JX);$zG^$ zUBK9*$79cMY1bzK23gZD3^)639rBtW^-TOGW6|quK}LnUtOa#5bq~&4vJ?o8|6+O` W$miRd^I#PN5O})!xvX Date: Sun, 28 Apr 2024 18:06:39 +0300 Subject: [PATCH 41/71] Fix lingering long ability popup names (#4456) --- src/battle_interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index 59f89ae30c..4e32fcf5a6 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3114,6 +3114,7 @@ static void PrintBattlerOnAbilityPopUp(u8 battlerId, u8 spriteId1, u8 spriteId2) static void PrintAbilityOnAbilityPopUp(u32 ability, u8 spriteId1, u8 spriteId2) { + ClearAbilityName(spriteId1, spriteId2); PrintOnAbilityPopUp(gAbilitiesInfo[ability].name, (void*)(OBJ_VRAM0) + (gSprites[spriteId1].oam.tileNum * 32) + 256, (void*)(OBJ_VRAM0) + (gSprites[spriteId2].oam.tileNum * 32) + 256, @@ -3320,7 +3321,6 @@ void UpdateAbilityPopup(u8 battlerId) u8 spriteId2 = gBattleStruct->abilityPopUpSpriteIds[battlerId][1]; u16 ability = (gBattleScripting.abilityPopupOverwrite != 0) ? gBattleScripting.abilityPopupOverwrite : gBattleMons[battlerId].ability; - ClearAbilityName(spriteId1, spriteId2); PrintAbilityOnAbilityPopUp(ability, spriteId1, spriteId2); RestoreOverwrittenPixels((void*)(OBJ_VRAM0) + (gSprites[spriteId1].oam.tileNum * 32)); } From c7797dff814f2d929af2a8859ab14129960ec02b Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 28 Apr 2024 22:48:34 +0200 Subject: [PATCH 42/71] Make Fur Coat breakable (#4459) --- src/data/abilities.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/abilities.h b/src/data/abilities.h index 6905d67617..a65d4e8c24 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -1276,6 +1276,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .name = _("Fur Coat"), .description = COMPOUND_STRING("Raises Defense."), .aiRating = 7, + .breakable = TRUE, }, [ABILITY_MAGICIAN] = From c1f92b088eb80d0147b0663dab269523826658c4 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:21:33 +0200 Subject: [PATCH 43/71] Eject Pack fix (#4463) --- src/battle_script_commands.c | 4 +--- test/battle/hold_effect/eject_pack.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f65ed39037..94a85dfad0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6040,8 +6040,6 @@ static void Cmd_moveend(void) for (i = 0; i < gBattlersCount; i++) { u32 holdEffect; - if (i == gBattlerAttacker) - continue; holdEffect = GetBattlerHoldEffect(i, TRUE); if (holdEffect == HOLD_EFFECT_EJECT_BUTTON) ejectButtonBattlers |= gBitTable[i]; @@ -6057,7 +6055,7 @@ static void Cmd_moveend(void) { u32 battler = battlers[i]; - if (ejectButtonBattlers & gBitTable[battler]) + if (battler != gBattlerAttacker && ejectButtonBattlers & gBitTable[battler]) { if (TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) // Apparently Sheer Force blocks Eject Button, but not Eject Pack continue; diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index 1a54d5a2e9..6757b11db9 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -22,6 +22,7 @@ SINGLE_BATTLE_TEST("Eject Pack does not cause the new pokemon to lose hp due to MESSAGE("Wobbuffet is switched out with the Eject Pack!"); MESSAGE("Go! Wynaut!"); NOT MESSAGE("Wynaut was hurt by its Life Orb!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); } } @@ -41,3 +42,24 @@ SINGLE_BATTLE_TEST("Eject Pack does not activate if there are no pokemon left to } } } + +SINGLE_BATTLE_TEST("Eject Pack triggers on the correct pokemon") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_OVERHEAT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_OVERHEAT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet is switched out with the Eject Pack!"); + MESSAGE("Go! Wynaut!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} From 8d175ebe89ca0f914ba0525031b6b76392d18443 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:29:00 +0300 Subject: [PATCH 44/71] Automatically display TM/HM icons based on their move (#4452) * Automatically get icons and palettes for TM/HMs * Restore HM icons * replace sTMTypePalettes with a gTypesInfo field --- include/data.h | 1 + src/battle_main.c | 19 +++++++ src/data/item_icon_table.h | 109 ------------------------------------- src/item_icon.c | 8 +++ 4 files changed, 28 insertions(+), 109 deletions(-) diff --git a/include/data.h b/include/data.h index 78abd00b35..b18dbf220f 100644 --- a/include/data.h +++ b/include/data.h @@ -109,6 +109,7 @@ struct TypeInfo u8 palette; u16 zMove; u16 maxMove; + const u32 *const paletteTMHM; //u16 enhanceItem; //u16 berry; //u16 gem; diff --git a/src/battle_main.c b/src/battle_main.c index d7bbb764bf..9b1add03e4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -311,6 +311,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_BREAKNECK_BLITZ, .maxMove = MOVE_MAX_STRIKE, + .paletteTMHM = gItemIconPalette_NormalTMHM, //.enhanceItem = ITEM_SILK_SCARF, //.berry = ITEM_CHILAN_BERRY, //.gem = ITEM_NORMAL_GEM, @@ -325,6 +326,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_ALL_OUT_PUMMELING, .maxMove = MOVE_MAX_KNUCKLE, + .paletteTMHM = gItemIconPalette_FightingTMHM, //.enhanceItem = ITEM_BLACK_BELT, //.berry = ITEM_CHOPLE_BERRY, //.gem = ITEM_FIGHTING_GEM, @@ -341,6 +343,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_SUPERSONIC_SKYSTRIKE, .maxMove = MOVE_MAX_AIRSTREAM, + .paletteTMHM = gItemIconPalette_FlyingTMHM, //.enhanceItem = ITEM_SHARP_BEAK, //.berry = ITEM_COBA_BERRY, //.gem = ITEM_FLYING_GEM, @@ -357,6 +360,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_ACID_DOWNPOUR, .maxMove = MOVE_MAX_OOZE, + .paletteTMHM = gItemIconPalette_PoisonTMHM, //.enhanceItem = ITEM_POISON_BARB, //.berry = ITEM_KEBIA_BERRY, //.gem = ITEM_POISON_GEM, @@ -373,6 +377,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_TECTONIC_RAGE, .maxMove = MOVE_MAX_QUAKE, + .paletteTMHM = gItemIconPalette_GroundTMHM, //.enhanceItem = ITEM_SOFT_SAND, //.berry = ITEM_SHUCA_BERRY, //.gem = ITEM_GROUND_GEM, @@ -389,6 +394,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_CONTINENTAL_CRUSH, .maxMove = MOVE_MAX_ROCKFALL, + .paletteTMHM = gItemIconPalette_RockTMHM, //.enhanceItem = ITEM_HARD_STONE, //.berry = ITEM_CHARTI_BERRY, //.gem = ITEM_ROCK_GEM, @@ -405,6 +411,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 15, .zMove = MOVE_SAVAGE_SPIN_OUT, .maxMove = MOVE_MAX_FLUTTERBY, + .paletteTMHM = gItemIconPalette_BugTMHM, //.enhanceItem = ITEM_SILVER_POWDER, //.berry = ITEM_TANGA_BERRY, //.gem = ITEM_BUG_GEM, @@ -421,6 +428,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_NEVER_ENDING_NIGHTMARE, .maxMove = MOVE_MAX_PHANTASM, + .paletteTMHM = gItemIconPalette_GhostTMHM, //.enhanceItem = ITEM_SPELL_TAG, //.berry = ITEM_KASIB_BERRY, //.gem = ITEM_GHOST_GEM, @@ -437,6 +445,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_CORKSCREW_CRASH, .maxMove = MOVE_MAX_STEELSPIKE, + .paletteTMHM = gItemIconPalette_SteelTMHM, //.enhanceItem = ITEM_METAL_COAT, //.berry = ITEM_BABIRI_BERRY, //.gem = ITEM_STEEL_GEM, @@ -459,6 +468,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_INFERNO_OVERDRIVE, .maxMove = MOVE_MAX_FLARE, + .paletteTMHM = gItemIconPalette_FireTMHM, //.enhanceItem = ITEM_CHARCOAL, //.berry = ITEM_OCCA_BERRY, //.gem = ITEM_FIRE_GEM, @@ -475,6 +485,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_HYDRO_VORTEX, .maxMove = MOVE_MAX_GEYSER, + .paletteTMHM = gItemIconPalette_WaterTMHM, //.enhanceItem = ITEM_MYSTIC_WATER, //.berry = ITEM_PASSHO_BERRY, //.gem = ITEM_WATER_GEM, @@ -491,6 +502,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 15, .zMove = MOVE_BLOOM_DOOM, .maxMove = MOVE_MAX_OVERGROWTH, + .paletteTMHM = gItemIconPalette_GrassTMHM, //.enhanceItem = ITEM_MIRACLE_SEED, //.berry = ITEM_RINDO_BERRY, //.gem = ITEM_GRASS_GEM, @@ -507,6 +519,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_GIGAVOLT_HAVOC, .maxMove = MOVE_MAX_LIGHTNING, + .paletteTMHM = gItemIconPalette_ElectricTMHM, //.enhanceItem = ITEM_MAGNET, //.berry = ITEM_WACAN_BERRY, //.gem = ITEM_ELECTRIC_GEM, @@ -523,6 +536,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_SHATTERED_PSYCHE, .maxMove = MOVE_MAX_MINDSTORM, + .paletteTMHM = gItemIconPalette_PsychicTMHM, //.enhanceItem = ITEM_TWISTED_SPOON, //.berry = ITEM_PAYAPA_BERRY, //.gem = ITEM_PSYCHIC_GEM, @@ -539,6 +553,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_SUBZERO_SLAMMER, .maxMove = MOVE_MAX_HAILSTORM, + .paletteTMHM = gItemIconPalette_IceTMHM, //.enhanceItem = ITEM_NEVER_MELT_ICE, //.berry = ITEM_YACHE_BERRY, //.gem = ITEM_ICE_GEM, @@ -555,6 +570,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 15, .zMove = MOVE_DEVASTATING_DRAKE, .maxMove = MOVE_MAX_WYRMWIND, + .paletteTMHM = gItemIconPalette_DragonTMHM, //.enhanceItem = ITEM_DRAGON_FANG, //.berry = ITEM_HABAN_BERRY, //.gem = ITEM_DRAGON_GEM, @@ -571,6 +587,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 13, .zMove = MOVE_BLACK_HOLE_ECLIPSE, .maxMove = MOVE_MAX_DARKNESS, + .paletteTMHM = gItemIconPalette_DarkTMHM, //.enhanceItem = ITEM_BLACK_GLASSES, //.berry = ITEM_COLBUR_BERRY, //.gem = ITEM_DARK_GEM, @@ -587,6 +604,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 14, .zMove = MOVE_TWINKLE_TACKLE, .maxMove = MOVE_MAX_STARFALL, + .paletteTMHM = gItemIconPalette_FairyTMHM, //.enhanceItem = ITEM_FAIRY_FEATHER, //.berry = ITEM_ROSELI_BERRY, //.gem = ITEM_FAIRY_GEM, @@ -603,6 +621,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = .palette = 15, .zMove = MOVE_BREAKNECK_BLITZ, .maxMove = MOVE_MAX_STRIKE, + .paletteTMHM = gItemIconPalette_NormalTMHM, // failsafe // .teraShard = ITEM_STELLAR_TERA_SHARD, }, }; diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index 9d3caea9d9..d4d4a19c58 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -621,115 +621,6 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_KEE_BERRY] = {gItemIcon_KeeBerry, gItemIconPalette_KeeBerry}, [ITEM_MARANGA_BERRY] = {gItemIcon_MarangaBerry, gItemIconPalette_MarangaBerry}, [ITEM_ENIGMA_BERRY_E_READER] = {gItemIcon_EnigmaBerry, gItemIconPalette_EnigmaBerry}, - // TMs/HMs - [ITEM_TM01] = {gItemIcon_TM, gItemIconPalette_FightingTMHM}, - [ITEM_TM02] = {gItemIcon_TM, gItemIconPalette_DragonTMHM}, - [ITEM_TM03] = {gItemIcon_TM, gItemIconPalette_WaterTMHM}, - [ITEM_TM04] = {gItemIcon_TM, gItemIconPalette_PsychicTMHM}, - [ITEM_TM05] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM06] = {gItemIcon_TM, gItemIconPalette_PoisonTMHM}, - [ITEM_TM07] = {gItemIcon_TM, gItemIconPalette_IceTMHM}, - [ITEM_TM08] = {gItemIcon_TM, gItemIconPalette_FightingTMHM}, - [ITEM_TM09] = {gItemIcon_TM, gItemIconPalette_GrassTMHM}, - [ITEM_TM10] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM11] = {gItemIcon_TM, gItemIconPalette_FireTMHM}, - [ITEM_TM12] = {gItemIcon_TM, gItemIconPalette_DarkTMHM}, - [ITEM_TM13] = {gItemIcon_TM, gItemIconPalette_IceTMHM}, - [ITEM_TM14] = {gItemIcon_TM, gItemIconPalette_IceTMHM}, - [ITEM_TM15] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM16] = {gItemIcon_TM, gItemIconPalette_PsychicTMHM}, - [ITEM_TM17] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM18] = {gItemIcon_TM, gItemIconPalette_WaterTMHM}, - [ITEM_TM19] = {gItemIcon_TM, gItemIconPalette_GrassTMHM}, - [ITEM_TM20] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM21] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM22] = {gItemIcon_TM, gItemIconPalette_GrassTMHM}, - [ITEM_TM23] = {gItemIcon_TM, gItemIconPalette_SteelTMHM}, - [ITEM_TM24] = {gItemIcon_TM, gItemIconPalette_ElectricTMHM}, - [ITEM_TM25] = {gItemIcon_TM, gItemIconPalette_ElectricTMHM}, - [ITEM_TM26] = {gItemIcon_TM, gItemIconPalette_GroundTMHM}, - [ITEM_TM27] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM28] = {gItemIcon_TM, gItemIconPalette_GroundTMHM}, - [ITEM_TM29] = {gItemIcon_TM, gItemIconPalette_PsychicTMHM}, - [ITEM_TM30] = {gItemIcon_TM, gItemIconPalette_GhostTMHM}, - [ITEM_TM31] = {gItemIcon_TM, gItemIconPalette_FightingTMHM}, - [ITEM_TM32] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM33] = {gItemIcon_TM, gItemIconPalette_PsychicTMHM}, - [ITEM_TM34] = {gItemIcon_TM, gItemIconPalette_ElectricTMHM}, - [ITEM_TM35] = {gItemIcon_TM, gItemIconPalette_FireTMHM}, - [ITEM_TM36] = {gItemIcon_TM, gItemIconPalette_PoisonTMHM}, - [ITEM_TM37] = {gItemIcon_TM, gItemIconPalette_RockTMHM}, - [ITEM_TM38] = {gItemIcon_TM, gItemIconPalette_FireTMHM}, - [ITEM_TM39] = {gItemIcon_TM, gItemIconPalette_RockTMHM}, - [ITEM_TM40] = {gItemIcon_TM, gItemIconPalette_FlyingTMHM}, - [ITEM_TM41] = {gItemIcon_TM, gItemIconPalette_DarkTMHM}, - [ITEM_TM42] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM43] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM44] = {gItemIcon_TM, gItemIconPalette_PsychicTMHM}, - [ITEM_TM45] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, - [ITEM_TM46] = {gItemIcon_TM, gItemIconPalette_DarkTMHM}, - [ITEM_TM47] = {gItemIcon_TM, gItemIconPalette_SteelTMHM}, - [ITEM_TM48] = {gItemIcon_TM, gItemIconPalette_PsychicTMHM}, - [ITEM_TM49] = {gItemIcon_TM, gItemIconPalette_DarkTMHM}, - [ITEM_TM50] = {gItemIcon_TM, gItemIconPalette_FireTMHM}, - [ITEM_TM51] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM52] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM53] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM54] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM55] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM56] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM57] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM58] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM59] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM60] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM61] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM62] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM63] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM64] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM65] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM66] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM67] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM68] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM69] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM70] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM71] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM72] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM73] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM74] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM75] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM76] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM77] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM78] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM79] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM80] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM81] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM82] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM83] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM84] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM85] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM86] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM87] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM88] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM89] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM90] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM91] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM92] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM93] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM94] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM95] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM96] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM97] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM98] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM99] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_TM100] = {gItemIcon_TM, gItemIconPalette_NormalTMHM}, // Todo - [ITEM_HM01] = {gItemIcon_HM, gItemIconPalette_NormalTMHM}, - [ITEM_HM02] = {gItemIcon_HM, gItemIconPalette_FlyingTMHM}, - [ITEM_HM03] = {gItemIcon_HM, gItemIconPalette_WaterTMHM}, - [ITEM_HM04] = {gItemIcon_HM, gItemIconPalette_NormalTMHM}, - [ITEM_HM05] = {gItemIcon_HM, gItemIconPalette_NormalTMHM}, - [ITEM_HM06] = {gItemIcon_HM, gItemIconPalette_FightingTMHM}, - [ITEM_HM07] = {gItemIcon_HM, gItemIconPalette_WaterTMHM}, - [ITEM_HM08] = {gItemIcon_HM, gItemIconPalette_WaterTMHM}, // Charms [ITEM_OVAL_CHARM] = {gItemIcon_OvalCharm, gItemIconPalette_OvalCharm}, [ITEM_SHINY_CHARM] = {gItemIcon_ShinyCharm, gItemIconPalette_ShinyCharm}, diff --git a/src/item_icon.c b/src/item_icon.c index fd6f041695..45775f6674 100644 --- a/src/item_icon.c +++ b/src/item_icon.c @@ -5,6 +5,8 @@ #include "malloc.h" #include "sprite.h" #include "constants/items.h" +#include "item.h" +#include "battle_main.h" // EWRAM vars EWRAM_DATA u8 *gItemIconDecompressionBuffer = NULL; @@ -163,6 +165,12 @@ const void *GetItemIconPicOrPalette(u16 itemId, u8 which) itemId = ITEMS_COUNT; // Use last icon, the "return to field" arrow else if (itemId >= ITEMS_COUNT) itemId = 0; + else if (itemId >= ITEM_TM01 && itemId <= ITEM_HM08 && which) + return gTypesInfo[gMovesInfo[gItemsInfo[itemId].secondaryId].type].paletteTMHM; + else if (itemId >= ITEM_TM01 && itemId < ITEM_HM01) + return gItemIcon_TM; + else if (itemId >= ITEM_HM01 && itemId <= ITEM_HM08) + return gItemIcon_HM; return gItemIconTable[itemId][which]; } From 6ad443c9a80b32b67ce1187e9a7deeca4d0e2418 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 29 Apr 2024 22:16:58 +0100 Subject: [PATCH 45/71] Fixes Eject Items causing wrong pokemon to take damage from entry hazards (#4465) * Fix hazards damaging the wrong pokemon using eject items * Added Stealth Rock test as requested * Added Stealth Rock double battle test --- data/battle_scripts_1.s | 15 +++++++++ include/battle_scripts.h | 1 + src/battle_script_commands.c | 2 ++ test/battle/move_effect/stealth_rock.c | 43 ++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b815c04d6d..2fae7c9e19 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6281,6 +6281,21 @@ BattleScript_DmgHazardsOnTargetFainted:: moveendall goto BattleScript_HandleFaintedMon +BattleScript_DmgHazardsOnBattlerScripting:: + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE + healthbarupdate BS_SCRIPTING + datahpupdate BS_SCRIPTING + call BattleScript_PrintHurtByDmgHazards + tryfaintmon BS_SCRIPTING + tryfaintmon_spikes BS_SCRIPTING, BattleScript_DmgHazardsOnBattlerScriptingFainted + return + +BattleScript_DmgHazardsOnBattlerScriptingFainted:: + setbyte sGIVEEXP_STATE, 0 + getexp BS_SCRIPTING + moveendall + goto BattleScript_HandleFaintedMon + BattleScript_DmgHazardsOnFaintedBattler:: orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE healthbarupdate BS_FAINTED diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 0cf14c0fc0..878389da98 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -77,6 +77,7 @@ extern const u8 BattleScript_EncoredNoMore[]; extern const u8 BattleScript_DestinyBondTakesLife[]; extern const u8 BattleScript_DmgHazardsOnAttacker[]; extern const u8 BattleScript_DmgHazardsOnTarget[]; +extern const u8 BattleScript_DmgHazardsOnBattlerScripting[]; extern const u8 BattleScript_DmgHazardsOnFaintedBattler[]; extern const u8 BattleScript_PerishSongTakesLife[]; extern const u8 BattleScript_PerishSongCountGoesDown[]; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 94a85dfad0..4b4e3faf5a 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7040,6 +7040,8 @@ static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) gBattlescriptCurrInstr = BattleScript_DmgHazardsOnTarget; else if (gBattlescriptCurrInstr[1] == BS_ATTACKER) gBattlescriptCurrInstr = BattleScript_DmgHazardsOnAttacker; + else if (gBattlescriptCurrInstr[1] == BS_SCRIPTING) + gBattlescriptCurrInstr = BattleScript_DmgHazardsOnBattlerScripting; else gBattlescriptCurrInstr = BattleScript_DmgHazardsOnFaintedBattler; } diff --git a/test/battle/move_effect/stealth_rock.c b/test/battle/move_effect/stealth_rock.c index 23a773f8e8..f8c7a6369e 100644 --- a/test/battle/move_effect/stealth_rock.c +++ b/test/battle/move_effect/stealth_rock.c @@ -30,3 +30,46 @@ SINGLE_BATTLE_TEST("Stealth Rock damage on switch in based on typing") HP_BAR(opponent, damage: maxHP / divisor); } } + +SINGLE_BATTLE_TEST("Stealth Rock damages the correct pokemon when Eject Button is triggered") +{ + GIVEN { + PLAYER(SPECIES_METAPOD) { Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_METAPOD); + OPPONENT(SPECIES_JOLTEON); + } WHEN { + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); MOVE(player, MOVE_HARDEN); } + TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, MOVE_HARDEN); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Go! Metapod!"); + HP_BAR(player); + } THEN { + EXPECT_EQ(opponent->hp, opponent->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Stealth Rock damages the correct pokemon when Eject Button is triggered in double battle") +{ + GIVEN { + PLAYER(SPECIES_METAPOD) { Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_METAPOD) { Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_METAPOD); + OPPONENT(SPECIES_JOLTEON); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STEALTH_ROCK); MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + MESSAGE("Go! Metapod!"); + HP_BAR(playerLeft); + } THEN { + EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP); + } +} From c5552f53da67d65b3809f0a8d8c7f1047aa1ed8b Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Tue, 30 Apr 2024 00:12:34 +0200 Subject: [PATCH 46/71] checkteratype setteratype (#4460) --- asm/macros/event.inc | 13 +++++++++++++ src/script_pokemon_util.c | 19 +++++++++++++++++++ test/pokemon.c | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 4817dd95a1..dba4b3ea33 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2172,3 +2172,16 @@ .2byte \status1 .2byte \slot .endm + + @ Sets VAR_RESULT to the Pokémon in \slot's Tera Type + .macro checkteratype slot:req + callnative CheckTeraType + .2byte \slot + .endm + + @ Sets the Pokémon in \slot's Tera Type + .macro setteratype type:req, slot:req + callnative SetTeraType + .byte \type + .2byte \slot + .endm diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 763bb3f9e6..f7dacbbfb7 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -294,6 +294,25 @@ void ToggleGigantamaxFactor(struct ScriptContext *ctx) } } +void CheckTeraType(struct ScriptContext *ctx) +{ + u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + + gSpecialVar_Result = TYPE_NONE; + + if (partyIndex < PARTY_SIZE) + gSpecialVar_Result = GetMonData(&gPlayerParty[partyIndex], MON_DATA_TERA_TYPE); +} + +void SetTeraType(struct ScriptContext *ctx) +{ + u32 type = ScriptReadByte(ctx); + u32 partyIndex = VarGet(ScriptReadHalfword(ctx)); + + if (type < NUMBER_OF_MON_TYPES && partyIndex < PARTY_SIZE) + SetMonData(&gPlayerParty[partyIndex], MON_DATA_TERA_TYPE, &type); +} + u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 nature, u8 abilityNum, u8 gender, u8 *evs, u8 *ivs, u16 *moves, bool8 isShiny, bool8 ggMaxFactor, u8 teraType) { u16 nationalDexNum; diff --git a/test/pokemon.c b/test/pokemon.c index 1ae655ab3c..2e35896027 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -307,3 +307,19 @@ TEST("givemon [vars]") EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_GIGANTAMAX_FACTOR), TRUE); EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_TERA_TYPE), TYPE_FIRE); } + +TEST("checkteratype/setteratype work") +{ + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 100, 0, FALSE, 0, OT_ID_PRESET, 0); + + RUN_OVERWORLD_SCRIPT( + checkteratype 0; + ); + EXPECT(VarGet(VAR_RESULT) == TYPE_PSYCHIC); + + RUN_OVERWORLD_SCRIPT( + setteratype TYPE_FIRE, 0; + checkteratype 0; + ); + EXPECT(VarGet(VAR_RESULT) == TYPE_FIRE); +} From 48d71b0de1fa1d418baeffc87390cf69c4d729df Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:59:02 +0200 Subject: [PATCH 47/71] Hard Level Caps issues (#4420) * Fix 1 exp gain on hard level caps * Level Cap issues * fix compile * brackets --- src/battle_script_commands.c | 11 ++++++++++- src/pokemon.c | 11 ++++++++++- tools/trainerproc/trainerproc | Bin 0 -> 50320 bytes 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100755 tools/trainerproc/trainerproc diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4b4e3faf5a..f07c560877 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4416,7 +4416,15 @@ static void Cmd_getexp(void) gBattleMoveDamage += GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expShareExpValue);; } - ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted); + if (EXP_CAP_HARD && gBattleMoveDamage != 0) + { + u32 growthRate = gSpeciesInfo[GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPECIES)].growthRate; + if (gExperienceTables[growthRate][GetCurrentLevelCap()] < gExperienceTables[growthRate][GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL)] + gBattleMoveDamage) + gBattleMoveDamage = gExperienceTables[growthRate][GetCurrentLevelCap()]; + } + + if (!EXP_CAP_HARD || gBattleMoveDamage != 0) // Edge case for hard level caps. Prevents mons from getting 1 exp + ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted); if (IsTradedMon(&gPlayerParty[*expMonId])) { @@ -15987,6 +15995,7 @@ void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBat value *= sExperienceScalingFactors[(faintedLevel * 2) + 10]; value /= sExperienceScalingFactors[faintedLevel + expGetterLevel + 10]; + *expAmount = value + 1; } } diff --git a/src/pokemon.c b/src/pokemon.c index 5b1a10a908..f1e8207470 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3530,8 +3530,17 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov { u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); dataUnsigned = sExpCandyExperienceTable[param - 1] + GetMonData(mon, MON_DATA_EXP, NULL); - if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) + + if (B_RARE_CANDY_CAP && EXP_CAP_HARD) + { + u32 currentLevelCap = GetCurrentLevelCap(); + if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][currentLevelCap]) + dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][currentLevelCap]; + } + else if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) + { dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]; + } } if (dataUnsigned != 0) // Failsafe diff --git a/tools/trainerproc/trainerproc b/tools/trainerproc/trainerproc new file mode 100755 index 0000000000000000000000000000000000000000..903e822d1bdea8722889b3a105eabd820962b08f GIT binary patch literal 50320 zcmeHwdwf*Ywf~tUK;)4#C~8!cQHO?9h=~FcB{oB5k~2C35dxy3Lzqm+)R2kE3{``7RH z`*0xHXMguvYp=cb+RwAk$(=4wd4|QJ$SYI1QlZ$|a*mXWgsx}B4T4f-iZTMf$0+A1 zgAvbS7*8+d0aE#OAR|4@7I-E?;>)F*6NIBVtjJJmNFnhJbakG|Q)H-Ed7StL(XEYs z;j*#csnAlz<1#ezvHV&Ey(w%xi{mmh@{w&SpW^A}aPAO}cZyK*k-3Pk>vufe93G^C z;F6)VC+SE(Q-nRuVJXt+DrLVF#mv_t_{^bG(94kWpljBlaLxZFze>T^@c@@&4%c&7 zk)h1*8t{=_{->9yc4el>&uo7gUnhjhP?%S_xIVaG^2CelYbMkO8zL(SFv)LGEG&KsFuF2CT&5H4DEyGwiGNt}+^?1U51#+X zilX+j?wwmS`Nkv7L__5u9-^U(F#3@f@skS@4J*o^^OaW1=nTTn#t*fh{Bu{1uANeR(S26J9?6dZNU}0q_?Gz^O~IA~*N6PdA|CjB=pg(gA0if2BQsw~u2&4@Hc!hWT8NmDQ!piKRX8kO4SK;UMIG*FE4pgw1^QsHq= zEAw4aFfny^Y5(md1(Ot?yUOc>27!ga=5U~?%3D?+Y6w(SFQ^YdiG_i-B+6s`R6Ne>&jSAaxV7@AGAbAxK|R(%fvvyqL3C4@%7-EzIXj3h zGDG=X(93>V`b|d#Pni1@4<%kI@H!K|y^Yhgn(+79IljV#9}#$m3IEj!PQS*4|6bs0 zO}LcvqzV74pkHUgcM5!?372v@P57&VewPVR}GrJP+R{9{4iWx~G__yH3xR%ArJP<9ex9ILew*(9`2x>2;Zly(gijXqxhA|!;5HL}jljp5@aqL$ zXu<;mpJKwLoKh1W7WA44Zxwi@372x_n(%uC{d^PtkihFqxRldq!ha*^TTJ){fw!9Q ztpZ2{l(W`^9}@IWn($8qzRrY8IU7y5CC2@x(}b%6-(|w3oGuf7 zzMwx~!ixlc(1c4l2@_r_=zC1KSKz%ST*^_NPWOMGpwBkpA%R;>xRjG?!fzM!HWRK3 ze4GiFatclO&jtMy6aIw2OHH_xqnYpxg1*v(Zx#4lGcNpLz6swa=<7`Q+X8Pi;ZmO# z6MjU{x0>*81ir$AOF11TeCSGEe``$m=>lJC!lj%iP54+rzs`hTD)5aaT*~P*;Z8xn z%Y@Goc$W#6at@gAYC(U{gf9|!!h}mXJtlmapzk%|y1*5&ec^F53pB6&mnUBr(D%27IUi zFE!x947g^%ha2!p13tol&o$sV27JB&w;J#|13uD#HyUu`eo~78Kg~ejYQSk9P+ltx zIPG!CtHXewA;FX3Nei5`z)1_7w7^LVoV3763;ZuEa8y0-nAY}DwiX-ohkF%8i*|-H z`nt5XZP{Cyw7yAu0QZf30l(ETrMRPb9i=4?_Td`4h2k`YOm?yOCW_M(I@!tMf224~ zC6nt|{5KS*sdREJi~ow^G^I>-u=p=1PE*NbD~sPxahgIV8(I7wiqq6FIiJOULUEch zCM#L|R*KV9Fuhs-Z!D z0Ua@B0pHnfABCU>vec37_Rm1#(LdDme>(LK64OzDw%$Iq{VpW8^#;R@ZM~tePm7g& zwn|a-En0k178ES%Ost3QTI@8Wynz(pw}lw~F!1LfQ_*5v(HiX{uDKfhPb@sNyS7nz z^u!G}I_5cUbll|dZS}-TA`p@cBX=z}AxQUixc5f;!Vfv(PJ7=KgFFButF$#veXN6L z_|pZ@JZ9hOf!=43CG{QAPBj|CD#;U@FbYJDXX!Ch(VN}5wFT-JzF>ChJ)09_oR*%w zy$;JZO@G_%|15DC*q!>jn!XkOt+p3ms3^|-Pu>1+J$mwaRv7<2&9XD`qi>LnWrwTy zvpfFciH}`Yjq*efYq8U(HN#bxNtB@P)cm`&SW|Yq(vpw+eH+72*Aw>?TeNtIqZ&NPm?Bf#ar*a*hE#*42U~xUL4&g{#vuLqfvz_`DkH0H_i{{_$ ziJ$x0d_~z9giLSTbE?Ig|FWj?`hxoZ&$3r%~~j4nkIKTP&X9J&|{``02kU zu6l5J^7nySCF;{bJd}w43q6(Awi(2`BkyYQL3eDb=8e3?!K&6 z`m4xDjUHtUPB4Jb11Dla1asn#U?k%V1>sJoy`Bu_(d+Gvnm*6of~6 z!&q!SGZSD@r$_&iBplU#tBzrOg(<$Na8anMD9Q?_{ffkpa$ZU6RVp-$frQvf`#K6% z+v&&!^Y(4%B{lyiT4rN*{vJ*5aqELY{3Md$`?vTgnD7Duhdw9Up_f~!8I(d54JBid z`680t-~P2&l^ugw7zAYPl2WdTjyiQBZ}QpLdgij8*uzYk*T2P4{HfYL1xb$htroop zh&y(5wllv=^Y>}-39sCwDAVH}#iHrQHabYX{Oz*-)D81S_7xw9oT)zVJe{?kCQ?ee zPlrc8L|kscC8*CxQ9o;>Cdx8UN}*%mPV)%jD=YpGver!X&?UX(5Ej4&X3uPQzX16A0UalxN+(<3%N$RG_ zn=T%MQM1QFKAwC4Ald=t>*D#=h0_Zy(bZW+w(2J zVICJ6jbImMfqG;Quo4M1DCYPyVBAVAEUgJGr@5Cyf3Oij^5cK^^`WalJ10HAq2aCV z`3|1XTx%+y>uS^ktgX{R zT~e}!G@#LT=2y(ZxuiTl7XIZFvhaG=R{JeHV%}Un5nzp0|5Sb6 zLI(Z`q_NuEhsiN4OfAlm{5kh^ZiDuVpip*uco3>nWG+QIMWoVI^rk!hU%kl7gJIFu!Q|b|A zgdW@#?SqWZaB~6 z$jC;?!I5A}SUvI67u*S!c*)T@==1c=n;<~ZUAY^lA#_D|hpTwMfPv`DzV^;=9%eQe z33f!z)8f<6sc%u+KcPJJErKHQPu7pPW4GqI+qUF7+P1EFy>UHazT}Atx z`fTWY?w7Y>#^r#bUO!hH>yOJ&4onNKKtSp*6!TwbcZSe^8T8-D^>?EeX?LgeCkmmz zC+4?mZCi7_(f4TG)7;~Xd3#v_$d2lU!N`(|@`R8N#uuD1m(>;RjL=eqcoUaFb&nqC zfv^O0rxl8wL)n0Ob(a<5Badov*J$$4T-;2do5EW7W7_=Wk;{0!KY_x=9!{H5dt#4^ zDfL#Ydf=Lx|7%VER@1-2ID5rx)aC&v##t8)v`=H8bztfpJ(Z09+(t-44|6%14_tx` zfbpNwv`ddt66=$h=l=(0(bQIJu^LMjfwyCtx*=-_L8qaiCJXV4`w8KLlOH18gt0aY1pkmbM)ilk< z(3S;McU2x$LufCo%4Tco$jB58jn(GSm)T4Ingu?Z>S|g%tJR~2?YSQ6leNtUGq_ph zdIx@TQx(yEdHl*}z-7-fg}e@iKoM{z+=!F$*FhPyMVg^GF@SWkkdH<#)S}g)?{?|m zIQ7@u{?DEHM=&|jEMIEH$HG%FFK|P9bi-NJsm1Eu*MfEGn8kMYUr`XnQrMU2Rv+8i zJjxTl-ZIsuM&}^iqrU;($WB-B5w-m$27Kv+&rEgdLI?e6rbFVXi_s;opGaCE8Fkej zM)jk>>$%E5%>_52TxHy#O9P_juV=G+HJ`6z)%=xgfq}{#;?$#~L88Sx_Cn3S#g+dJ zMhh+Ow3HB(3qxmVmR)ZDOYTe$rq?>wRdv}q%c!l}qDF6nUr>M5N_t}uEv5QFC85gt z7Zc$L%;%0>%FSI&8MdL0G3?^EcGvb&hV%|rqve0XMk~AkynFgkiw%K`d!K zN>eYs-fv4d$ih2Ffxp3v%kYz_sgLbc+x`g5(YD>@In z6qz=NlCO|jv`nr=t$C?CaH$(c4W&h-t9Y*(odFnyW#jcZ7_XVp{yYkxn0da1v70UB z;)AZ31r+~^YO$7Vx4*{`FSR)Hx1hzXM54Avi-*s!be|^oIk1hO74Hu(!I0a! zY)IeG$l*3#UUCZ(hm9{{#DA*3rtXYJXZ~i9k7XBbD5q|yIB2q|257D-&7b@;CVrrKx{_+)J0^>?B9gVc1pQE{er}g`ILI$2kz{5x8;w8vRjLg}|YMS6;@=Y$KsO>}0_c9N>6uilg zQ6Mbzu)@O_)I1#ow9t!`(<1MOGzPUj-$k9g8g*kwmW?y0Y#M+t_kRH?Bnp#OB1)Vu ze8UY3Cm;Tl&tTs}ig^Z0)S^XnuR=NVPGHY8+uV&Ar?lrIpZE3Y2cYg5Qe8Fr8B|0Q zj9lCL+;g~vy0zHUL!n7iOhA76EP+3bQeyvLc?;GV)GUf&q5l0F6ew|~A-{9@0^v_y zGLE%A>WO@8e`7zBCTzQrFf4TRVh=<(_p-02+qv99UpGIM2T)^crmRKoW+WJ7(HkdO|)o_pnvVy0uoTRB1rCmHpM%_ce`V^Wn-2%b%+~o zQ2dn|Z8CVowYWzeSgtabPmVXdM@qhmdMfHnzH@|;et<%Jr|I7v(O+F*@St>!&l1!3 z{1i82QZJHE=43L_jnwcVZC(Y;bt=1`Gz!|&66l@xFrw(&SyOoi-G9*+XgHgYjgt6J zBzZ6$Xh5iyz?be+?|lnx1kZFZ(9^sl%bvJ}62&8$D>0Z5!{L7ci;co=coOp(_N*rH zbA}DKYuLzWoA*8uluQZ}JvQJcJ)|M$PWFf@afDIw-Edm04oZ{|g2m?~gx=_3JgYdw zHLC)U9ES+jDNjN(%#Ubm9sB7mc(6syIX)>9qCI+FVg+dR?X<;Mg#;9ndNoE;;zaG^ z=~Qhq3jGe8qr0_jncW7TF2FG4_Rq<7$8y?^W!;gFhez>AtFbMtS@v}2Xl)5pS?||s z`(V(i8Mh_Q2$sQ4fCO8K5?NN;+>4C5ms`VpFq{`^xPU$s`SFS@ItIA z$383>?S2`(^c~xH`1{RHZRd{^L z(E1LlhgP%-OfriOCmwu$7)Kt))c#7YMyhD`mXFT#?&jBN2P}X%+ z*8E=i$m2c({Q;tPnCMrh(?2EX-Ni3Q7(IINH2H&_Y4Z1)>8F|K7o^MIZPo|YOOvmq z(|=`_k116eeF-u&ly?HPDAJE~P9N=zf6eC^XYm}S!9>|)()+uZZlp5$g&L*a+#t6Q+c{6O?Hs4NVI|x7!5$Bdx>-lY24b zw%y){?)Q~`v08j~z3ysEegVQ-oiImQ6my z6HFAUMgM6l$dacS^cceAcL0Wbnhy#~VWqJWm>#91WB?o;aQ{ckC>QjuhCV)R()uK( zJhC^U9XeLlm(ioOJ@+K#fsI7!#MS5uQV-hCN2otSX|hCy1;#ugGEAVbz*qxHJ-QaZ zlOD{U=-dzVI`{kZvKsX2fjTaF(%^=Gw&&JC78JS&dG(k5X<&Mkl9CT%`G+j_QA_x) zqW=!_0$KD|kp+1E_S>tfpEXL$ye3;K9%T=or^U*$VR>~5spqw! zs~T0**?peKXA^R0)ht`I%^%t#KXhO|PW==bBlah8#A_5goE8~}gMn5$*@fb_Bbf-8 z)ZN(2F-YB&upi+4hGPyqd#IKkNAU}4`*6_X>Ci#1|1+=U6KC-|>Z(4d@6mTT^+VmGQu{gBFQ)C1;(vq( zwN4ot>2b$S)xUD+38=SsAs#*DAB1)aw1aLPuQ9cs0bbOJ?fdwM7s(9qNr$mhlxRY# zqwV%w%N;k+88=ID;`W(shch()0cZZ9>G7IL8PB2VD*4;Hhtqy3_G#S3Z-p;*GFQMPzV6J_6w z?A<$EBnjF%cDTk_YW({cAczii&A}4f(-k9r`~se-aLMCT}mr$JA&mlE}DP@%Hc% z_4!eQ9axZNVTf{x;Ee1fKd&H!3q>L9U+SL*YVVv&Sp` zHubE<|B6?1O+97tTk(otBDN2tfAf(7M8Ee{Mjv6PS$WyFcHk)uE9W)rk@nk(7eIu3 z?_5DgUhyb$6UH!w8S(~nB|UgD%+4_;?PL(adpdX%4}h_IPlw}LP5;ELf5})JbKLrV zW)ap_~>;`BVp5Eh=M7k`(H%W z6Bg1OvHLsG%&?Ohy&b!>j@VBcDcY$*ONQFsgza!})9Dx58oZeayT1cbW_Zp#X*lT^e3=`6xRea3L#Y{cQ39l*mL3El z7#*>DNTNG-Kb@{g{1T6UxJkb1Z{R|dkDB_u$T`COBGN3c@f-$hXr`Z8>AI%2<| zV;s>p)q8(~s4I34ZH?h6H%{81)UMc1Xd@6$xYcMMF9#iUFqUHjmE+NUyc`n@AcA^JlH=C)P{gaLn5Bs5MO+soBF)1c-Ul28o^ zF3diAAi)bs34~PW_73CWbt9B`+27)`x~86`KG@lM>=gCEE!ka>ZSgX6YU-6ykBE4y zi2qW=SBSVp$XmOANKIVL_*UDW6dKr(BWb)&XmGWKX)rCN!7pB-j8@y91_2$4r7V_{ zt?r@(Y#;s^30Uh~PK-EqixV%S(Zol!Y8x@F>r*i8&^Et2Si7+OG|EGbo}bNg+{JT@ zZWcD6RiApe$V8~C^dVmrxW72SZ3XkVUTd++qy12}$-fKICOxm_hk@E-~ z?eA#&*Eg+Sk5!|~papqlax7L$m|wt2*+`tY`bDe^`k)RUA1FVVGL>Jr1o`a*O~2*Z z>0v(X%1`CQC+$RsgW>T45D~Y{z+Fq+IubD7Zvz>s;w}VWCUNVjA~e;7mYsO-ADkPt zo67swAm#d1AxEy?COZg_jr5UroI6kCk$$Lh;Un#Dp^UuZFXFl_7t)P=&UW&y!$^Q` zAB<DqDgk%Vr_0NY=c0Kw6(muw9BG((eiQ3+as4HG&x4879 zo3PNrj`J2*{=ZP)jsp0KWh;){cc0DXZ8&l;1P_?n>)~wF*LjK$ga=Ql8q^m)Ht zTg%_mJzC7q@v!1-+Oeu)?07a={5JA+o{M~4`YX)p$#>yrIC{)2{NzQf_U$Mx)jrxp zs{UqD!;X<1PD$AT9`G}8fESg1Mtkt#!M|#|cdmA0m;QM|6qphp83eMUEdz zE!{#(qw}0gTCQW9EA4ZU4RvXi2)R+tpyazgJHAWX2dRk`kz*a`(Sfwbc#e0ba(rzM zSE&XBP^AlQ$$GNH^CCfX&+XNm)0N_U1O!wKqYM^(3haq2p=*oKwK1jZ2c*)gM~~O_ z2bA!F$Z>_xwGaI?FJo6etN4oqK|9y=6)a!5uBRd)*#&au&z$FB&hzE&f$CQ ztle%G+M(X5W=A1P^*@^Bar$(S!YtPzAaBS8%Ie)FZ^qFL>iu5iBdWZAeumcOff|WV zk(fN#EA%4|u!4>2`Pn#Dwi!atT!S@#O;*`}1X3)Ub5P|gxP?+bC((C*%FAc$%nm?g z;ttAl9`Zz|&<>$|VCm0y8PE5%U7R1XQjO_PG$Xwd{BGoX0Ft38){V%SW?aa*k}`Ni za2lK8JW9x-oa?`!(za5*U{HZGA&G^8%UI6mshlq&fy&v5!cieNrYy9Pl7IQh@#XBv zL&AgLGxyZLxeN)nGl4KQ6ol)q+6G&Yzoy!j7h5Fp5+9@&y_uLHs7HYs3f~0L+_yID zB)xDdoWykAF`qr5IPvV(k@ z(7-seoj`J)5^^?WBL`IO#Q@#0%M*8PgXai+XhjJNwIE0w zIDEW?@=4@Nq6A5u+l9_{81wxHJU;P5k+02=?|;!O^*)hctg^q7*cKrc3T;E2R~apN zp%9%7NmQIbp*57(l~SPu3;}izz?4FXwV2tMePkqMI3$(fpl>c<9N_K6ouhRVoqCg# zP@fP$kaQIr<-3Ct*g{#Z)K|^nRY7k}q%?q3+Mg(T69?h96JL%Hc$z^V6$CGe{*KZc zgj^Tj0r19uoa>6dc?V8ejfXcJ_Uf;8pTp;mRIW@Zh0d&A3f7nxE6Ul(|9z;y4w7w0 zfA!kxRd*m%2_rfP;ICwH2X3TT=*yoDDmdG`_KS-Zf6Qhav4C{@Bk1SYTQ zL2BYpf|C^4mxy9PjzatN#P?A{($~f)ddC4V0o`w^MhID%yv) z7*L)CTWi(&%{ZMM&eilOY+dTq=ifRvr8-ilusB`hB z;VZS6mhJKHK$-JD)z-Ms8 z>K!lPXS+N96FkIlTY9>ukUTfGS8)VXvwTWtzJ|7bH8e5;?)wb7N#$=A{3_lR8Kpj7 zjw#O^yw(*~HGRJuFF^0Sp{SFd1G)0wg2}ey=QUTnW}*eg+TzsxR`yf|HC>j?9v7v? zf7&9)8&k*Jf5j@X3_qDhzlvR^#VWGNi*`#ds>pW6Lc<~d+EFN3u1m)wkC08>;2Q_B z{a0Dt`X#YD@OxTzcOLu*uT!+IfZ9_hgoitdci&#pmMENh%96=+x;}rq!}1S@zRT4M>a9ygOf6mfSG-g}FK9IFAy3#vzy8~$Ps8J~ba&VlK2y^# zVI_6y3*Zhvwyn4x-Y|`onLd7i{gKTU%n~i#KNIs}{yhk4P4GMR=;W@C)<9oPUuH*1 z*R!|J43GA3yo*v}L)4K@dkr;!xq6j-KE)bw#sW|DoOY}3!TS@K=~b%v9=p|bMUlGd zQE+*1m@Zy#uk^&1*$eTctotT<{|K)k;q-4QZPwsuMcD4tj(@LA)A20vh(0SjF%`QH z#8XKJ{c%z^JRLpBJ!8B!2fWzHS%qOo_yooDVE<{tfemk z3%yGMKXt&6ntqL~dpLA*=>bJNX~f47pmhJk#tZN*2{MggyNT;h@{XK`!*&0acn)r4 zc!FBn0q;tzz;07)hSd`r!n_HM*{f&3pEe&PHzBu~W_88pTAk>R{F|L|wJi_i7m9@Dd`w>LkPWQ zIlQ6_-Q{^s{U+FXtJ?k)WaG^>yfg8#`$?Nf?6 zfnN(>{y@#ovNLa1^YPp)@n_nIz$i!?s@5x}S;HeWbX@qz16<$QIyGZx_&hjOtja#6 z-+z9DrMZTRX@$KM>||!6a|+eidDpblX=jxLsNQbF@6mRLIJ@!NaJ0;<;wZ{&Wy`1vW*{N@t#nR!T1m(Xq_c z$QB!sub+cJFSAjTMpG=Kx_O-ZGBJxY{Q(4tCpc4k6}*cCUWRyL8ZSWmJ&?rg#jOdU zxekWFj_SyB)RBIxI`U@Jp$lG$!vT0T6$dD?Xio(%Mlv@_7C7U}rD2-AcWe$BIEG()vP_7&>L8z9?n>wYFxUMdq7hiPR>(no4i`w+*c<3!HyK#^$u z3+k7?&-0!etGYQ}eFxR{=j_M@D*PX_@Nu3ed3CAem&PL(?RM&#n7{gLT6`Y8!|**t$*-}OCLY0#399)NI*W-E^glS9ybCLO)L%JP zT8YuH4b|T^N>xMS?NoE*r2z#`_i0Z8Pun?yG=Wem3A&RDx{L(9jC&IFTjJPR&N%7Q zL=ZKYXlEfkaT98Qc4wdGxrOcXk>@gdBZ% z%fw>{;h$qbJd8PtfjD^^`2_PblAjCG#D%QS4Cld2qp9DjVf|KmpO|!|_#pN^Ebr3$ zEjyAEAO6wkZ`2tb!}NXjtQbCkqrdEom19|T2Hy1u*Y|(pNb@JeH;%?GLdiDbRVl1s zJ*QjT`5)0Yj>h1*G5^d_4t?fm6bd#Sr$)oKU?x>c4AaSe`5(&98;t}JK5rDp>tX!U zM(?}XIj?ii0I^%Y-gX0KQloCf{_s33joA2v#R`2e>1W+fvpABla@f5hlXduP4Zv^~ z?M|fnZ<+@kB`0y~udp)47LTG=p+18`?$~J_|1J!;hTRizMv2Wpcnr_x#M4CGSa zMmONaCw1QACM)T~!d%mDv+>b3m4z6em=aDZ@nTed_unxPEaFbmjvUb0()iTLSKnwy zuR|D)(D8AJ)(ln}doTdi+vj`qEUXYxhXVNsg_VPb4`Ixwl|vm?4vjmU_Hz{cI-7o- zMN^S%+~g{lNEPBnB{&DK4S4LMX^|180S#ksyS)|K(D-yDF%G+RSU5DsZnHWO);V$N zpuYG^wfzW4Jme_;{Z2hu^d=q_W?_l1bzvQT%94p}{u>|u5PWJ{e4f@t)6(LdYC8=U zn3kqdFUDk)eYfUQwVg&bHz?>Oq`-M<>b+g)uCPA9B>h_~q1lWo_X}x12}fD=KxU zpwxUeK4=2=j=CT3*9DpF$7-khzQmZ{-fwR*e2A>i6CW`QEE_cx%Nw6ZA0pd} z6|sB+f_;c=B=yM(o8$2p0p6jHHesHX_Ts=!JlOQ)<7-R&+faoD`U6DoG|->p(GMDE z?FL#LSvkhuGmt7!r`Mb=dz68m$)079eF5kz(&@8|^yq)k(d2`IW#ggmsClkrpM6_^ zaR>UdPfGPbB)yO7F2;*c^pbw}0e(JTfx+o?gLB0)dybBU_L0Qn*bSq%v*u%Qgmq~n zzKliO@kt+JW|MdysWjP{j!`MCVm2Z*`8iNof9&i$y_YZLtNPrm?nnCjCwlAREkk;) zJ~umgD{rE(FXR8nkbagvcX;yp^mIPBa{bTN=US7O8`AME-iuCDANBeb1#@7&Xl<*G_s7iPYf?Oq>i{LCJJQDkB%j{=6FIZ+jM~#kWvdGWlgYL!( zfZUy?0d=(SP7XsVJ7Y|byfI*kuYAIEC$EzhIB9{C7C32vlNLB>fs+K_#7=N9(#)iLJ99S4=LY#%Ra42M}$KO5o75Q>tdpb}8i!&n%a6F+T5kA^v=Nz*c0tU~KaRGBy?;pRnP&K_PDC zMq3Gw6IKvz!XHx(G)=Ew6tI=peB&-qF32B$e0o_u{(LhCke|an-8Iu!=5frLrH}}^ zq?pQ}pNJ|QGpmRwMLo??cD1k4U6umqzT78;mAkwSkIS~qHZ(=xnqD?z_Vg+U@y?zF zVwMf%gg@TAh%~v)rnswIUS+(kKMj9C8h?LzT6H*FPo?Qks1BCbS1)7*aJYTt9!G_O z-1^fOMHV*Kg~DZ_`cM-wdS_S6(q>fo%4T?G%ul6?&%di z+~8vegQwkBvtDr7WU*m=$jD@7cg1};eq+O{VuJgI(%z~?ogBGNT=_g;2LK%PX1pIFB zrx8^z2-XL~ODQMEG`GiHbzORT{o?u)mAYU7{+9P~8aKd{O{})2!-6wi>7s(ov+9Bk zEUPpX@F%yctClu0zN+ggOsSdJ&;N>=O{ZItx|D>Z&sXv zT4h$F13ymu1eB@luee`H8P*5=!7$3XD4;l5o3imnrFeNmQP2XK@CV@W;?A^-4u>VRb{80Hq4mXrp>mA`Le; zgqAee=!vQ$4ZNbuT0DWP9BK@6t zFgE91&brbC!G$MG9g4pxA8aV_D`=w4ftm?diY~BOxv;URdf}pKgT+NsV^heVrx+qi zaB;Iz&q^Z|H}gt&k!ar$h1^|Jcm0^p(fmH1R0R0u-?{)GawJvZw~s6I0N#O z3hr74=^V*Z&>lwAxNf5jCHNnY_T@&v}%SZ9$+mgtlE7x;d~2j*)6n zfklnzK3Hq=O`kE{(Eb{N{+r<|0cvof?I^A}X*NO>mP^MF%~Kq_TQZhUKxs-dC(MJ# z*4G@1+lS2YWkr__)2Ao;%;b0S7@uOhnLEx?5u7=cfs0M zI*#^&dUZltD4oMq43Bt5s5y*2EfhK~r3v#G#fzRIw8X^I6pA#|C@%DCb?GT?KOX-3 zgMQ2@(sHj11)EX&s!$7NJ?SaUOZ{~yyf@fTlb$j&%!}0D4Au6S}@M z{94pP6H0>Vm(&VPJ3v*04z{U*nm&su6tg*-f~abVO(?4i!pYfha}pYQGwa-&1LmYz zbxcY6Es|yh>oH}mU&{J)89|Nc0hvAyTe*1ED3&VQL@Z1_Y_wa%Fn3Ce(x}jR=t_@5-^pu{mehrq8Idd1pA? z<=5G&H1{l9x!dD9-{ub6g3Y$-NH{cMAzDQfnof6D}s*ug~SbzCt~beX)eaL|INY(yBU59;c%@Msv8DR%HD?G0-f5n*bkO8ekTIC3jPB zK?G9LQjKCP!29!qw9IYi%bCx`~&iNmG#KkVE<6j@x!H0vwNEKR76&7=JA1WL~iz z7|s|e9V>Ah3}IIs&;JK0iuF%@eO-uK*Z1{VA)pJP6XCiIIPE~;U;6r9LD+$}+cg|+ z>iP#>*hExkc(Q<+geG$&gd zrc6PUJ{|Bh(hds!;~B1$UsZ6wYRtIDBmCEDsW2)Pfzr-cu(vO7~gRFQMg zl6l#Q%YHTKNxIPYQRctW*Ecz(3-fV}d_1MgY?ChFCf*+K)}ViQ4fnkMEBfny73$xW zV|C_aR}Ayvp0~_$S%>4%cVzGH>pN5AyNYZee88D%n;ESp`(8aP;~EpB4I{fOgS-Qf zHwz)ROTSOBwxy9>gipXOKC%mJWolfgA@~=5UVt2JMPJ{gW2HQ1XVz}qkacp@T@L4aMEHKq@G+lh;WLoV3763!JpTNei5` zz)1_7w7^LVoV3763!JpTNei5`!2gpL=&0aomx@ppVtX6EqrG3_^=^~Wn8Y|+LnsBFx&obfFBEHCk zFB5Uygs%~Cp8Nm&_W{vve=Yd`B*M)i+#|wQMR-tz-6HH2;h;FrGe?AHiO?>>@gkfo z!htlTEmgYq^FSFcLdC{!Mu_-l;z1w2p~0@or+A3ZzpyJ?g!HvXy5@@X(LAci^v}QT z3lT)mjp!1OgOpvOqm%tzuSg%t$&_-T=r9qdeRI0#xtH-0Z1@&7N3k`}u3dtk9;eVX zga-;f)xfS^AxPpKLSdHJNPVc)1AYh_F_Ki$%Cxgb#}FKSj7fggZpIUxe?A@Cy+RK3`$i z86y0F2q%lMT!eE($f@yskUd0El!~&l%WdN-rq9l|6hUA;U|{#)X;91!MKJ`lKVA;;PCD%^gF9}8OPe9<4sc9RW5lYAw} z>Des? zaC(y6$`NVj*Z^|=1Gp9XTmK*>@bDP3587inkN-dO{&PM~_eFNa9qTpm7vwxlk+$NPUgj0ZDV2I0G>Q_$9z^ut85guMg z@?kec*iE)C`R+y-uM{-+@Is4%!)!}@>{SjQdtFYcZEOPWW9Jxg#3x1FET`ZzRH=dD!fWoK6`pul^drveFc{kDg4wTUe5FR zFjMfkG++5lhu7uPoHG?)g=fYzhsQUgynL3c%2(x>=5hIy+NMCDw8G<_R_40|vx-UJ ztPd{m`=Cuz*hlZ>q*5=PsNfK4gMU$D3StcIDz6VG+wiJUGfux&dCTf?6s?L*w}PHt zVn`!LhSfD`2z$vX4e9Vvt-n6hoUR?cjMR|M?yC(p1WB{{K)wI~f!cH| z9H{TtiVI$d*K&mMeIz;@GE>wvhkSK78wvCJ+%rIn0zG_8p1hF=o&ts zzonY$4sYJxY7&47MK=l>!{>4iqeGh!o%z zu}0MAQUkOg60EP85UddpUX`2>u3o4x@;bcFp%maCFtXrbxQVAM#(8f#`O3&d#D}z| zKz%hah`Yx6uu{M@F9-)(aL@d*pee+Bxgb#It8J=Y6!6v6fLF#ihp)P+sTztBhrA~) z$^rtII@ODCJilOJ2!ipefaS&|quP`Le`wJnyeOI8Xa8?vwEnf=2g4z|=#Rh9B}2LX z_Y_`;pCR~>{8I$K3`>z_yi((c04F0f^0x?n8QP7^xW}-RcDWHV@;@y2WoS#I zKA!(tqt#4a@XL9#S^sLJ)0$rL_lo>w*d-E3N3yfzm+J^xJ`juKm-`4Z zl=}uCVHe3TUbi4ZYjnvk*X1&tDu|{2l1_$qAf49yGA`HsGAtGRX8i@M#BoD)BKbQ+ z0c9xn9c2FI^8ZZmPZI*=K86hCz6bG>&CL9d0wbG9`HBq@x@_#XBKLQA?D+bB7zsvx zo8XgSDJ2>%%skm8!zU0k^2_<23?G;&7(^iXBudLTBfnfH$Z&)t6rtJw|0MXO{gqZh zD8i1@L|g>s{QnA^@|XN_eJ8{9>Fmd~zuhK&Z8c|*;S{++CgjNalVV@S4OY>t4G<>x zG7mm+NL3 z9wgzosQu6-^Of~rLAo*jb-(9yG91UFso#=chJ#J~avd(ibtz7MBl%>gn)v0qP=-=Z zDMs?i`03y$OqO4+gXFrTQWA>LW{RIJ@-H;ulHc5ZLC!8?D1Fx`VwWg*i|826MIw!5 oQh(Y1o&rqjZsafh3y;;C>R;w7X-5vg|1>o$To;-cOi|_k0L{Rre*gdg literal 0 HcmV?d00001 From 362f08148c9a30c8ed8b72daaa27d2f1f81d2297 Mon Sep 17 00:00:00 2001 From: Nopinou <140268269+Nopinou@users.noreply.github.com> Date: Wed, 1 May 2024 22:24:44 +0200 Subject: [PATCH 48/71] fix non expanded types names macro (#4473) Co-authored-by: Baptiste-Lecoutre <59924283+Baptiste-Lecoutre@users.noreply.github.com> --- src/battle_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_main.c b/src/battle_main.c index 9b1add03e4..b24facd6fd 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -298,7 +298,7 @@ static const s8 sCenterToCornerVecXs[8] ={-32, -16, -16, -32, -32}; #if B_EXPANDED_TYPE_NAMES == TRUE #define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__)) #else -#define HANDLE_EXPANDED_TYPE_NAME(_name) _(_name) +#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(_name) #endif // .generic is large enough that the text for TYPE_ELECTRIC will exceed TEXT_BUFF_ARRAY_COUNT. From 77e17247cded9028336fe0da8f19096fb8b0c9b5 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Wed, 1 May 2024 23:23:37 +0200 Subject: [PATCH 49/71] Color change fixes (#4472) * Fixed forseen moves not triggering Color Change and added tests for Color Change * Added issue number to Known Failing test --------- Co-authored-by: Hedara --- data/battle_scripts_1.s | 1 + test/battle/ability/color_change.c | 148 +++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 test/battle/ability/color_change.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 2fae7c9e19..9a1609d963 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6637,6 +6637,7 @@ BattleScript_DoFutureAttackResult: checkteamslost BattleScript_FutureAttackEnd BattleScript_FutureAttackEnd:: moveendcase MOVEEND_RAGE + moveendcase MOVEEND_ABILITIES moveendfromto MOVEEND_ITEM_EFFECTS_ALL, MOVEEND_UPDATE_LAST_MOVES setbyte gMoveResultFlags, 0 end2 diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c new file mode 100644 index 0000000000..b7828bb978 --- /dev/null +++ b/test/battle/ability/color_change.c @@ -0,0 +1,148 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Color Change changes the type of a Pokemon being hit by a move if the type of the move and the Pokemon are different") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Normal type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change does not change the type when hit by a move that's the same type as itself") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Normal type!"); + } + } +} + +SINGLE_BATTLE_TEST("Color Change does not change the type of a dual-type Pokemon when hit by a move that shares its primary type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_XATU) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Xatu's Color Change made it the Psychic type!"); + } + } +} + +SINGLE_BATTLE_TEST("Color Change does not change the type of a dual-type Pokemon when hit by a move that shares its secondary type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWBRO) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Slowbro's Color Change made it the Psychic type!"); + } + } +} + +SINGLE_BATTLE_TEST("Color Change changes the user to Electric type if hit by a move while the opponent is under the effect of Electrify") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Electr type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type when a Pokemon is hit by Future Sight") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_FUTURE_SIGHT); } + TURN { } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); + MESSAGE("Foe Snorlax took the Future Sight attack!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Snorlax's Color Change made it the Psychc type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type when a Pokemon is hit by Doom Desire") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_DOOM_DESIRE); } + TURN { } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOOM_DESIRE, player); + MESSAGE("Foe Wobbuffet took the Doom Desire attack!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Steel type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type to Electric when a Pokemon is hit by a forseen attack under the effect of Electrify") +{ + KNOWN_FAILING; // #4471. + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BLASTOISE) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_FUTURE_SIGHT); } + TURN { } + TURN { MOVE(opponent, MOVE_ELECTRIFY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); + MESSAGE("Foe Blastoise took the Future Sight attack!"); + MESSAGE("It's super effective!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Blastoise's Color Change made it the Electr type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type to Normal when a Pokemon is hit by a forseen attack under the effect of Normalize") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NORMALIZE); } + OPPONENT(SPECIES_BLASTOISE) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_FUTURE_SIGHT); } + TURN { } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); + MESSAGE("Foe Blastoise took the Future Sight attack!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Blastoise's Color Change made it the Normal type!"); + } +} From d59ef3710a3abe24dda6fd44aeb3d94536b95a41 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 1 May 2024 18:15:56 -0400 Subject: [PATCH 50/71] Fixed Future Sight script whitespace (#4475) --- data/battle_scripts_1.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9a1609d963..31b0619118 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6637,7 +6637,7 @@ BattleScript_DoFutureAttackResult: checkteamslost BattleScript_FutureAttackEnd BattleScript_FutureAttackEnd:: moveendcase MOVEEND_RAGE - moveendcase MOVEEND_ABILITIES + moveendcase MOVEEND_ABILITIES moveendfromto MOVEEND_ITEM_EFFECTS_ALL, MOVEEND_UPDATE_LAST_MOVES setbyte gMoveResultFlags, 0 end2 From 2aed78ebbb762720e064c09c62cf806751cb139b Mon Sep 17 00:00:00 2001 From: WillKolada <57021938+WillKolada@users.noreply.github.com> Date: Thu, 2 May 2024 01:42:08 -0500 Subject: [PATCH 51/71] Update IsDamageMoveUsable to check for Steel Roller viability (#4476) * Update IsDamageMoveUsable to check for Steel Roller viability * Condense terrain flag checks and renamed IsDamageMoveUsable IsDamageMoveUsable is now named IsDamageMoveUnusable to more accurately reflect the boolean it returns. --- src/battle_ai_util.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 40f3da2b5a..2dc9946e3d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -366,7 +366,7 @@ static inline s32 LowestRollDmg(s32 dmg) return dmg; } -bool32 IsDamageMoveUsable(u32 move, u32 battlerAtk, u32 battlerDef) +bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) { s32 moveType; struct AiLogicData *aiData = AI_DATA; @@ -438,6 +438,10 @@ bool32 IsDamageMoveUsable(u32 move, u32 battlerAtk, u32 battlerDef) if (!IS_BATTLER_OF_TYPE(battlerAtk, gMovesInfo[move].argument)) return TRUE; break; + case EFFECT_HIT_SET_REMOVE_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) + return TRUE; + break; } return FALSE; @@ -472,7 +476,7 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes GET_MOVE_TYPE(move, moveType); if (gMovesInfo[move].power) - isDamageMoveUnusable = IsDamageMoveUsable(move, battlerAtk, battlerDef); + isDamageMoveUnusable = IsDamageMoveUnusable(move, battlerAtk, battlerDef); effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE); if (gMovesInfo[move].power && !isDamageMoveUnusable) From 6d397f9867a9c899a1afe7ecad73ed0bdab57d26 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 2 May 2024 14:23:46 +0200 Subject: [PATCH 52/71] Fixes Magic Bounce in double battles (#4464) * Fixes Magic Bounce in double battles * Add Double Battle check --- include/battle.h | 4 +-- src/battle_main.c | 1 - src/battle_script_commands.c | 48 ++++++++++++++++++---------- src/battle_util.c | 5 +-- test/battle/ability/magic_bounce.c | 51 ++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 23 deletions(-) diff --git a/include/battle.h b/include/battle.h index b537be776e..dc771a5d77 100644 --- a/include/battle.h +++ b/include/battle.h @@ -157,7 +157,6 @@ struct ProtectStruct u32 flinchImmobility:1; u32 notFirstStrike:1; u32 palaceUnableToUseMove:1; - u32 usesBouncedMove:1; u32 usedHealBlockedMove:1; u32 usedGravityPreventedMove:1; u32 powderSelfDmg:1; @@ -736,9 +735,10 @@ struct BattleStruct u8 quickClawBattlerId; struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member) u8 forcedSwitch:4; // For each battler + u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects u8 blunderPolicy:1; // should blunder policy activate u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky - u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects + u8 bouncedMoveIsUsed:1; u8 ballSpriteIds[2]; // item gfx, window gfx u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle. diff --git a/src/battle_main.c b/src/battle_main.c index e45b43d099..e8f825676c 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3581,7 +3581,6 @@ const u8* FaintClearSetData(u32 battler) gProtectStructs[battler].flinchImmobility = FALSE; gProtectStructs[battler].notFirstStrike = FALSE; gProtectStructs[battler].usedHealBlockedMove = FALSE; - gProtectStructs[battler].usesBouncedMove = FALSE; gProtectStructs[battler].usedGravityPreventedMove = FALSE; gProtectStructs[battler].usedThroatChopPreventedMove = FALSE; gProtectStructs[battler].statRaised = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f07c560877..53b9cbb024 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1394,9 +1394,9 @@ static void Cmd_attackcanceler(void) if (gProtectStructs[gBattlerTarget].bounceMove && gMovesInfo[gCurrentMove].magicCoatAffected - && !gProtectStructs[gBattlerAttacker].usesBouncedMove) + && !gBattleStruct->bouncedMoveIsUsed) { - gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE; + gBattleStruct->bouncedMoveIsUsed = TRUE; gBattleCommunication[MULTISTRING_CHOOSER] = 0; // Edge case for bouncing a powder move against a grass type pokemon. SetAtkCancellerForCalledMove(); @@ -1413,18 +1413,33 @@ static void Cmd_attackcanceler(void) } return; } - else if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE - && gMovesInfo[gCurrentMove].magicCoatAffected - && !gProtectStructs[gBattlerAttacker].usesBouncedMove) + else if (gMovesInfo[gCurrentMove].magicCoatAffected && !gBattleStruct->bouncedMoveIsUsed) { - gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = 1; - // Edge case for bouncing a powder move against a grass type pokemon. - SetAtkCancellerForCalledMove(); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; - gBattlerAbility = gBattlerTarget; - return; + u32 battler = gBattlerTarget; + + if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE) + { + battler = gBattlerTarget; + gBattleStruct->bouncedMoveIsUsed = TRUE; + } + else if (IsDoubleBattle() + && gMovesInfo[gCurrentMove].target == MOVE_TARGET_OPPONENTS_FIELD + && GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) == ABILITY_MAGIC_BOUNCE) + { + gBattlerTarget = battler = BATTLE_PARTNER(gBattlerTarget); + gBattleStruct->bouncedMoveIsUsed = TRUE; + } + + if (gBattleStruct->bouncedMoveIsUsed) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + // Edge case for bouncing a powder move against a grass type pokemon. + SetAtkCancellerForCalledMove(); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; + gBattlerAbility = battler; + return; + } } // Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers) @@ -5920,9 +5935,10 @@ static void Cmd_moveend(void) return; } // Check if the move used was actually a bounced move. If so, we need to go back to the original attacker and make sure, its move hits all 2 or 3 pokemon. - else if (gProtectStructs[gBattlerAttacker].usesBouncedMove) + else if (gBattleStruct->bouncedMoveIsUsed) { u8 originalBounceTarget = gBattlerAttacker; + gBattleStruct->bouncedMoveIsUsed = FALSE; gBattlerAttacker = gBattleStruct->attackerBeforeBounce; gBattleStruct->targetsDone[gBattlerAttacker] |= gBitTable[originalBounceTarget]; gBattleStruct->targetsDone[originalBounceTarget] = 0; @@ -6221,7 +6237,7 @@ static void Cmd_moveend(void) if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker] || (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove - && gProtectStructs[gBattlerAttacker].usesBouncedMove))) + && gBattleStruct->bouncedMoveIsUsed))) { // Dance move succeeds // Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) @@ -6316,7 +6332,6 @@ static void Cmd_moveend(void) CancelMultiTurnMoves(gBattlerAttacker); // Cancel it gBattleStruct->targetsDone[gBattlerAttacker] = 0; - gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE; gProtectStructs[gBattlerAttacker].targetAffected = FALSE; gProtectStructs[gBattlerAttacker].shellTrap = FALSE; gBattleStruct->ateBoost[gBattlerAttacker] = 0; @@ -6333,6 +6348,7 @@ static void Cmd_moveend(void) gBattleStruct->hitSwitchTargetFailed = FALSE; gBattleStruct->isAtkCancelerForCalledMove = FALSE; gBattleStruct->swapDamageCategory = FALSE; + gBattleStruct->bouncedMoveIsUsed = FALSE; gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; gBattleScripting.moveendState++; diff --git a/src/battle_util.c b/src/battle_util.c index e4885c665b..ecdd8db266 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3489,10 +3489,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) if (effect != 0) gBattlescriptCurrInstr = BattleScript_PowderMoveNoEffect; } - if (gProtectStructs[gBattlerAttacker].usesBouncedMove) // Edge case for bouncing a powder move against a grass type pokemon. - gBattleStruct->atkCancellerTracker = CANCELLER_END; - else - gBattleStruct->atkCancellerTracker++; + gBattleStruct->atkCancellerTracker++; break; case CANCELLER_POWDER_STATUS: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER) diff --git a/test/battle/ability/magic_bounce.c b/test/battle/ability/magic_bounce.c index 95eaf382e6..8d583b154d 100644 --- a/test/battle/ability/magic_bounce.c +++ b/test/battle/ability/magic_bounce.c @@ -81,3 +81,54 @@ DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting both foes at two foe MESSAGE("Foe Wynaut's Defense fell!"); } } + +DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting foes field") +{ + u32 battlerOne, battlerTwo, abilityBattlerOne, abilityBattlerTwo; + + PARAMETRIZE { battlerOne = SPECIES_NATU; abilityBattlerOne = ABILITY_MAGIC_BOUNCE; + battlerTwo = SPECIES_ESPEON; abilityBattlerTwo = ABILITY_SYNCHRONIZE; } + PARAMETRIZE { battlerOne = SPECIES_NATU; abilityBattlerOne = ABILITY_KEEN_EYE; + battlerTwo = SPECIES_ESPEON; abilityBattlerTwo = ABILITY_MAGIC_BOUNCE; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].target == MOVE_TARGET_OPPONENTS_FIELD); + PLAYER(SPECIES_ABRA); + PLAYER(SPECIES_KADABRA); + OPPONENT(battlerOne) { Ability(abilityBattlerOne); } + OPPONENT(battlerTwo) { Ability(abilityBattlerTwo); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_STEALTH_ROCK); } + } SCENE { + if (abilityBattlerOne == ABILITY_MAGIC_BOUNCE) + ABILITY_POPUP(opponentLeft, ABILITY_MAGIC_BOUNCE); + else + ABILITY_POPUP(opponentRight, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, playerLeft); + if (abilityBattlerOne == ABILITY_MAGIC_BOUNCE) { + MESSAGE("Abra's Stealth Rock was bounced back by Foe Natu's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentLeft); + } else { + MESSAGE("Abra's Stealth Rock was bounced back by Foe Espeon's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentRight); + } + } +} + +SINGLE_BATTLE_TEST("Magic Bounce bounced back status moves can not be bounced back by Magic Bounce") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + PLAYER(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); } + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + MESSAGE("Espeon's Toxic was bounced back by Foe Espeon's Magic Bounce!"); + NOT ABILITY_POPUP(player, ABILITY_MAGIC_BOUNCE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, opponent); + STATUS_ICON(player, badPoison: TRUE); + } +} From a48d2c9ce230f5b5a39fd8adb50cba7f6bf8ca8b Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 2 May 2024 17:53:32 +0200 Subject: [PATCH 53/71] Set GiveBoxMonIntialMoveset_Fast as default (#4470) * Set GiveBoxMonIntialMoveset_Fast as default * Update pokemon.h removed duplicates --- include/pokemon.h | 2 -- src/debug.c | 2 +- src/pokemon.c | 25 +------------------------ 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/include/pokemon.h b/include/pokemon.h index 1f7003fe39..90450e0a13 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -658,8 +658,6 @@ void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot); void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot); void GiveMonInitialMoveset(struct Pokemon *mon); void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon); -void GiveMonInitialMoveset_Fast(struct Pokemon *mon); -void GiveBoxMonInitialMoveset_Fast(struct BoxPokemon *boxMon); u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove); void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move); void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move); diff --git a/src/debug.c b/src/debug.c index 76fd85fdcd..92972c289e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -4102,7 +4102,7 @@ static void DebugAction_PCBag_Fill_PCBoxes_Fast(u8 taskId) //Credit: Sierraffini StringCopy(speciesName, GetSpeciesName(species)); SetBoxMonData(&boxMon, MON_DATA_NICKNAME, &speciesName); SetBoxMonData(&boxMon, MON_DATA_SPECIES, &species); - GiveBoxMonInitialMoveset_Fast(&boxMon); + GiveBoxMonInitialMoveset(&boxMon); gPokemonStoragePtr->boxes[boxId][boxPosition] = boxMon; } } diff --git a/src/pokemon.c b/src/pokemon.c index be3feba37f..7c2b749f12 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1678,30 +1678,7 @@ void GiveMonInitialMoveset(struct Pokemon *mon) GiveBoxMonInitialMoveset(&mon->box); } -void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon) -{ - u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL); - s32 level = GetLevelFromBoxMonExp(boxMon); - s32 i; - const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species); - - for (i = 0; learnset[i].move != LEVEL_UP_MOVE_END; i++) - { - if (learnset[i].level > level) - break; - if (learnset[i].level == 0) - continue; - if (GiveMoveToBoxMon(boxMon, learnset[i].move) == MON_HAS_MAX_MOVES) - DeleteFirstMoveAndGiveMoveToBoxMon(boxMon, learnset[i].move); - } -} - -void GiveMonInitialMoveset_Fast(struct Pokemon *mon) -{ - GiveBoxMonInitialMoveset_Fast(&mon->box); -} - -void GiveBoxMonInitialMoveset_Fast(struct BoxPokemon *boxMon) //Credit: AsparagusEduardo +void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon) //Credit: AsparagusEduardo { u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL); s32 level = GetLevelFromBoxMonExp(boxMon); From 22f9eee5094f4bdf017cfdb06465ea4068690d17 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 2 May 2024 14:01:22 -0400 Subject: [PATCH 54/71] Added trainerproc to .gitignore --- .gitignore | 1 + tools/trainerproc/trainerproc | Bin 50320 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100755 tools/trainerproc/trainerproc diff --git a/.gitignore b/.gitignore index af9009dd01..ba80433cf7 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ prefabs.json *.sym *.js src/data/map_group_count.h +tools/trainerproc/trainerproc diff --git a/tools/trainerproc/trainerproc b/tools/trainerproc/trainerproc deleted file mode 100755 index 903e822d1bdea8722889b3a105eabd820962b08f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50320 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 From e791bc08e76f42f7b8da726b59ba22cad9cdfb6b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 2 May 2024 14:27:58 -0400 Subject: [PATCH 55/71] Fixed incoming master test messages --- test/battle/ability/color_change.c | 4 ++-- test/battle/ability/hospitality.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c index b7828bb978..86c009e8ac 100644 --- a/test/battle/ability/color_change.c +++ b/test/battle/ability/color_change.c @@ -73,7 +73,7 @@ SINGLE_BATTLE_TEST("Color Change changes the user to Electric type if hit by a m } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); - MESSAGE("Foe Wobbuffet's Color Change made it the Electr type!"); + MESSAGE("Foe Wobbuffet's Color Change made it the Electric type!"); } } @@ -90,7 +90,7 @@ SINGLE_BATTLE_TEST("Color Change changes the type when a Pokemon is hit by Futur ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); MESSAGE("Foe Snorlax took the Future Sight attack!"); ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); - MESSAGE("Foe Snorlax's Color Change made it the Psychc type!"); + MESSAGE("Foe Snorlax's Color Change made it the Psychic type!"); } } diff --git a/test/battle/ability/hospitality.c b/test/battle/ability/hospitality.c index 8f494ba4c6..308177d90f 100644 --- a/test/battle/ability/hospitality.c +++ b/test/battle/ability/hospitality.c @@ -85,7 +85,7 @@ DOUBLE_BATTLE_TEST("Hospitality does not trigger if there is no ally on the fiel MESSAGE("Wobbuffet fainted!"); HP_BAR(playerRight); MESSAGE("Wobbuffet fainted!"); - MESSAGE("Go! Ptchageist!"); + MESSAGE("Go! Poltchageist!"); NOT ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); } } From 9c46b83dbd41bf09d94fea8c67911336349ff9cd Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Fri, 3 May 2024 00:02:36 -0700 Subject: [PATCH 56/71] CanTerastallize Adjustments (#4440) * updated CanTerastallize to be disabled if B_FLAG_TERA_ORB_CHARGED undefined * added check for B_FLAG_TERA_ORB_NO_COST * reworked and tested CanTerastallize again * fixed infinite cost if NO_COST flag not assigned --- src/battle_terastal.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 8cc03aca57..ad83779682 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -27,8 +27,7 @@ void PrepareBattlerForTera(u32 battler) // Remove Tera Orb charge. if (B_FLAG_TERA_ORB_CHARGED != 0 - && B_FLAG_TERA_ORB_NO_COST != 0 - && !FlagGet(B_FLAG_TERA_ORB_NO_COST) + && (B_FLAG_TERA_ORB_NO_COST == 0 || !FlagGet(B_FLAG_TERA_ORB_NO_COST)) && side == B_SIDE_PLAYER && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !IsPartnerMonFromSameTrainer(battler))) { @@ -47,10 +46,10 @@ bool32 CanTerastallize(u32 battler) u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); // Check if Player has Tera Orb and has charge. - if (B_FLAG_TERA_ORB_CHARGED != 0 - && (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) - && !(CheckBagHasItem(ITEM_TERA_ORB, 1) - && FlagGet(B_FLAG_TERA_ORB_CHARGED))) + if (!CheckBagHasItem(ITEM_TERA_ORB, 1) + || !((B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST)) + || (B_FLAG_TERA_ORB_CHARGED != 0 && FlagGet(B_FLAG_TERA_ORB_CHARGED) + && ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)))))) { return FALSE; } From 0aad2a9ba7b358f559c50ecc07a8752400f1d64f Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Fri, 3 May 2024 12:32:06 -0500 Subject: [PATCH 57/71] Change Battle Frontier validation to species flag (#4341) * Split isLegendary into isRestrictedLegendary and isSubLegendary * Address reviews and fix build * Update frontier_util.c * Remove gFrontierBannedSpecies , fast edition * Sped up name generation * More speed up * Split Frontier restriction into its own flag * Mythicals shouldn't be banned always anymore * u32 --------- Co-authored-by: Eduardo Quezada --- include/frontier_util.h | 2 - include/pokemon.h | 7 +- src/battle_util.c | 2 +- .../pokemon/species_info/gen_1_families.h | 4 + .../pokemon/species_info/gen_2_families.h | 3 + .../pokemon/species_info/gen_3_families.h | 11 +++ .../pokemon/species_info/gen_4_families.h | 12 +++ .../pokemon/species_info/gen_5_families.h | 11 +++ .../pokemon/species_info/gen_6_families.h | 13 +++ .../pokemon/species_info/gen_7_families.h | 15 ++++ .../pokemon/species_info/gen_8_families.h | 11 +++ .../pokemon/species_info/gen_9_families.h | 46 +++++----- src/frontier_util.c | 85 ++++++++----------- src/party_menu.c | 10 +-- test/battle/ability/booster_energy.c | 4 +- 15 files changed, 152 insertions(+), 84 deletions(-) diff --git a/include/frontier_util.h b/include/frontier_util.h index f445949ef9..637b7f91bb 100644 --- a/include/frontier_util.h +++ b/include/frontier_util.h @@ -25,6 +25,4 @@ u8 GetFrontierBrainMonNature(u8 monId); u8 GetFrontierBrainMonEvs(u8 monId, u8 evStatId); s32 GetFronterBrainSymbol(void); -extern const u16 gFrontierBannedSpecies[]; - #endif // GUARD_FRONTIER_UTIL_H diff --git a/include/pokemon.h b/include/pokemon.h index 90450e0a13..1ff7cfaf44 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -425,8 +425,8 @@ struct SpeciesInfo /*0x8C*/ /* 0x7A */ u32 isLegendary:1; u32 isMythical:1; u32 isUltraBeast:1; + u32 isParadox:1; u32 isTotem:1; - u32 isParadoxForm:1; u32 isMegaEvolution:1; u32 isPrimalReversion:1; u32 isUltraBurst:1; @@ -438,8 +438,9 @@ struct SpeciesInfo /*0x8C*/ u32 cannotBeTraded:1; u32 allPerfectIVs:1; u32 dexForceRequired:1; // This species will be taken into account for Pokédex ratings even if they have the "isMythical" flag set. - u32 tmIlliterate:1; // This species will be unable to learn the universal moves. - u32 padding4:15; + u32 tmIlliterate:1; // This species will be unable to learn the universal moves. + u32 isFrontierBanned:1; // This species is not allowed to participate in Battle Frontier facilities. + u32 padding4:14; // Move Data /* 0x80 */ const struct LevelUpMove *levelUpLearnset; /* 0x84 */ const u16 *teachableLearnset; diff --git a/src/battle_util.c b/src/battle_util.c index a0b5435bc9..7446c05a23 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10731,7 +10731,7 @@ bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId) else if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) return FALSE; else if (holdEffect == HOLD_EFFECT_BOOSTER_ENERGY - && (gSpeciesInfo[gBattleMons[gBattlerAttacker].species].isParadoxForm || gSpeciesInfo[gBattleMons[gBattlerTarget].species].isParadoxForm)) + && (gSpeciesInfo[gBattleMons[gBattlerAttacker].species].isParadox || gSpeciesInfo[gBattleMons[gBattlerTarget].species].isParadox)) return FALSE; else return TRUE; diff --git a/src/data/pokemon/species_info/gen_1_families.h b/src/data/pokemon/species_info/gen_1_families.h index e8de1682c5..10910bf85a 100644 --- a/src/data/pokemon/species_info/gen_1_families.h +++ b/src/data/pokemon/species_info/gen_1_families.h @@ -15461,6 +15461,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .iconPalIndex = 2, FOOTPRINT(Mewtwo) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMewtwoLevelUpLearnset, .teachableLearnset = sMewtwoTeachableLearnset, .formSpeciesIdTable = sMewtwoFormSpeciesIdTable, @@ -15518,6 +15519,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = FOOTPRINT(Mewtwo) .isLegendary = TRUE, .isMegaEvolution = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMewtwoLevelUpLearnset, .teachableLearnset = sMewtwoTeachableLearnset, .formSpeciesIdTable = sMewtwoFormSpeciesIdTable, @@ -15575,6 +15577,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = FOOTPRINT(Mewtwo) .isLegendary = TRUE, .isMegaEvolution = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMewtwoLevelUpLearnset, .teachableLearnset = sMewtwoTeachableLearnset, .formSpeciesIdTable = sMewtwoFormSpeciesIdTable, @@ -15642,6 +15645,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .iconPalIndex = 0, FOOTPRINT(Mew) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMewLevelUpLearnset, .teachableLearnset = sMewTeachableLearnset, }, diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h index fcc1e18cd7..16f6a43f7d 100644 --- a/src/data/pokemon/species_info/gen_2_families.h +++ b/src/data/pokemon/species_info/gen_2_families.h @@ -6325,6 +6325,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .iconPalIndex = 0, FOOTPRINT(Lugia) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sLugiaLevelUpLearnset, .teachableLearnset = sLugiaTeachableLearnset, }, @@ -6389,6 +6390,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .iconPalIndex = 1, FOOTPRINT(HoOh) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sHoOhLevelUpLearnset, .teachableLearnset = sHoOhTeachableLearnset, }, @@ -6453,6 +6455,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .iconPalIndex = 1, FOOTPRINT(Celebi) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sCelebiLevelUpLearnset, .teachableLearnset = sCelebiTeachableLearnset, }, diff --git a/src/data/pokemon/species_info/gen_3_families.h b/src/data/pokemon/species_info/gen_3_families.h index fb34fdc4af..af37135d77 100644 --- a/src/data/pokemon/species_info/gen_3_families.h +++ b/src/data/pokemon/species_info/gen_3_families.h @@ -9340,6 +9340,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 2, FOOTPRINT(Kyogre) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKyogreLevelUpLearnset, .teachableLearnset = sKyogreTeachableLearnset, .formSpeciesIdTable = sKyogreFormSpeciesIdTable, @@ -9396,6 +9397,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = FOOTPRINT(Kyogre) .isLegendary = TRUE, .isPrimalReversion = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKyogreLevelUpLearnset, .teachableLearnset = sKyogreTeachableLearnset, .formSpeciesIdTable = sKyogreFormSpeciesIdTable, @@ -9460,6 +9462,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 0, FOOTPRINT(Groudon) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sGroudonLevelUpLearnset, .teachableLearnset = sGroudonTeachableLearnset, .formSpeciesIdTable = sGroudonFormSpeciesIdTable, @@ -9517,6 +9520,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = FOOTPRINT(Groudon) .isLegendary = TRUE, .isPrimalReversion = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sGroudonLevelUpLearnset, .teachableLearnset = sGroudonTeachableLearnset, .formSpeciesIdTable = sGroudonFormSpeciesIdTable, @@ -9584,6 +9588,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 1, FOOTPRINT(Rayquaza) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sRayquazaLevelUpLearnset, .teachableLearnset = sRayquazaTeachableLearnset, .formSpeciesIdTable = sRayquazaFormSpeciesIdTable, @@ -9643,6 +9648,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = FOOTPRINT(Rayquaza) .isLegendary = TRUE, .isMegaEvolution = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sRayquazaLevelUpLearnset, .teachableLearnset = sRayquazaTeachableLearnset, .formSpeciesIdTable = sRayquazaFormSpeciesIdTable, @@ -9710,6 +9716,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 0, FOOTPRINT(Jirachi) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sJirachiLevelUpLearnset, .teachableLearnset = sJirachiTeachableLearnset, }, @@ -9771,6 +9778,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 0, FOOTPRINT(Deoxys) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDeoxysNormalLevelUpLearnset, .teachableLearnset = sDeoxysNormalTeachableLearnset, .formSpeciesIdTable = sDeoxysFormSpeciesIdTable, @@ -9823,6 +9831,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 0, FOOTPRINT(Deoxys) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDeoxysAttackLevelUpLearnset, .teachableLearnset = sDeoxysAttackTeachableLearnset, .formSpeciesIdTable = sDeoxysFormSpeciesIdTable, @@ -9875,6 +9884,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 0, FOOTPRINT(Deoxys) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDeoxysDefenseLevelUpLearnset, .teachableLearnset = sDeoxysDefenseTeachableLearnset, .formSpeciesIdTable = sDeoxysFormSpeciesIdTable, @@ -9927,6 +9937,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .iconPalIndex = 0, FOOTPRINT(Deoxys) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDeoxysSpeedLevelUpLearnset, .teachableLearnset = sDeoxysSpeedTeachableLearnset, .formSpeciesIdTable = sDeoxysFormSpeciesIdTable, diff --git a/src/data/pokemon/species_info/gen_4_families.h b/src/data/pokemon/species_info/gen_4_families.h index d388eb13ee..b8f54203f2 100644 --- a/src/data/pokemon/species_info/gen_4_families.h +++ b/src/data/pokemon/species_info/gen_4_families.h @@ -4786,6 +4786,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 2, FOOTPRINT(Dialga) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDialgaLevelUpLearnset, .teachableLearnset = sDialgaTeachableLearnset, .formSpeciesIdTable = sDialgaFormSpeciesIdTable, @@ -4841,6 +4842,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 0, FOOTPRINT(Dialga) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDialgaLevelUpLearnset, .teachableLearnset = sDialgaTeachableLearnset, .formSpeciesIdTable = sDialgaFormSpeciesIdTable, @@ -4906,6 +4908,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 2, FOOTPRINT(Palkia) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sPalkiaLevelUpLearnset, .teachableLearnset = sPalkiaTeachableLearnset, .formSpeciesIdTable = sPalkiaFormSpeciesIdTable, @@ -4961,6 +4964,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 2, FOOTPRINT(Palkia) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sPalkiaLevelUpLearnset, .teachableLearnset = sPalkiaTeachableLearnset, .formSpeciesIdTable = sPalkiaFormSpeciesIdTable, @@ -5148,6 +5152,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 0, FOOTPRINT(Giratina) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sGiratinaLevelUpLearnset, .teachableLearnset = sGiratinaTeachableLearnset, .formSpeciesIdTable = sGiratinaFormSpeciesIdTable, @@ -5204,6 +5209,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 0, FOOTPRINT(Giratina) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sGiratinaLevelUpLearnset, .teachableLearnset = sGiratinaTeachableLearnset, .formSpeciesIdTable = sGiratinaFormSpeciesIdTable, @@ -5330,6 +5336,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 0, FOOTPRINT(Phione) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sPhioneLevelUpLearnset, .teachableLearnset = sPhioneTeachableLearnset, }, @@ -5390,6 +5397,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 0, FOOTPRINT(Manaphy) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sManaphyLevelUpLearnset, .teachableLearnset = sManaphyTeachableLearnset, }, @@ -5453,6 +5461,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 0, FOOTPRINT(Darkrai) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDarkraiLevelUpLearnset, .teachableLearnset = sDarkraiTeachableLearnset, }, @@ -5518,6 +5527,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 1, FOOTPRINT(Shaymin) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sShayminLandLevelUpLearnset, .teachableLearnset = sShayminLandTeachableLearnset, .formSpeciesIdTable = sShayminFormSpeciesIdTable, @@ -5580,6 +5590,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .iconPalIndex = 1, FOOTPRINT(Shaymin) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sShayminSkyLevelUpLearnset, .teachableLearnset = sShayminSkyTeachableLearnset, .formSpeciesIdTable = sShayminFormSpeciesIdTable, @@ -5653,6 +5664,7 @@ const struct SpeciesInfo gSpeciesInfoGen4[] = .formSpeciesIdTable = sArceusFormSpeciesIdTable, \ .formChangeTable = sArceusFormChangeTable, \ .isMythical = TRUE, \ + .isFrontierBanned = TRUE, \ } [SPECIES_ARCEUS_NORMAL] = ARCEUS_SPECIES_INFO(TYPE_NORMAL, Normal, 1), diff --git a/src/data/pokemon/species_info/gen_5_families.h b/src/data/pokemon/species_info/gen_5_families.h index 9f7200849b..2d4a0abeb0 100644 --- a/src/data/pokemon/species_info/gen_5_families.h +++ b/src/data/pokemon/species_info/gen_5_families.h @@ -53,6 +53,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 0, FOOTPRINT(Victini) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sVictiniLevelUpLearnset, .teachableLearnset = sVictiniTeachableLearnset, }, @@ -9747,6 +9748,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 0, FOOTPRINT(Reshiram) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sReshiramLevelUpLearnset, .teachableLearnset = sReshiramTeachableLearnset, }, @@ -9802,6 +9804,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 2, FOOTPRINT(Zekrom) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZekromLevelUpLearnset, .teachableLearnset = sZekromTeachableLearnset, }, @@ -9973,6 +9976,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 0, FOOTPRINT(Kyurem) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKyuremLevelUpLearnset, .teachableLearnset = sKyuremTeachableLearnset, .formSpeciesIdTable = sKyuremFormSpeciesIdTable, @@ -10036,6 +10040,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = FOOTPRINT(Kyurem) .isLegendary = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKyuremWhiteLevelUpLearnset, .teachableLearnset = sKyuremTeachableLearnset, .formSpeciesIdTable = sKyuremFormSpeciesIdTable, @@ -10098,6 +10103,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = FOOTPRINT(Kyurem) .isLegendary = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKyuremBlackLevelUpLearnset, .teachableLearnset = sKyuremTeachableLearnset, .formSpeciesIdTable = sKyuremFormSpeciesIdTable, @@ -10155,6 +10161,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 0, FOOTPRINT(Keldeo) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKeldeoLevelUpLearnset, .teachableLearnset = sKeldeoTeachableLearnset, .formSpeciesIdTable = sKeldeoFormSpeciesIdTable, @@ -10211,6 +10218,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 0, FOOTPRINT(Keldeo) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKeldeoLevelUpLearnset, .teachableLearnset = sKeldeoTeachableLearnset, .formSpeciesIdTable = sKeldeoFormSpeciesIdTable, @@ -10274,6 +10282,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 4, FOOTPRINT(Meloetta) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMeloettaLevelUpLearnset, .teachableLearnset = sMeloettaTeachableLearnset, .formSpeciesIdTable = sMeloettaFormSpeciesIdTable, @@ -10334,6 +10343,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .iconPalIndex = 0, FOOTPRINT(Meloetta) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMeloettaLevelUpLearnset, .teachableLearnset = sMeloettaTeachableLearnset, .formSpeciesIdTable = sMeloettaFormSpeciesIdTable, @@ -10393,6 +10403,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .formSpeciesIdTable = sGenesectFormSpeciesIdTable, \ .formChangeTable = sGenesectFormChangeTable, \ .isMythical = TRUE, \ + .isFrontierBanned = TRUE, \ } [SPECIES_GENESECT] = GENESECT_SPECIES_INFO(Genesect), diff --git a/src/data/pokemon/species_info/gen_6_families.h b/src/data/pokemon/species_info/gen_6_families.h index d49628cb0c..6cd3196bc5 100644 --- a/src/data/pokemon/species_info/gen_6_families.h +++ b/src/data/pokemon/species_info/gen_6_families.h @@ -4677,6 +4677,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 0, FOOTPRINT(Xerneas) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sXerneasLevelUpLearnset, .teachableLearnset = sXerneasTeachableLearnset, .formSpeciesIdTable = sXerneasFormSpeciesIdTable, @@ -4728,6 +4729,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 0, FOOTPRINT(Xerneas) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sXerneasLevelUpLearnset, .teachableLearnset = sXerneasTeachableLearnset, .formSpeciesIdTable = sXerneasFormSpeciesIdTable, @@ -4786,6 +4788,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 0, FOOTPRINT(Yveltal) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sYveltalLevelUpLearnset, .teachableLearnset = sYveltalTeachableLearnset, }, @@ -4838,6 +4841,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 1, FOOTPRINT(Zygarde) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZygardeLevelUpLearnset, .teachableLearnset = sZygardeTeachableLearnset, .formSpeciesIdTable = sZygardeFormSpeciesIdTable, @@ -4889,6 +4893,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 1, FOOTPRINT(Zygarde) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZygardeLevelUpLearnset, .teachableLearnset = sZygardeTeachableLearnset, .formSpeciesIdTable = sZygardeFormSpeciesIdTable, @@ -4940,6 +4945,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 1, FOOTPRINT(Zygarde) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZygardeLevelUpLearnset, .teachableLearnset = sZygardeTeachableLearnset, .formSpeciesIdTable = sZygardeFormSpeciesIdTable, @@ -4992,6 +4998,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 1, FOOTPRINT(Zygarde) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZygardeLevelUpLearnset, .teachableLearnset = sZygardeTeachableLearnset, .formSpeciesIdTable = sZygardeFormSpeciesIdTable, @@ -5048,6 +5055,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 1, FOOTPRINT(Zygarde) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZygardeLevelUpLearnset, .teachableLearnset = sZygardeTeachableLearnset, .formSpeciesIdTable = sZygardeFormSpeciesIdTable, @@ -5106,6 +5114,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 1, FOOTPRINT(Diancie) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDiancieLevelUpLearnset, .teachableLearnset = sDiancieTeachableLearnset, .formSpeciesIdTable = sDiancieFormSpeciesIdTable, @@ -5164,6 +5173,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = FOOTPRINT(Diancie) .isMythical = TRUE, .isMegaEvolution = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sDiancieLevelUpLearnset, .teachableLearnset = sDiancieTeachableLearnset, .formSpeciesIdTable = sDiancieFormSpeciesIdTable, @@ -5223,6 +5233,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 0, FOOTPRINT(Hoopa) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sHoopaConfinedLevelUpLearnset, .teachableLearnset = sHoopaConfinedTeachableLearnset, .formSpeciesIdTable = sHoopaFormSpeciesIdTable, @@ -5279,6 +5290,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 0, FOOTPRINT(Hoopa) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sHoopaUnboundLevelUpLearnset, .teachableLearnset = sHoopaUnboundTeachableLearnset, .formSpeciesIdTable = sHoopaFormSpeciesIdTable, @@ -5336,6 +5348,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .iconPalIndex = 0, FOOTPRINT(Volcanion) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sVolcanionLevelUpLearnset, .teachableLearnset = sVolcanionTeachableLearnset, }, diff --git a/src/data/pokemon/species_info/gen_7_families.h b/src/data/pokemon/species_info/gen_7_families.h index 6c4632f0ed..bde6e26838 100644 --- a/src/data/pokemon/species_info/gen_7_families.h +++ b/src/data/pokemon/species_info/gen_7_families.h @@ -4754,6 +4754,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = FOOTPRINT(Cosmog) .isLegendary = TRUE, .tmIlliterate = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sCosmogLevelUpLearnset, .teachableLearnset = sCosmogTeachableLearnset, .evolutions = EVOLUTION({EVO_LEVEL, 43, SPECIES_COSMOEM}), @@ -4811,6 +4812,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = FOOTPRINT(Cosmoem) .isLegendary = TRUE, .tmIlliterate = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sCosmoemLevelUpLearnset, .teachableLearnset = sCosmoemTeachableLearnset, .evolutions = EVOLUTION({EVO_LEVEL_DAY, 53, SPECIES_SOLGALEO}, @@ -4866,6 +4868,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 0, FOOTPRINT(Solgaleo) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sSolgaleoLevelUpLearnset, .teachableLearnset = sSolgaleoTeachableLearnset, }, @@ -4920,6 +4923,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 2, FOOTPRINT(Lunala) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sLunalaLevelUpLearnset, .teachableLearnset = sLunalaTeachableLearnset, }, @@ -5368,6 +5372,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 0, FOOTPRINT(Necrozma) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sNecrozmaLevelUpLearnset, .teachableLearnset = sNecrozmaTeachableLearnset, .formSpeciesIdTable = sNecrozmaFormSpeciesIdTable, @@ -5425,6 +5430,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = FOOTPRINT(Necrozma) .isLegendary = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sNecrozmaLevelUpLearnset, .teachableLearnset = sNecrozmaTeachableLearnset, .formSpeciesIdTable = sNecrozmaFormSpeciesIdTable, @@ -5483,6 +5489,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = FOOTPRINT(Necrozma) .isLegendary = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sNecrozmaLevelUpLearnset, .teachableLearnset = sNecrozmaTeachableLearnset, .formSpeciesIdTable = sNecrozmaFormSpeciesIdTable, @@ -5545,6 +5552,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .isLegendary = TRUE, .isUltraBurst = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sNecrozmaLevelUpLearnset, .teachableLearnset = sNecrozmaTeachableLearnset, .formSpeciesIdTable = sNecrozmaFormSpeciesIdTable, @@ -5603,6 +5611,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 0, FOOTPRINT(Magearna) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMagearnaLevelUpLearnset, .teachableLearnset = sMagearnaTeachableLearnset, .formSpeciesIdTable = sMagearnaFormSpeciesIdTable, @@ -5656,6 +5665,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 0, FOOTPRINT(Magearna) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMagearnaLevelUpLearnset, .teachableLearnset = sMagearnaTeachableLearnset, .formSpeciesIdTable = sMagearnaFormSpeciesIdTable, @@ -5714,6 +5724,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 0, FOOTPRINT(Marshadow) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMarshadowLevelUpLearnset, .teachableLearnset = sMarshadowTeachableLearnset, }, @@ -5987,6 +5998,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 0, FOOTPRINT(Zeraora) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZeraoraLevelUpLearnset, .teachableLearnset = sZeraoraTeachableLearnset, }, @@ -6041,6 +6053,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 2, FOOTPRINT(Meltan) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMeltanLevelUpLearnset, .teachableLearnset = sMeltanTeachableLearnset, }, @@ -6093,6 +6106,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .iconPalIndex = 2, FOOTPRINT(Melmetal) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMelmetalLevelUpLearnset, .teachableLearnset = sMelmetalTeachableLearnset, .formSpeciesIdTable = sMelmetalFormSpeciesIdTable, @@ -6150,6 +6164,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = FOOTPRINT(Melmetal) .isMythical = TRUE, .isGigantamax = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMelmetalLevelUpLearnset, .teachableLearnset = sMelmetalTeachableLearnset, .formSpeciesIdTable = sMelmetalFormSpeciesIdTable, diff --git a/src/data/pokemon/species_info/gen_8_families.h b/src/data/pokemon/species_info/gen_8_families.h index 65c5659f7b..d36066bf58 100644 --- a/src/data/pokemon/species_info/gen_8_families.h +++ b/src/data/pokemon/species_info/gen_8_families.h @@ -5609,6 +5609,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 2, FOOTPRINT(Zacian) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZacianLevelUpLearnset, .teachableLearnset = sZacianTeachableLearnset, .formSpeciesIdTable = sZacianFormSpeciesIdTable, @@ -5664,6 +5665,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 2, FOOTPRINT(Zacian) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZacianLevelUpLearnset, .teachableLearnset = sZacianTeachableLearnset, .formSpeciesIdTable = sZacianFormSpeciesIdTable, @@ -5721,6 +5723,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 2, FOOTPRINT(Zamazenta) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZamazentaLevelUpLearnset, .teachableLearnset = sZamazentaTeachableLearnset, .formSpeciesIdTable = sZamazentaFormSpeciesIdTable, @@ -5776,6 +5779,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 2, FOOTPRINT(Zamazenta) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZamazentaLevelUpLearnset, .teachableLearnset = sZamazentaTeachableLearnset, .formSpeciesIdTable = sZamazentaFormSpeciesIdTable, @@ -5834,6 +5838,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 0, FOOTPRINT(Eternatus) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sEternatusLevelUpLearnset, .teachableLearnset = sEternatusTeachableLearnset, .formSpeciesIdTable = sEternatusFormSpeciesIdTable, @@ -5889,6 +5894,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 0, FOOTPRINT(Eternatus) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sEternatusLevelUpLearnset, .teachableLearnset = sEternatusTeachableLearnset, .formSpeciesIdTable = sEternatusFormSpeciesIdTable, @@ -6228,6 +6234,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 1, FOOTPRINT(Zarude) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZarudeLevelUpLearnset, .teachableLearnset = sZarudeTeachableLearnset, .formSpeciesIdTable = sZarudeFormSpeciesIdTable, @@ -6281,6 +6288,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 1, FOOTPRINT(Zarude) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sZarudeLevelUpLearnset, .teachableLearnset = sZarudeTeachableLearnset, .formSpeciesIdTable = sZarudeFormSpeciesIdTable, @@ -6558,6 +6566,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .iconPalIndex = 0, FOOTPRINT(Calyrex) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sCalyrexLevelUpLearnset, .teachableLearnset = sCalyrexTeachableLearnset, .formSpeciesIdTable = sCalyrexFormSpeciesIdTable, @@ -6614,6 +6623,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = FOOTPRINT(Calyrex) .isLegendary = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sCalyrexIceRiderLevelUpLearnset, .teachableLearnset = sCalyrexIceRiderTeachableLearnset, .formSpeciesIdTable = sCalyrexFormSpeciesIdTable, @@ -6669,6 +6679,7 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = FOOTPRINT(Calyrex) .isLegendary = TRUE, .cannotBeTraded = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sCalyrexShadowRiderLevelUpLearnset, .teachableLearnset = sCalyrexShadowRiderTeachableLearnset, .formSpeciesIdTable = sCalyrexFormSpeciesIdTable, diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index d272120cf9..33097c3ef6 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -4390,7 +4390,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_GreatTusk, .iconPalIndex = 0, //FOOTPRINT(GreatTusk) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sGreatTuskLevelUpLearnset, .teachableLearnset = sGreatTuskTeachableLearnset, }, @@ -4445,7 +4445,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_ScreamTail, .iconPalIndex = 0, //FOOTPRINT(ScreamTail) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sScreamTailLevelUpLearnset, .teachableLearnset = sScreamTailTeachableLearnset, }, @@ -4500,7 +4500,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_BruteBonnet, .iconPalIndex = 1, //FOOTPRINT(BruteBonnet) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sBruteBonnetLevelUpLearnset, .teachableLearnset = sBruteBonnetTeachableLearnset, }, @@ -4558,7 +4558,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_FlutterMane, .iconPalIndex = 2, //FOOTPRINT(FlutterMane) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sFlutterManeLevelUpLearnset, .teachableLearnset = sFlutterManeTeachableLearnset, }, @@ -4612,7 +4612,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_SlitherWing, .iconPalIndex = 1, //FOOTPRINT(SlitherWing) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sSlitherWingLevelUpLearnset, .teachableLearnset = sSlitherWingTeachableLearnset, }, @@ -4667,7 +4667,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_SandyShocks, .iconPalIndex = 0, //FOOTPRINT(SandyShocks) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sSandyShocksLevelUpLearnset, .teachableLearnset = sSandyShocksTeachableLearnset, }, @@ -4722,7 +4722,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronTreads, .iconPalIndex = 1, //FOOTPRINT(IronTreads) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronTreadsLevelUpLearnset, .teachableLearnset = sIronTreadsTeachableLearnset, }, @@ -4777,7 +4777,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronBundle, .iconPalIndex = 0, //FOOTPRINT(IronBundle) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronBundleLevelUpLearnset, .teachableLearnset = sIronBundleTeachableLearnset, }, @@ -4832,7 +4832,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronHands, .iconPalIndex = 0, //FOOTPRINT(IronHands) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronHandsLevelUpLearnset, .teachableLearnset = sIronHandsTeachableLearnset, }, @@ -4888,7 +4888,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronJugulis, .iconPalIndex = 0, //FOOTPRINT(IronJugulis) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronJugulisLevelUpLearnset, .teachableLearnset = sIronJugulisTeachableLearnset, }, @@ -4944,7 +4944,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronMoth, .iconPalIndex = 3, //FOOTPRINT(IronMoth) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronMothLevelUpLearnset, .teachableLearnset = sIronMothTeachableLearnset, }, @@ -4999,7 +4999,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronThorns, .iconPalIndex = 1, //FOOTPRINT(IronThorns) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronThornsLevelUpLearnset, .teachableLearnset = sIronThornsTeachableLearnset, }, @@ -5598,7 +5598,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_RoaringMoon, .iconPalIndex = 0, //FOOTPRINT(RoaringMoon) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sRoaringMoonLevelUpLearnset, .teachableLearnset = sRoaringMoonTeachableLearnset, }, @@ -5652,7 +5652,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronValiant, .iconPalIndex = 1, //FOOTPRINT(IronValiant) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronValiantLevelUpLearnset, .teachableLearnset = sIronValiantTeachableLearnset, }, @@ -5708,6 +5708,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 0, //FOOTPRINT(Koraidon) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sKoraidonLevelUpLearnset, .teachableLearnset = sKoraidonTeachableLearnset, }, @@ -5763,6 +5764,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 2, //FOOTPRINT(Miraidon) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sMiraidonLevelUpLearnset, .teachableLearnset = sMiraidonTeachableLearnset, }, @@ -5816,7 +5818,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_WalkingWake, .iconPalIndex = 2, //FOOTPRINT(WalkingWake) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sWalkingWakeLevelUpLearnset, .teachableLearnset = sWalkingWakeTeachableLearnset, }, @@ -5870,7 +5872,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronLeaves, .iconPalIndex = 1, //FOOTPRINT(IronLeaves) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronLeavesLevelUpLearnset, .teachableLearnset = sIronLeavesTeachableLearnset, }, @@ -6369,7 +6371,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_GougingFire, .iconPalIndex = 5, //FOOTPRINT(GougingFire) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sGougingFireLevelUpLearnset, .teachableLearnset = sGougingFireTeachableLearnset, }, @@ -6424,7 +6426,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_RagingBolt, .iconPalIndex = 2, //FOOTPRINT(RagingBolt) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sRagingBoltLevelUpLearnset, .teachableLearnset = sRagingBoltTeachableLearnset, }, @@ -6478,7 +6480,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronBoulder, .iconPalIndex = 5, //FOOTPRINT(IronBoulder) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronBoulderLevelUpLearnset, .teachableLearnset = sIronBoulderTeachableLearnset, }, @@ -6533,7 +6535,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconSprite = gMonIcon_IronCrown, .iconPalIndex = 3, //FOOTPRINT(IronCrown) - .isParadoxForm = TRUE, + .isParadox = TRUE, .levelUpLearnset = sIronCrownLevelUpLearnset, .teachableLearnset = sIronCrownTeachableLearnset, }, @@ -6589,6 +6591,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 0, //FOOTPRINT(Terapagos) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sTerapagosLevelUpLearnset, .teachableLearnset = sTerapagosTeachableLearnset, .formSpeciesIdTable = sTerapagosFormSpeciesIdTable, @@ -6645,6 +6648,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 0, //FOOTPRINT(Terapagos) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sTerapagosLevelUpLearnset, .teachableLearnset = sTerapagosTeachableLearnset, .formSpeciesIdTable = sTerapagosFormSpeciesIdTable, @@ -6700,6 +6704,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 0, //FOOTPRINT(Terapagos) .isLegendary = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sTerapagosLevelUpLearnset, .teachableLearnset = sTerapagosTeachableLearnset, .formSpeciesIdTable = sTerapagosFormSpeciesIdTable, @@ -6757,6 +6762,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 0, //FOOTPRINT(Pecharunt) .isMythical = TRUE, + .isFrontierBanned = TRUE, .levelUpLearnset = sPecharuntLevelUpLearnset, .teachableLearnset = sPecharuntTeachableLearnset, }, diff --git a/src/frontier_util.c b/src/frontier_util.c index 7f9b37b957..ca8c76a0fd 100644 --- a/src/frontier_util.c +++ b/src/frontier_util.c @@ -623,20 +623,6 @@ static const u8 sFrontierBrainObjEventGfx[NUM_FRONTIER_FACILITIES][2] = [FRONTIER_FACILITY_PYRAMID] = {OBJ_EVENT_GFX_BRANDON, FALSE}, }; -const u16 gFrontierBannedSpecies[] = -{ - SPECIES_MEW, SPECIES_MEWTWO, - SPECIES_HO_OH, SPECIES_LUGIA, SPECIES_CELEBI, - SPECIES_KYOGRE, SPECIES_GROUDON, SPECIES_RAYQUAZA, SPECIES_JIRACHI, SPECIES_DEOXYS, - SPECIES_DIALGA, SPECIES_PALKIA, SPECIES_GIRATINA, SPECIES_MANAPHY, SPECIES_PHIONE, SPECIES_DARKRAI, SPECIES_SHAYMIN, SPECIES_ARCEUS, - SPECIES_VICTINI, SPECIES_RESHIRAM, SPECIES_ZEKROM, SPECIES_KYUREM, SPECIES_KELDEO, SPECIES_MELOETTA, SPECIES_GENESECT, - SPECIES_XERNEAS, SPECIES_YVELTAL, SPECIES_ZYGARDE, SPECIES_DIANCIE, SPECIES_HOOPA, SPECIES_VOLCANION, - SPECIES_COSMOG, SPECIES_COSMOEM, SPECIES_SOLGALEO, SPECIES_LUNALA, SPECIES_NECROZMA, SPECIES_MAGEARNA, SPECIES_MARSHADOW, SPECIES_ZERAORA, SPECIES_MELTAN, SPECIES_MELMETAL, - SPECIES_ZACIAN, SPECIES_ZAMAZENTA, SPECIES_ETERNATUS, SPECIES_CALYREX, SPECIES_ZARUDE, - SPECIES_KORAIDON, SPECIES_MIRAIDON, - 0xFFFF -}; - static const u8 *const sRecordsWindowChallengeTexts[][2] = { [RANKING_HALL_TOWER_SINGLES] = {gText_BattleTower2, gText_FacilitySingle}, @@ -1899,26 +1885,20 @@ static void CheckBattleTypeFlag(void) #define SPECIES_PER_LINE 3 -static u8 AppendCaughtBannedMonSpeciesName(u16 species, u8 count, s32 numBannedMonsCaught) +static void AppendCaughtBannedMonSpeciesName(u16 species, u8 count, s32 numBannedMonsCaught) { - if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + if (numBannedMonsCaught == count) + StringAppend(gStringVar1, gText_SpaceAndSpace); + else if (numBannedMonsCaught > count) + StringAppend(gStringVar1, gText_CommaSpace); + if ((count % SPECIES_PER_LINE) == 0) { - count++; - if (numBannedMonsCaught == count) - StringAppend(gStringVar1, gText_SpaceAndSpace); - else if (numBannedMonsCaught > count) - StringAppend(gStringVar1, gText_CommaSpace); - if ((count % SPECIES_PER_LINE) == 0) - { - if (count == SPECIES_PER_LINE) - StringAppend(gStringVar1, gText_NewLine); - else - StringAppend(gStringVar1, gText_LineBreak); - } - StringAppend(gStringVar1, GetSpeciesName(species)); + if (count == SPECIES_PER_LINE) + StringAppend(gStringVar1, gText_NewLine); + else + StringAppend(gStringVar1, gText_LineBreak); } - - return count; + StringAppend(gStringVar1, GetSpeciesName(species)); } static void AppendIfValid(u16 species, u16 heldItem, u16 hp, u8 lvlMode, u8 monLevel, u16 *speciesArray, u16 *itemsArray, u8 *count) @@ -1927,13 +1907,7 @@ static void AppendIfValid(u16 species, u16 heldItem, u16 hp, u8 lvlMode, u8 monL if (species == SPECIES_EGG || species == SPECIES_NONE) return; - - for (i = 0; gFrontierBannedSpecies[i] != 0xFFFF - && gFrontierBannedSpecies[i] != GET_BASE_SPECIES_ID(species) - && IsSpeciesEnabled(gFrontierBannedSpecies[i]); i++) - ; - - if (gFrontierBannedSpecies[i] != 0xFFFF) + if (gSpeciesInfo[species].isFrontierBanned) return; if (lvlMode == FRONTIER_LVL_50 && monLevel > FRONTIER_MAX_LEVEL_50) return; @@ -2019,28 +1993,41 @@ static void CheckPartyIneligibility(void) if (numEligibleMons < toChoose) { - s32 i; - s32 caughtBannedMons = 0; - s32 species = gFrontierBannedSpecies[0]; - for (i = 0; species != 0xFFFF; i++, species = gFrontierBannedSpecies[i]) + u32 i; + u32 baseSpecies = 0; + u32 totalCaughtBanned = 0; + u32 caughtBanned[100] = {0}; + + for (i = 0; i < NUM_SPECIES; i++) { - if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) - caughtBannedMons++; + if (totalCaughtBanned >= ARRAY_COUNT(caughtBanned)) + break; + baseSpecies = GET_BASE_SPECIES_ID(i); + if (baseSpecies == i) + { + if (gSpeciesInfo[baseSpecies].isFrontierBanned) + { + if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(baseSpecies), FLAG_GET_CAUGHT)) + { + caughtBanned[totalCaughtBanned] = baseSpecies; + totalCaughtBanned++; + } + } + } } gStringVar1[0] = EOS; gSpecialVar_0x8004 = TRUE; - count = 0; - for (i = 0; gFrontierBannedSpecies[i] != 0xFFFF; i++) - count = AppendCaughtBannedMonSpeciesName(gFrontierBannedSpecies[i], count, caughtBannedMons); + for (i = 0; i < totalCaughtBanned; i++) + AppendCaughtBannedMonSpeciesName(caughtBanned[i], i+1, totalCaughtBanned); - if (count == 0) + if (totalCaughtBanned == 0) { StringAppend(gStringVar1, gText_Space2); StringAppend(gStringVar1, gText_Are); } else { - if (count % SPECIES_PER_LINE == SPECIES_PER_LINE - 1) + if (totalCaughtBanned % SPECIES_PER_LINE == SPECIES_PER_LINE - 1) StringAppend(gStringVar1, gText_LineBreak); else StringAppend(gStringVar1, gText_Space2); diff --git a/src/party_menu.c b/src/party_menu.c index af88db790d..5fa741385d 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -6926,8 +6926,7 @@ static u8 GetPartySlotEntryStatus(s8 slot) static bool8 GetBattleEntryEligibility(struct Pokemon *mon) { - u16 i = 0; - u16 species; + u32 species; if (GetMonData(mon, MON_DATA_IS_EGG) || GetMonData(mon, MON_DATA_LEVEL) > GetBattleEntryLevelCap() @@ -6948,11 +6947,8 @@ static bool8 GetBattleEntryEligibility(struct Pokemon *mon) return TRUE; default: // Battle Frontier species = GetMonData(mon, MON_DATA_SPECIES); - for (; gFrontierBannedSpecies[i] != 0xFFFF; i++) - { - if (gFrontierBannedSpecies[i] == GET_BASE_SPECIES_ID(species)) - return FALSE; - } + if (gSpeciesInfo[species].isFrontierBanned) + return FALSE; return TRUE; } } diff --git a/test/battle/ability/booster_energy.c b/test/battle/ability/booster_energy.c index 3d9b41d5aa..a63f462b72 100644 --- a/test/battle/ability/booster_energy.c +++ b/test/battle/ability/booster_energy.c @@ -185,7 +185,7 @@ SINGLE_BATTLE_TEST("Booster Energy increases special defense by 30% if it is the SINGLE_BATTLE_TEST("Booster Energy can't be flung if a Paradox species is involved") { GIVEN { - ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadoxForm == TRUE); + ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadox == TRUE); PLAYER(SPECIES_IRON_MOTH); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BOOSTER_ENERGY); } } WHEN { @@ -199,7 +199,7 @@ SINGLE_BATTLE_TEST("Booster Energy can't be flung if a Paradox species is involv SINGLE_BATTLE_TEST("Booster Energy can't be tricked if a Paradox species is involved") { GIVEN { - ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadoxForm == TRUE); + ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadox == TRUE); PLAYER(SPECIES_IRON_MOTH) { Item(ITEM_BERRY_JUICE); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BOOSTER_ENERGY); } } WHEN { From adbb5a44c8cca1934c6a4dcfd91d0a1f97d440a4 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sat, 4 May 2024 14:11:32 +0100 Subject: [PATCH 58/71] Fixes no effect on Diamond Storm, typo on Mortal Spin (#4489) --- src/data/moves_info.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 6fbe504a77..5a22e783fc 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -14113,6 +14113,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Whips up a storm of\n" "diamonds. May up Defense."), + .effect = EFFECT_HIT, .power = 100, .type = TYPE_ROCK, .accuracy = 95, @@ -18721,7 +18722,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .name = COMPOUND_STRING("Mortal Spin"), .description = COMPOUND_STRING( "Erases trap moves and Leech\n" - "Seed. Poisons adjecent foes."), + "Seed. Poisons adjacent foes."), .effect = EFFECT_HIT, .power = 30, .type = TYPE_POISON, From ff2d1bb02cd75857cb3f52ca3d60f7372258efb8 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sat, 4 May 2024 15:59:47 +0100 Subject: [PATCH 59/71] Fixes Assist (#4491) --- src/data/moves_info.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 5a22e783fc..87edd20d9e 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -132,6 +132,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .metronomeBanned = TRUE, .mirrorMoveBanned = TRUE, .sketchBanned = TRUE, + .assistBanned = TRUE, }, [MOVE_POUND] = From 4410c1b797f30e3465a33392198b35e925bcbca6 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 5 May 2024 05:58:11 -0400 Subject: [PATCH 60/71] Improve Eject Pack test names (#4497) --- test/battle/hold_effect/eject_pack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index 6757b11db9..7640292079 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -6,7 +6,7 @@ ASSUMPTIONS ASSUME(gItemsInfo[ITEM_EJECT_PACK].holdEffect == HOLD_EFFECT_EJECT_PACK); } -SINGLE_BATTLE_TEST("Eject Pack does not cause the new pokemon to lose hp due to it's held Life Orb") +SINGLE_BATTLE_TEST("Eject Pack does not cause the new Pokémon to lose HP due to it's held Life Orb") { GIVEN { ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); @@ -26,7 +26,7 @@ SINGLE_BATTLE_TEST("Eject Pack does not cause the new pokemon to lose hp due to } } -SINGLE_BATTLE_TEST("Eject Pack does not activate if there are no pokemon left to battle") +SINGLE_BATTLE_TEST("Eject Pack does not activate if there are no Pokémon left to battle") { GIVEN { PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } @@ -43,7 +43,7 @@ SINGLE_BATTLE_TEST("Eject Pack does not activate if there are no pokemon left to } } -SINGLE_BATTLE_TEST("Eject Pack triggers on the correct pokemon") +SINGLE_BATTLE_TEST("Eject Pack is triggered by self-inflicting stat decreases") { GIVEN { ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); From f07ef07855087aae28272e63399e93025ef08a33 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sun, 5 May 2024 13:25:40 +0100 Subject: [PATCH 61/71] Fixes various abilities' switch-in messages (#4500) * Fixes Costar, Zero to Hero, Supreme Overlord and Ruin abilities' switch-in messages * Fixed typo in Supreme Overlord's String Id * Fixes spacing in battle_string_ids.h --- data/battle_scripts_1.s | 2 +- include/constants/battle_string_ids.h | 2 +- src/battle_message.c | 6 +++--- src/battle_util.c | 2 ++ 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 31b0619118..788b5225f0 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7954,7 +7954,7 @@ BattleScript_QuarkDriveActivates:: BattleScript_RuinAbilityActivates:: call BattleScript_AbilityPopUp - printstring STRINGID_ABILITYWEAKENEDFSURROUNDINGMONSSTAT + printstring STRINGID_ABILITYWEAKENEDSURROUNDINGMONSSTAT waitmessage B_WAIT_TIME_LONG end3 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 7a64f6d6fd..6f5b0db9ef 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -635,7 +635,7 @@ #define STRINGID_SUNLIGHTACTIVATEDABILITY 633 #define STRINGID_STATWASHEIGHTENED 634 #define STRINGID_ELECTRICTERRAINACTIVATEDABILITY 635 -#define STRINGID_ABILITYWEAKENEDFSURROUNDINGMONSSTAT 636 +#define STRINGID_ABILITYWEAKENEDSURROUNDINGMONSSTAT 636 #define STRINGID_ATTACKERGAINEDSTRENGTHFROMTHEFALLEN 637 #define STRINGID_PKMNSABILITYPREVENTSABILITY 638 #define STRINGID_PREPARESHELLTRAP 639 diff --git a/src/battle_message.c b/src/battle_message.c index cbd7bf55ea..a54aba645e 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -792,8 +792,8 @@ static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURREN 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}!"); -static const u8 sText_AbilityWeakenedSurroundingMonsStat[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY}\nweakened the {B_BUFF1} of\lall surrounding Pokémon!\p"); -static const u8 sText_AttackerGainedStrengthFromTheFallen[] = _("{B_ATK_NAME_WITH_PREFIX} gained strength\nfrom the fallen!"); +static const u8 sText_AbilityWeakenedSurroundingMonsStat[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nweakened the {B_BUFF1} of\lall surrounding Pokémon!\p"); +static const u8 sText_AttackerGainedStrengthFromTheFallen[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} gained strength\nfrom the fallen!"); static const u8 sText_PrepareShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set a shell trap!"); static const u8 sText_ShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap didn't work!"); static const u8 sText_SharpSteelFloats[] = _("Sharp-pointed steel floats\naround {B_DEF_TEAM2} team!"); @@ -889,7 +889,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_SHARPSTEELDMG - BATTLESTRINGS_TABLE_START] = sText_SharpSteelDmg, [STRINGID_SHARPSTEELFLOATS - BATTLESTRINGS_TABLE_START] = sText_SharpSteelFloats, [STRINGID_ATTACKERGAINEDSTRENGTHFROMTHEFALLEN - BATTLESTRINGS_TABLE_START] = sText_AttackerGainedStrengthFromTheFallen, - [STRINGID_ABILITYWEAKENEDFSURROUNDINGMONSSTAT - BATTLESTRINGS_TABLE_START] = sText_AbilityWeakenedSurroundingMonsStat, + [STRINGID_ABILITYWEAKENEDSURROUNDINGMONSSTAT - BATTLESTRINGS_TABLE_START] = sText_AbilityWeakenedSurroundingMonsStat, [STRINGID_ELECTRICTERRAINACTIVATEDABILITY - BATTLESTRINGS_TABLE_START] = sText_ElectricTerrainActivatedAbility, [STRINGID_STATWASHEIGHTENED - BATTLESTRINGS_TABLE_START] = sText_StatWasHeightened, [STRINGID_SUNLIGHTACTIVATEDABILITY - BATTLESTRINGS_TABLE_START] = sText_SunlightActivatedAbility, diff --git a/src/battle_util.c b/src/battle_util.c index ecdd8db266..efa1a3ecf7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4697,6 +4697,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && CountBattlerStatIncreases(BATTLE_PARTNER(battler), FALSE)) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; + gBattlerAttacker = battler; for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[battler].statStages[i] = gBattleMons[BATTLE_PARTNER(battler)].statStages[i]; gBattlerTarget = BATTLE_PARTNER(battler); @@ -4713,6 +4714,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !(gBattleStruct->transformZeroToHero[side] & gBitTable[gBattlerPartyIndexes[battler]])) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; + gBattlerAttacker = battler; gBattleStruct->transformZeroToHero[side] |= gBitTable[gBattlerPartyIndexes[battler]]; BattleScriptPushCursorAndCallback(BattleScript_ZeroToHeroActivates); effect++; From bcb230f3c980c2db3e318cf22571ecebc916d631 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 5 May 2024 08:26:30 -0400 Subject: [PATCH 62/71] Fixed Flute Items being consumed in battle (#4496) --- src/party_menu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/party_menu.c b/src/party_menu.c index 3d535be62d..6b5926bdc0 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4595,7 +4595,8 @@ void ItemUseCB_BattleScript(u8 taskId, TaskFunc task) gBattleStruct->itemPartyIndex[gBattlerInMenuId] = GetPartyIdFromBattleSlot(gPartyMenu.slotId); gPartyMenuUseExitCallback = TRUE; PlaySE(SE_SELECT); - RemoveBagItem(gSpecialVar_ItemId, 1); + if (!IsItemFlute(gSpecialVar_ItemId)) + RemoveBagItem(gSpecialVar_ItemId, 1); ScheduleBgCopyTilemapToVram(2); gTasks[taskId].func = task; } From 41138f101cab957fef4e7a401bb6b528c0f3889e Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 5 May 2024 14:29:59 +0200 Subject: [PATCH 63/71] Fix Red Card, Endured Damage interaction (#4482) --- src/battle_main.c | 1 + test/battle/hold_effect/red_card.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/battle_main.c b/src/battle_main.c index e8f825676c..f09c8110a3 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3499,6 +3499,7 @@ void SwitchInClearSetData(u32 battler) // Reset damage to prevent things like red card activating if the switched-in mon is holding it gSpecialStatuses[battler].physicalDmg = 0; gSpecialStatuses[battler].specialDmg = 0; + gBattleStruct->enduredDamage &= ~gBitTable[battler]; // Reset G-Max Chi Strike boosts. gBattleStruct->bonusCritStages[battler] = 0; diff --git a/test/battle/hold_effect/red_card.c b/test/battle/hold_effect/red_card.c index ce4ac80b96..8d7e175cea 100644 --- a/test/battle/hold_effect/red_card.c +++ b/test/battle/hold_effect/red_card.c @@ -447,4 +447,25 @@ SINGLE_BATTLE_TEST("Red Card does not cause the dragged out mon to lose hp due t } } +SINGLE_BATTLE_TEST("Red Card does not activate if holder is switched in mid-turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet is switched out with the Eject Button!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet held up its Red Card against Foe Wobbuffet!"); + } + } +} + // SINGLE_BATTLE_TEST("Red Card activates but fails if the attacker has Dynamaxed") From e20cb62de61b9a08651ce8c357a7a85c07ea1de9 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 5 May 2024 18:54:28 -0400 Subject: [PATCH 64/71] Fixed Steam Roller AI check affecting Terrain setting effects (#4498) * Fixed battle_ai_util.c whitespace * Add arguments check * Parenthesis * Update src/battle_ai_util.c Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> * Added AI tests by AlexOn1ine --------- Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_ai_util.c | 6 +-- .../move_effect/hit_set_remove_terrain.c | 42 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 2dc9946e3d..009cef3bdc 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -439,9 +439,9 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) return TRUE; break; case EFFECT_HIT_SET_REMOVE_TERRAIN: - if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) - return TRUE; - break; + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) && gMovesInfo[move].argument == ARG_TRY_REMOVE_TERRAIN_FAIL) + return TRUE; + break; } return FALSE; diff --git a/test/battle/move_effect/hit_set_remove_terrain.c b/test/battle/move_effect/hit_set_remove_terrain.c index 834b2dc223..549b6bf04f 100644 --- a/test/battle/move_effect/hit_set_remove_terrain.c +++ b/test/battle/move_effect/hit_set_remove_terrain.c @@ -82,3 +82,45 @@ SINGLE_BATTLE_TEST("Ice Spinner doesn't fail if there is no terrain on the field NOT MESSAGE("But it failed!"); } } + +AI_SINGLE_BATTLE_TEST("Steel Roller will not be chosen by the AI if it might fail") +{ + u32 move; + + PARAMETRIZE { move = MOVE_ELECTRIC_TERRAIN; } + PARAMETRIZE { move = MOVE_NONE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_STEEL_ROLLER, MOVE_ICE_SHARD); } + } WHEN { + if (move == MOVE_ELECTRIC_TERRAIN) { + TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); EXPECT_MOVE(opponent, MOVE_ICE_SHARD); } + TURN { EXPECT_MOVE(opponent, MOVE_STEEL_ROLLER); } + } else { + TURN { EXPECT_MOVE(opponent, MOVE_ICE_SHARD); } + } + } +} + +AI_SINGLE_BATTLE_TEST("Ice Spinner can be chosen by the AI regardless if there is a terrain or not") +{ + u32 move; + + PARAMETRIZE { move = MOVE_ELECTRIC_TERRAIN; } + PARAMETRIZE { move = MOVE_NONE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_ICE_SPINNER, MOVE_ICE_SHARD); } + } WHEN { + if (move == MOVE_ELECTRIC_TERRAIN) { + TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); EXPECT_MOVE(opponent, MOVE_ICE_SPINNER); } + TURN { EXPECT_MOVE(opponent, MOVE_ICE_SPINNER); } + } else { + TURN { EXPECT_MOVE(opponent, MOVE_ICE_SPINNER); } + } + } +} From 5ec08ee98c373414f3663bb5bb116a5c6760c746 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 6 May 2024 03:36:52 -0400 Subject: [PATCH 65/71] Small Battle Test reorganization (#4504) * Fixed test folders + Chud Chew test name fixes * Adjusted file names + merged Burn Up and Double Shock files * Added Spit Up/Swallow files that point to Stockpile's file * Multiple changes (see description) - Moved secondary effect files to their own folder. - Split hit_set_entry_hazards.c to separate files for Spikes/Stealth Rock. - Grouped Hex/Venoshock to the same file --- .../cud_chuw.c => ability/cud_chew.c} | 4 +- .../{white_herb.c => restore_stats.c} | 0 test/battle/move_effect/burn_up.c | 54 -------- .../{techno_blast.c => change_type_on_item.c} | 0 test/battle/move_effect/double_shock.c | 54 -------- .../battle/move_effect/fail_if_not_arg_type.c | 112 +++++++++++++++++ .../{ability => move_effect}/gastro_acid.c | 0 test/battle/move_effect/hex.c | 34 ----- .../move_effect/hit_set_entry_hazardss.c | 117 ------------------ .../{ability => move_effect}/role_play.c | 0 ...vulnerable_moves.c => semi_invulnerable.c} | 0 test/battle/move_effect/spit_up.c | 4 + test/battle/move_effect/swallow.c | 4 + .../{two_turn_moves.c => two_turns_attack.c} | 0 test/battle/move_effect/venoshock.c | 28 ----- .../bug_bite.c | 0 .../burn.c} | 0 .../confusion.c} | 0 .../def_minus_1.c} | 0 .../dire_claw.c | 0 .../double_power_on_arg_status.c | 52 ++++++++ .../flinch.c} | 0 .../freeze.c} | 0 .../paralysis.c} | 0 .../pay_day.c | 0 .../poison.c} | 8 +- .../psychic_noise.c | 0 .../rapid_spin.c | 0 .../sp_atk_two_down.c | 0 .../spd_minus_2.c} | 0 test/battle/move_effect_secondary/spikes.c | 64 ++++++++++ .../move_effect_secondary/stealth_rock.c | 65 ++++++++++ .../syrup_bomb.c | 5 + .../thrash.c} | 0 .../throat_chop.c | 0 .../trap_both.c} | 0 .../tri_attack.c | 0 .../axe_kick.c | 0 .../barb_barrage.c | 0 .../flinch_status.c | 0 .../hurricane.c | 0 .../infernal_parade.c | 1 + .../make_it_rain.c | 0 .../triple_arrows.c | 0 .../{move_effect => move_flags}/recoil.c | 0 45 files changed, 311 insertions(+), 295 deletions(-) rename test/battle/{move_effect/cud_chuw.c => ability/cud_chew.c} (93%) rename test/battle/hold_effect/{white_herb.c => restore_stats.c} (100%) delete mode 100644 test/battle/move_effect/burn_up.c rename test/battle/move_effect/{techno_blast.c => change_type_on_item.c} (100%) delete mode 100644 test/battle/move_effect/double_shock.c create mode 100644 test/battle/move_effect/fail_if_not_arg_type.c rename test/battle/{ability => move_effect}/gastro_acid.c (100%) delete mode 100644 test/battle/move_effect/hex.c delete mode 100644 test/battle/move_effect/hit_set_entry_hazardss.c rename test/battle/{ability => move_effect}/role_play.c (100%) rename test/battle/move_effect/{semi_invulnerable_moves.c => semi_invulnerable.c} (100%) create mode 100644 test/battle/move_effect/spit_up.c create mode 100644 test/battle/move_effect/swallow.c rename test/battle/move_effect/{two_turn_moves.c => two_turns_attack.c} (100%) delete mode 100644 test/battle/move_effect/venoshock.c rename test/battle/{move_effect => move_effect_secondary}/bug_bite.c (100%) rename test/battle/{move_effect/burn_hit.c => move_effect_secondary/burn.c} (100%) rename test/battle/{move_effect/confusion_hit.c => move_effect_secondary/confusion.c} (100%) rename test/battle/{move_effect/clanging_scales.c => move_effect_secondary/def_minus_1.c} (100%) rename test/battle/{move_effect => move_effect_secondary}/dire_claw.c (100%) create mode 100644 test/battle/move_effect_secondary/double_power_on_arg_status.c rename test/battle/{move_effect/flinch_hit.c => move_effect_secondary/flinch.c} (100%) rename test/battle/{move_effect/freeze_hit.c => move_effect_secondary/freeze.c} (100%) rename test/battle/{move_effect/paralyze_hit.c => move_effect_secondary/paralysis.c} (100%) rename test/battle/{move_effect => move_effect_secondary}/pay_day.c (100%) rename test/battle/{move_effect/poison_hit.c => move_effect_secondary/poison.c} (86%) rename test/battle/{move_effect => move_effect_secondary}/psychic_noise.c (100%) rename test/battle/{move_effect => move_effect_secondary}/rapid_spin.c (100%) rename test/battle/{move_effect => move_effect_secondary}/sp_atk_two_down.c (100%) rename test/battle/{move_effect/spin_out.c => move_effect_secondary/spd_minus_2.c} (100%) create mode 100644 test/battle/move_effect_secondary/spikes.c create mode 100644 test/battle/move_effect_secondary/stealth_rock.c rename test/battle/{move_effect => move_effect_secondary}/syrup_bomb.c (98%) rename test/battle/{move_effect/rampage.c => move_effect_secondary/thrash.c} (100%) rename test/battle/{move_effect => move_effect_secondary}/throat_chop.c (100%) rename test/battle/{move_effect/jaw_lock.c => move_effect_secondary/trap_both.c} (100%) rename test/battle/{move_effect => move_effect_secondary}/tri_attack.c (100%) rename test/battle/{move_effect => move_effects_combined}/axe_kick.c (100%) rename test/battle/{move_effect => move_effects_combined}/barb_barrage.c (100%) rename test/battle/{move_effect => move_effects_combined}/flinch_status.c (100%) rename test/battle/{move_effect => move_effects_combined}/hurricane.c (100%) rename test/battle/{move_effect => move_effects_combined}/infernal_parade.c (94%) rename test/battle/{move_effect => move_effects_combined}/make_it_rain.c (100%) rename test/battle/{move_effect => move_effects_combined}/triple_arrows.c (100%) rename test/battle/{move_effect => move_flags}/recoil.c (100%) diff --git a/test/battle/move_effect/cud_chuw.c b/test/battle/ability/cud_chew.c similarity index 93% rename from test/battle/move_effect/cud_chuw.c rename to test/battle/ability/cud_chew.c index ba59d355a4..305969aa93 100644 --- a/test/battle/move_effect/cud_chuw.c +++ b/test/battle/ability/cud_chew.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Cud Chuw will activate Kee Berry effect again on the next turn") +SINGLE_BATTLE_TEST("Cud Chew will activate Kee Berry effect again on the next turn") { GIVEN { ASSUME(gItemsInfo[ITEM_KEE_BERRY].holdEffect == HOLD_EFFECT_KEE_BERRY); @@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Cud Chuw will activate Kee Berry effect again on the next tu } } -SINGLE_BATTLE_TEST("Cud Chuw will activate Oran Berry effect again on the next turn") +SINGLE_BATTLE_TEST("Cud Chew will activate Oran Berry effect again on the next turn") { GIVEN { ASSUME(gItemsInfo[ITEM_ORAN_BERRY].holdEffect == HOLD_EFFECT_RESTORE_HP); diff --git a/test/battle/hold_effect/white_herb.c b/test/battle/hold_effect/restore_stats.c similarity index 100% rename from test/battle/hold_effect/white_herb.c rename to test/battle/hold_effect/restore_stats.c diff --git a/test/battle/move_effect/burn_up.c b/test/battle/move_effect/burn_up.c deleted file mode 100644 index 46b26a49a0..0000000000 --- a/test/battle/move_effect/burn_up.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_BURN_UP].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); - ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_BURN_UP, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) == TRUE); - ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_FIRE || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_FIRE); - ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[0] == TYPE_FIRE || gSpeciesInfo[SPECIES_CYNDAQUIL].types[1] == TYPE_FIRE); -} - -SINGLE_BATTLE_TEST("Burn Up user loses its Fire-type") -{ - GIVEN { - PLAYER(SPECIES_CYNDAQUIL); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_BURN_UP); } - TURN { MOVE(player, MOVE_BURN_UP); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); - MESSAGE("Cyndaquil burned itself out!"); - MESSAGE("Cyndaquil used Burn Up!"); - MESSAGE("But it failed!"); - } -} - -SINGLE_BATTLE_TEST("Burn Up fails if the user isn't a Fire-type") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_BURN_UP); } - } SCENE { - NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); } - MESSAGE("Wobbuffet used Burn Up!"); - MESSAGE("But it failed!"); - } -} - -SINGLE_BATTLE_TEST("Burn Up user loses its Fire-type if enemy faints") -{ - GIVEN { - PLAYER(SPECIES_CYNDAQUIL); - OPPONENT(SPECIES_WOBBUFFET) { HP(1); } - } WHEN { - TURN { MOVE(player, MOVE_BURN_UP); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); - HP_BAR(opponent, hp: 0); - MESSAGE("Cyndaquil burned itself out!"); - } -} diff --git a/test/battle/move_effect/techno_blast.c b/test/battle/move_effect/change_type_on_item.c similarity index 100% rename from test/battle/move_effect/techno_blast.c rename to test/battle/move_effect/change_type_on_item.c diff --git a/test/battle/move_effect/double_shock.c b/test/battle/move_effect/double_shock.c deleted file mode 100644 index b7f59a3ab7..0000000000 --- a/test/battle/move_effect/double_shock.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); - ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_DOUBLE_SHOCK, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_ELECTRIC) == TRUE); - ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_ELECTRIC || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_ELECTRIC); - ASSUME(gSpeciesInfo[SPECIES_PIKACHU].types[0] == TYPE_ELECTRIC || gSpeciesInfo[SPECIES_PIKACHU].types[1] == TYPE_ELECTRIC); -} - -SINGLE_BATTLE_TEST("Double Shock user loses its Electric-type") -{ - GIVEN { - PLAYER(SPECIES_PIKACHU); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } - TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); - MESSAGE("Pikachu used up all of its electricity!"); - MESSAGE("Pikachu used Double Shock!"); - MESSAGE("But it failed!"); - } -} - -SINGLE_BATTLE_TEST("Double Shock fails if the user isn't an Electric-type") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } - } SCENE { - NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); } - MESSAGE("Wobbuffet used Double Shock!"); - MESSAGE("But it failed!"); - } -} - -SINGLE_BATTLE_TEST("Double Shock user loses its Electric-type if enemy faints") -{ - GIVEN { - PLAYER(SPECIES_PIKACHU); - OPPONENT(SPECIES_WOBBUFFET) { HP(1); } - } WHEN { - TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); - HP_BAR(opponent, hp: 0); - MESSAGE("Pikachu used up all of its electricity!"); - } -} diff --git a/test/battle/move_effect/fail_if_not_arg_type.c b/test/battle/move_effect/fail_if_not_arg_type.c new file mode 100644 index 0000000000..4ffe0a0323 --- /dev/null +++ b/test/battle/move_effect/fail_if_not_arg_type.c @@ -0,0 +1,112 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Burn Up user loses its Fire-type") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_BURN_UP].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_BURN_UP, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) == TRUE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_FIRE || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[0] == TYPE_FIRE || gSpeciesInfo[SPECIES_CYNDAQUIL].types[1] == TYPE_FIRE); + PLAYER(SPECIES_CYNDAQUIL); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BURN_UP); } + TURN { MOVE(player, MOVE_BURN_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); + MESSAGE("Cyndaquil burned itself out!"); + MESSAGE("Cyndaquil used Burn Up!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Burn Up fails if the user isn't a Fire-type") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_BURN_UP].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_BURN_UP, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) == TRUE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_FIRE || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_FIRE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BURN_UP); } + } SCENE { + NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); } + MESSAGE("Wobbuffet used Burn Up!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Burn Up user loses its Fire-type if enemy faints") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_BURN_UP].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_BURN_UP, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) == TRUE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_FIRE || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_FIRE); + ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[0] == TYPE_FIRE || gSpeciesInfo[SPECIES_CYNDAQUIL].types[1] == TYPE_FIRE); + PLAYER(SPECIES_CYNDAQUIL); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_BURN_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player); + HP_BAR(opponent, hp: 0); + MESSAGE("Cyndaquil burned itself out!"); + } +} + +SINGLE_BATTLE_TEST("Double Shock user loses its Electric-type") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_DOUBLE_SHOCK, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_ELECTRIC) == TRUE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_ELECTRIC || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_ELECTRIC); + ASSUME(gSpeciesInfo[SPECIES_PIKACHU].types[0] == TYPE_ELECTRIC || gSpeciesInfo[SPECIES_PIKACHU].types[1] == TYPE_ELECTRIC); + PLAYER(SPECIES_PIKACHU); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + MESSAGE("Pikachu used up all of its electricity!"); + MESSAGE("Pikachu used Double Shock!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Double Shock fails if the user isn't an Electric-type") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_DOUBLE_SHOCK, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_ELECTRIC) == TRUE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_ELECTRIC || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_ELECTRIC); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } + } SCENE { + NONE_OF { ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); } + MESSAGE("Wobbuffet used Double Shock!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Double Shock user loses its Electric-type if enemy faints") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_DOUBLE_SHOCK, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_ELECTRIC) == TRUE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_ELECTRIC || gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_ELECTRIC); + ASSUME(gSpeciesInfo[SPECIES_PIKACHU].types[0] == TYPE_ELECTRIC || gSpeciesInfo[SPECIES_PIKACHU].types[1] == TYPE_ELECTRIC); + PLAYER(SPECIES_PIKACHU); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, hp: 0); + MESSAGE("Pikachu used up all of its electricity!"); + } +} diff --git a/test/battle/ability/gastro_acid.c b/test/battle/move_effect/gastro_acid.c similarity index 100% rename from test/battle/ability/gastro_acid.c rename to test/battle/move_effect/gastro_acid.c diff --git a/test/battle/move_effect/hex.c b/test/battle/move_effect/hex.c deleted file mode 100644 index 25660d7309..0000000000 --- a/test/battle/move_effect/hex.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_HEX].effect == EFFECT_DOUBLE_POWER_ON_ARG_STATUS); - ASSUME(gMovesInfo[MOVE_HEX].argument == STATUS1_ANY); -} - -SINGLE_BATTLE_TEST("Hex deals double damage to foes with a status", s16 damage) -{ - u32 status1; - PARAMETRIZE { status1 = STATUS1_NONE; } - PARAMETRIZE { status1 = STATUS1_SLEEP; } - PARAMETRIZE { status1 = STATUS1_POISON; } - PARAMETRIZE { status1 = STATUS1_BURN; } - PARAMETRIZE { status1 = STATUS1_FREEZE; } - PARAMETRIZE { status1 = STATUS1_PARALYSIS; } - PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); } - } WHEN { - TURN { MOVE(player, MOVE_HEX); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_HEX, player); - HP_BAR(opponent, captureDamage: &results[i].damage); - } THEN { - if (i > 0) - EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[i].damage); - if (i > 1) - EXPECT_EQ(results[i-1].damage, results[i].damage); - } -} diff --git a/test/battle/move_effect/hit_set_entry_hazardss.c b/test/battle/move_effect/hit_set_entry_hazardss.c deleted file mode 100644 index fa405ed3b3..0000000000 --- a/test/battle/move_effect/hit_set_entry_hazardss.c +++ /dev/null @@ -1,117 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(MoveHasAdditionalEffect(MOVE_STONE_AXE, MOVE_EFFECT_STEALTH_ROCK) == TRUE); - ASSUME(MoveHasAdditionalEffect(MOVE_CEASELESS_EDGE, MOVE_EFFECT_SPIKES) == TRUE); -} - -SINGLE_BATTLE_TEST("Stone Axe / Ceaseless Edge set up hazards after hitting the target") -{ - u16 move; - PARAMETRIZE { move = MOVE_STONE_AXE; } - PARAMETRIZE { move = MOVE_CEASELESS_EDGE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, move); } - TURN { SWITCH(opponent, 1); } - } SCENE { - s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); - ANIMATION(ANIM_TYPE_MOVE, move, player); - HP_BAR(opponent); - if (move == MOVE_CEASELESS_EDGE) { - MESSAGE("Spikes were scattered all around the opposing team!"); - } - else { - MESSAGE("Pointed stones float in the air around the opposing team!"); - } - MESSAGE("2 sent out Wobbuffet!"); - if (move == MOVE_CEASELESS_EDGE) { - HP_BAR(opponent, damage: maxHP / 8); - MESSAGE("Foe Wobbuffet is hurt by spikes!"); - } - else { - HP_BAR(opponent, damage: maxHP / 8); - MESSAGE("Pointed stones dug into Foe Wobbuffet!"); - } - } -} - -SINGLE_BATTLE_TEST("Ceaseless Edge can set up to 3 layers of Spikes") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(player, MOVE_CEASELESS_EDGE); } - TURN { MOVE(player, MOVE_CEASELESS_EDGE); } - TURN { MOVE(player, MOVE_CEASELESS_EDGE); } - TURN { MOVE(player, MOVE_CEASELESS_EDGE); } - TURN { SWITCH(opponent, 1); } - } SCENE { - s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); - HP_BAR(opponent); - MESSAGE("Spikes were scattered all around the opposing team!"); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); - HP_BAR(opponent); - MESSAGE("Spikes were scattered all around the opposing team!"); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); - HP_BAR(opponent); - MESSAGE("Spikes were scattered all around the opposing team!"); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); - HP_BAR(opponent); - NOT MESSAGE("Spikes were scattered all around the opposing team!"); - - MESSAGE("2 sent out Wynaut!"); - HP_BAR(opponent, damage: maxHP / 4); - MESSAGE("Foe Wynaut is hurt by spikes!"); - } -} - -SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(player, MOVE_STONE_AXE); } - TURN { MOVE(player, MOVE_STONE_AXE); } - TURN { MOVE(player, MOVE_STONE_AXE); } - TURN { MOVE(player, MOVE_STONE_AXE); } - TURN { SWITCH(opponent, 1); } - } SCENE { - s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); - HP_BAR(opponent); - MESSAGE("Pointed stones float in the air around the opposing team!"); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); - HP_BAR(opponent); - NOT MESSAGE("Pointed stones float in the air around the opposing team!"); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); - HP_BAR(opponent); - NOT MESSAGE("Pointed stones float in the air around the opposing team!"); - - ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); - HP_BAR(opponent); - NOT MESSAGE("Pointed stones float in the air around the opposing team!"); - - MESSAGE("2 sent out Wynaut!"); - HP_BAR(opponent, damage: maxHP / 8); - MESSAGE("Pointed stones dug into Foe Wynaut!"); - } -} - diff --git a/test/battle/ability/role_play.c b/test/battle/move_effect/role_play.c similarity index 100% rename from test/battle/ability/role_play.c rename to test/battle/move_effect/role_play.c diff --git a/test/battle/move_effect/semi_invulnerable_moves.c b/test/battle/move_effect/semi_invulnerable.c similarity index 100% rename from test/battle/move_effect/semi_invulnerable_moves.c rename to test/battle/move_effect/semi_invulnerable.c diff --git a/test/battle/move_effect/spit_up.c b/test/battle/move_effect/spit_up.c new file mode 100644 index 0000000000..1291962e1f --- /dev/null +++ b/test/battle/move_effect/spit_up.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Go to test/battle/move_effect/stockpile.c for Spit Up's tests diff --git a/test/battle/move_effect/swallow.c b/test/battle/move_effect/swallow.c new file mode 100644 index 0000000000..bce0a4470c --- /dev/null +++ b/test/battle/move_effect/swallow.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Go to test/battle/move_effect/stockpile.c for Swallow's tests diff --git a/test/battle/move_effect/two_turn_moves.c b/test/battle/move_effect/two_turns_attack.c similarity index 100% rename from test/battle/move_effect/two_turn_moves.c rename to test/battle/move_effect/two_turns_attack.c diff --git a/test/battle/move_effect/venoshock.c b/test/battle/move_effect/venoshock.c deleted file mode 100644 index feea48df10..0000000000 --- a/test/battle/move_effect/venoshock.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_VENOSHOCK].effect == EFFECT_DOUBLE_POWER_ON_ARG_STATUS); - ASSUME(gMovesInfo[MOVE_VENOSHOCK].argument == STATUS1_PSN_ANY); -} - -SINGLE_BATTLE_TEST("Venoshock's power doubles if the target is poisoned/badly poisoned", s16 damage) -{ - u32 status1; - PARAMETRIZE { status1 = 0; } - PARAMETRIZE { status1 = STATUS1_POISON; } - PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); } - } WHEN { - TURN { MOVE(player, MOVE_VENOSHOCK); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOSHOCK, player); - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); - EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage); - } -} diff --git a/test/battle/move_effect/bug_bite.c b/test/battle/move_effect_secondary/bug_bite.c similarity index 100% rename from test/battle/move_effect/bug_bite.c rename to test/battle/move_effect_secondary/bug_bite.c diff --git a/test/battle/move_effect/burn_hit.c b/test/battle/move_effect_secondary/burn.c similarity index 100% rename from test/battle/move_effect/burn_hit.c rename to test/battle/move_effect_secondary/burn.c diff --git a/test/battle/move_effect/confusion_hit.c b/test/battle/move_effect_secondary/confusion.c similarity index 100% rename from test/battle/move_effect/confusion_hit.c rename to test/battle/move_effect_secondary/confusion.c diff --git a/test/battle/move_effect/clanging_scales.c b/test/battle/move_effect_secondary/def_minus_1.c similarity index 100% rename from test/battle/move_effect/clanging_scales.c rename to test/battle/move_effect_secondary/def_minus_1.c diff --git a/test/battle/move_effect/dire_claw.c b/test/battle/move_effect_secondary/dire_claw.c similarity index 100% rename from test/battle/move_effect/dire_claw.c rename to test/battle/move_effect_secondary/dire_claw.c diff --git a/test/battle/move_effect_secondary/double_power_on_arg_status.c b/test/battle/move_effect_secondary/double_power_on_arg_status.c new file mode 100644 index 0000000000..d147264470 --- /dev/null +++ b/test/battle/move_effect_secondary/double_power_on_arg_status.c @@ -0,0 +1,52 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Hex deals double damage to foes with a status", s16 damage) +{ + u32 status1; + PARAMETRIZE { status1 = STATUS1_NONE; } + PARAMETRIZE { status1 = STATUS1_SLEEP; } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_BURN; } + PARAMETRIZE { status1 = STATUS1_FREEZE; } + PARAMETRIZE { status1 = STATUS1_PARALYSIS; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + GIVEN { + ASSUME(gMovesInfo[MOVE_HEX].effect == EFFECT_DOUBLE_POWER_ON_ARG_STATUS); + ASSUME(gMovesInfo[MOVE_HEX].argument == STATUS1_ANY); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); } + } WHEN { + TURN { MOVE(player, MOVE_HEX); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEX, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } THEN { + if (i > 0) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[i].damage); + if (i > 1) + EXPECT_EQ(results[i-1].damage, results[i].damage); + } +} + +SINGLE_BATTLE_TEST("Venoshock's power doubles if the target is poisoned/badly poisoned", s16 damage) +{ + u32 status1; + PARAMETRIZE { status1 = 0; } + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + GIVEN { + ASSUME(gMovesInfo[MOVE_VENOSHOCK].effect == EFFECT_DOUBLE_POWER_ON_ARG_STATUS); + ASSUME(gMovesInfo[MOVE_VENOSHOCK].argument == STATUS1_PSN_ANY); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); } + } WHEN { + TURN { MOVE(player, MOVE_VENOSHOCK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOSHOCK, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage); + } +} diff --git a/test/battle/move_effect/flinch_hit.c b/test/battle/move_effect_secondary/flinch.c similarity index 100% rename from test/battle/move_effect/flinch_hit.c rename to test/battle/move_effect_secondary/flinch.c diff --git a/test/battle/move_effect/freeze_hit.c b/test/battle/move_effect_secondary/freeze.c similarity index 100% rename from test/battle/move_effect/freeze_hit.c rename to test/battle/move_effect_secondary/freeze.c diff --git a/test/battle/move_effect/paralyze_hit.c b/test/battle/move_effect_secondary/paralysis.c similarity index 100% rename from test/battle/move_effect/paralyze_hit.c rename to test/battle/move_effect_secondary/paralysis.c diff --git a/test/battle/move_effect/pay_day.c b/test/battle/move_effect_secondary/pay_day.c similarity index 100% rename from test/battle/move_effect/pay_day.c rename to test/battle/move_effect_secondary/pay_day.c diff --git a/test/battle/move_effect/poison_hit.c b/test/battle/move_effect_secondary/poison.c similarity index 86% rename from test/battle/move_effect/poison_hit.c rename to test/battle/move_effect_secondary/poison.c index f7f46df4f9..63b2816bfd 100644 --- a/test/battle/move_effect/poison_hit.c +++ b/test/battle/move_effect_secondary/poison.c @@ -1,15 +1,10 @@ #include "global.h" #include "test/battle.h" -ASSUMPTIONS -{ - ASSUME(MoveHasAdditionalEffect(MOVE_POISON_STING, MOVE_EFFECT_POISON) == TRUE); - ASSUME(MoveHasAdditionalEffect(MOVE_TWINEEDLE, MOVE_EFFECT_POISON) == TRUE); -} - SINGLE_BATTLE_TEST("Poison Sting inflicts poison") { GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_POISON_STING, MOVE_EFFECT_POISON) == TRUE); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -29,6 +24,7 @@ SINGLE_BATTLE_TEST("Poison cannot be inflicted on Poison and Steel-type Pokémon PARAMETRIZE { mon = SPECIES_NIDORAN_M; } PARAMETRIZE { mon = SPECIES_REGISTEEL; } GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_TWINEEDLE, MOVE_EFFECT_POISON) == TRUE); ASSUME(gSpeciesInfo[SPECIES_NIDORAN_M].types[0] == TYPE_POISON); ASSUME(gSpeciesInfo[SPECIES_REGISTEEL].types[0] == TYPE_STEEL); PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/psychic_noise.c b/test/battle/move_effect_secondary/psychic_noise.c similarity index 100% rename from test/battle/move_effect/psychic_noise.c rename to test/battle/move_effect_secondary/psychic_noise.c diff --git a/test/battle/move_effect/rapid_spin.c b/test/battle/move_effect_secondary/rapid_spin.c similarity index 100% rename from test/battle/move_effect/rapid_spin.c rename to test/battle/move_effect_secondary/rapid_spin.c diff --git a/test/battle/move_effect/sp_atk_two_down.c b/test/battle/move_effect_secondary/sp_atk_two_down.c similarity index 100% rename from test/battle/move_effect/sp_atk_two_down.c rename to test/battle/move_effect_secondary/sp_atk_two_down.c diff --git a/test/battle/move_effect/spin_out.c b/test/battle/move_effect_secondary/spd_minus_2.c similarity index 100% rename from test/battle/move_effect/spin_out.c rename to test/battle/move_effect_secondary/spd_minus_2.c diff --git a/test/battle/move_effect_secondary/spikes.c b/test/battle/move_effect_secondary/spikes.c new file mode 100644 index 0000000000..b9c72930c2 --- /dev/null +++ b/test/battle/move_effect_secondary/spikes.c @@ -0,0 +1,64 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_CEASELESS_EDGE, MOVE_EFFECT_SPIKES) == TRUE); +} + +SINGLE_BATTLE_TEST("Ceaseless Edge sets up hazards after hitting the target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + MESSAGE("2 sent out Wobbuffet!"); + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Foe Wobbuffet is hurt by spikes!"); + } +} + +SINGLE_BATTLE_TEST("Ceaseless Edge can set up to 3 layers of Spikes") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { MOVE(player, MOVE_CEASELESS_EDGE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + MESSAGE("Spikes were scattered all around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player); + HP_BAR(opponent); + NOT MESSAGE("Spikes were scattered all around the opposing team!"); + + MESSAGE("2 sent out Wynaut!"); + HP_BAR(opponent, damage: maxHP / 4); + MESSAGE("Foe Wynaut is hurt by spikes!"); + } +} diff --git a/test/battle/move_effect_secondary/stealth_rock.c b/test/battle/move_effect_secondary/stealth_rock.c new file mode 100644 index 0000000000..034e2c347b --- /dev/null +++ b/test/battle/move_effect_secondary/stealth_rock.c @@ -0,0 +1,65 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_STONE_AXE, MOVE_EFFECT_STEALTH_ROCK) == TRUE); +} + +SINGLE_BATTLE_TEST("Stone Axe sets up hazards after hitting the target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + MESSAGE("Pointed stones float in the air around the opposing team!"); + MESSAGE("2 sent out Wobbuffet!"); + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Pointed stones dug into Foe Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { MOVE(player, MOVE_STONE_AXE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + MESSAGE("Pointed stones float in the air around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + NOT MESSAGE("Pointed stones float in the air around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + NOT MESSAGE("Pointed stones float in the air around the opposing team!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player); + HP_BAR(opponent); + NOT MESSAGE("Pointed stones float in the air around the opposing team!"); + + MESSAGE("2 sent out Wynaut!"); + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Pointed stones dug into Foe Wynaut!"); + } +} + diff --git a/test/battle/move_effect/syrup_bomb.c b/test/battle/move_effect_secondary/syrup_bomb.c similarity index 98% rename from test/battle/move_effect/syrup_bomb.c rename to test/battle/move_effect_secondary/syrup_bomb.c index 57831abb55..1e6d613721 100644 --- a/test/battle/move_effect/syrup_bomb.c +++ b/test/battle/move_effect_secondary/syrup_bomb.c @@ -1,6 +1,11 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_SYRUP_BOMB, MOVE_EFFECT_SYRUP_BOMB) == TRUE); +} + SINGLE_BATTLE_TEST("Syrup Bomb covers the foe in sticky syrup for 3 turns") { u8 j; diff --git a/test/battle/move_effect/rampage.c b/test/battle/move_effect_secondary/thrash.c similarity index 100% rename from test/battle/move_effect/rampage.c rename to test/battle/move_effect_secondary/thrash.c diff --git a/test/battle/move_effect/throat_chop.c b/test/battle/move_effect_secondary/throat_chop.c similarity index 100% rename from test/battle/move_effect/throat_chop.c rename to test/battle/move_effect_secondary/throat_chop.c diff --git a/test/battle/move_effect/jaw_lock.c b/test/battle/move_effect_secondary/trap_both.c similarity index 100% rename from test/battle/move_effect/jaw_lock.c rename to test/battle/move_effect_secondary/trap_both.c diff --git a/test/battle/move_effect/tri_attack.c b/test/battle/move_effect_secondary/tri_attack.c similarity index 100% rename from test/battle/move_effect/tri_attack.c rename to test/battle/move_effect_secondary/tri_attack.c diff --git a/test/battle/move_effect/axe_kick.c b/test/battle/move_effects_combined/axe_kick.c similarity index 100% rename from test/battle/move_effect/axe_kick.c rename to test/battle/move_effects_combined/axe_kick.c diff --git a/test/battle/move_effect/barb_barrage.c b/test/battle/move_effects_combined/barb_barrage.c similarity index 100% rename from test/battle/move_effect/barb_barrage.c rename to test/battle/move_effects_combined/barb_barrage.c diff --git a/test/battle/move_effect/flinch_status.c b/test/battle/move_effects_combined/flinch_status.c similarity index 100% rename from test/battle/move_effect/flinch_status.c rename to test/battle/move_effects_combined/flinch_status.c diff --git a/test/battle/move_effect/hurricane.c b/test/battle/move_effects_combined/hurricane.c similarity index 100% rename from test/battle/move_effect/hurricane.c rename to test/battle/move_effects_combined/hurricane.c diff --git a/test/battle/move_effect/infernal_parade.c b/test/battle/move_effects_combined/infernal_parade.c similarity index 94% rename from test/battle/move_effect/infernal_parade.c rename to test/battle/move_effects_combined/infernal_parade.c index bd7ec793ac..6aa46ef8cb 100644 --- a/test/battle/move_effect/infernal_parade.c +++ b/test/battle/move_effects_combined/infernal_parade.c @@ -5,6 +5,7 @@ ASSUMPTIONS { ASSUME(gMovesInfo[MOVE_INFERNAL_PARADE].effect == EFFECT_DOUBLE_POWER_ON_ARG_STATUS); ASSUME(gMovesInfo[MOVE_INFERNAL_PARADE].argument == STATUS1_ANY); + ASSUME(MoveHasAdditionalEffect(MOVE_INFERNAL_PARADE, MOVE_EFFECT_BURN) == TRUE); } SINGLE_BATTLE_TEST("Infernal Parade inflicts burn") diff --git a/test/battle/move_effect/make_it_rain.c b/test/battle/move_effects_combined/make_it_rain.c similarity index 100% rename from test/battle/move_effect/make_it_rain.c rename to test/battle/move_effects_combined/make_it_rain.c diff --git a/test/battle/move_effect/triple_arrows.c b/test/battle/move_effects_combined/triple_arrows.c similarity index 100% rename from test/battle/move_effect/triple_arrows.c rename to test/battle/move_effects_combined/triple_arrows.c diff --git a/test/battle/move_effect/recoil.c b/test/battle/move_flags/recoil.c similarity index 100% rename from test/battle/move_effect/recoil.c rename to test/battle/move_flags/recoil.c From d98a4cb9a5acf3b7e51e8d21aab69aeedf0cb1ae Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 6 May 2024 12:07:22 +0200 Subject: [PATCH 66/71] Fixed evolution tracker updates (#4503) * update evolution tracker correctly * Update src/battle_script_commands.c --------- Co-authored-by: Bassoonian --- src/battle_script_commands.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 53b9cbb024..f80bebd8a2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -365,7 +365,7 @@ static void RemoveAllTerrains(void); static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate); static bool8 CanBurnHitThaw(u16 move); static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); -static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount); +static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -6316,7 +6316,7 @@ static void Cmd_moveend(void) case MOVEEND_SET_EVOLUTION_TRACKER: // If the Pokémon needs to keep track of move usage for its evolutions, do it if (originallyUsedMove != MOVE_NONE) - TryUpdateEvolutionTracker(EVO_LEVEL_MOVE_TWENTY_TIMES, 1); + TryUpdateEvolutionTracker(EVO_LEVEL_MOVE_TWENTY_TIMES, 1, originallyUsedMove); gBattleScripting.moveendState++; break; case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits. @@ -16785,7 +16785,7 @@ void BS_RunStatChangeItems(void) ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, GetBattlerForBattleScript(cmd->battler), FALSE); } -static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount) +static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove) { u32 i; @@ -16810,9 +16810,19 @@ static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount) // We only have 9 bits to use u16 val = min(511, GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER) + upAmount); // Reset progress if you faint for the recoil method. - if (gBattleMons[gBattlerAttacker].hp == 0 && (evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_MALE || evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_FEMALE)) - val = 0; - SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val); + switch (evolutionMethod) + { + case EVO_LEVEL_MOVE_TWENTY_TIMES: + if (evolutions[i].param == usedMove) + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val); + break; + case EVO_LEVEL_RECOIL_DAMAGE_MALE: + case EVO_LEVEL_RECOIL_DAMAGE_FEMALE: + if (gBattleMons[gBattlerAttacker].hp == 0) + val = 0; + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val); + break; + } return; } } @@ -16822,8 +16832,18 @@ static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount) void BS_TryUpdateRecoilTracker(void) { NATIVE_ARGS(); - TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_MALE, gBattleMoveDamage); - TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_FEMALE, gBattleMoveDamage); + u8 gender = GetMonGender(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]); + + switch(gender) + { + case MON_MALE: + TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_MALE, gBattleMoveDamage, MOVE_NONE); + break; + case MON_FEMALE: + TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_FEMALE, gBattleMoveDamage, MOVE_NONE); + break; + } + gBattlescriptCurrInstr = cmd->nextInstr; } From 2a605f2d8498808b1570ebcfc5d47e47f83c88b2 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 6 May 2024 11:37:03 -0400 Subject: [PATCH 67/71] Gen 1 type matchups + cleaned type matchup table (#4508) --- include/config/battle.h | 2 +- src/battle_util.c | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index b6c55e8d24..18379d4add 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -43,7 +43,7 @@ #define B_GHOSTS_ESCAPE GEN_LATEST // In Gen6+, abilities like Shadow Tag or moves like Mean Look fail on Ghost-type Pokémon. They can also escape any Wild Battle. #define B_PARALYZE_ELECTRIC GEN_LATEST // In Gen6+, Electric-type Pokémon can't be paralyzed. #define B_POWDER_GRASS GEN_LATEST // In Gen6+, Grass-type Pokémon are immune to powder and spore moves. -#define B_STEEL_RESISTANCES GEN_LATEST // In Gen6+, Steel-type Pokémon are no longer resistant to Dark-type and Ghost-type moves. +#define B_UPDATED_TYPE_MATCHUPS GEN_LATEST // Updates Type matchups. Refer to sTypeEffectivenessTable for details. #define B_PRANKSTER_DARK_TYPES GEN_LATEST // In Gen7+, Prankster-elevated status moves do not affect Dark type Pokémon. #define B_SHEER_COLD_IMMUNITY GEN_LATEST // In Gen7+, Ice-types are immune to Sheer Cold #define B_ROOST_PURE_FLYING GEN_LATEST // In Gen5+, Roost makes pure Flying-types into Normal-type. diff --git a/src/battle_util.c b/src/battle_util.c index 8044c33a2b..12ed07d6d7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -906,21 +906,24 @@ static const uq4_12_t sPercentToModifier[] = #define X UQ_4_12 #define ______ X(1.0) // Regular effectiveness. +// Type matchup updates. Attacker Defender +#define STL_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_6 ? X(1.0) : X(0.5)) // Ghost/Dark -> Steel +#define PSN_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(2.0)) // Bug -> Poison +#define BUG_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(1.0) : X(2.0)) // Poison -> Bug +#define PSY_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(2.0) : X(0.0)) // Ghost -> Psychic +#define FIR_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(1.0)) // Ice -> Fire + static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = {// Defender --> // Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar [TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, [TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______}, [TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______}, - [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______}, + [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), BUG_RS, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______}, [TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______}, [TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______}, - [TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______}, -#if B_STEEL_RESISTANCES >= GEN_6 - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______}, -#else - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______}, -#endif + [TYPE_BUG] = {______, X(0.5), X(0.5), PSN_RS, ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______}, + [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, PSY_RS, ______, ______, X(0.5), ______, ______}, [TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______}, [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, [TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______}, @@ -928,13 +931,9 @@ static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON [TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, [TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______}, [TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______}, - [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______}, + [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, FIR_RS, X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______}, [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______}, -#if B_STEEL_RESISTANCES >= GEN_6 - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, -#else - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, -#endif + [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, [TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______}, [TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, }; From b2fda17c06369086350e2c5047cfbed85df48263 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Mon, 6 May 2024 12:18:15 -0400 Subject: [PATCH 68/71] Make filepath links in readme relative (#4509) This allows programs like emacs to open the proper file when clicking them. Co-authored-by: binarycat --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6487086183..d29cd7bede 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ Based off RHH's pokeemerald-expansion v1.8.0 https://github.com/rh-hideout/pokee ## What features are included? - ***IMPORTANT*❗❗ Read through these to learn what features you can toggle**: - - [Battle configurations](/include/config/battle.h) - - [Pokémon configurations](/include/config/pokemon.h) - - [Item configurations](/include/config/item.h) - - [Overworld configurations](/include/config/overworld.h) - - [Debug configurations](/include/config/debug.h) + - [Battle configurations](include/config/battle.h) + - [Pokémon configurations](include/config/pokemon.h) + - [Item configurations](include/config/item.h) + - [Overworld configurations](include/config/overworld.h) + - [Debug configurations](include/config/debug.h) - ***Upgraded battle engine.*** - Gen5+ damage calculation. - 2v2 Wild battles support. @@ -62,7 +62,7 @@ Based off RHH's pokeemerald-expansion v1.8.0 https://github.com/rh-hideout/pokee - Recalculating stats at the end of every battle. - Level 100 Pokémon can earn EVs. - Inverse battle support. - - TONS of other features listed [here](/include/config/battle.h). + - TONS of other features listed [here](include/config/battle.h). - ***Full Trainer customization*** - Nickname, EVs, IVs, moves, ability, ball, friendship, nature, gender, shininess. - Custom tag battle support (teaming up an NPC in a double battle). @@ -94,7 +94,7 @@ Based off RHH's pokeemerald-expansion v1.8.0 https://github.com/rh-hideout/pokee - All gender differences. - Custom female icons for female Hippopotas Hippowdon, Pikachu and Wobbufett - 3 Perfect IVs on Legendaries, Mythicals and Ultra Beasts. -- ***Customizable form change tables. Full list of methods [here](/include/constants/form_change_types.h).*** +- ***Customizable form change tables. Full list of methods [here](include/constants/form_change_types.h).*** - Item holding (eg. Giratina/Arceus) - Item using (eg. Oricorio) - Time of day option for Shaymin @@ -115,7 +115,7 @@ Based off RHH's pokeemerald-expansion v1.8.0 https://github.com/rh-hideout/pokee - Egg Move Transfer, including Mirror Herb (configurable). - Nature inheriting 100% of the time with Everstone (configurable) - Gen6+ Ability inheriting (configurable). -- ***Items from newer Generations. Full list [here](/include/constants/items.h).*** +- ***Items from newer Generations. Full list [here](include/constants/items.h).*** - ***Gen 6+ Exp. Share*** (configurable) - Berserk Gene - Most battle items from Gen 4+ From 4ce8a05d5d69a6a1bc93229736431ed1ae2d5266 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 6 May 2024 13:59:37 -0400 Subject: [PATCH 69/71] Add tests to complement PR4500 (#4511) --- test/battle/ability/beads_of_ruin.c | 44 ++++++++++++++++++++ test/battle/ability/costar.c | 8 ++++ test/battle/ability/supreme_overlord.c | 57 +++++++++++++++++++++++++- test/battle/ability/sword_of_ruin.c | 44 ++++++++++++++++++++ test/battle/ability/tablets_of_ruin.c | 44 ++++++++++++++++++++ test/battle/ability/vessel_of_ruin.c | 44 ++++++++++++++++++++ test/battle/ability/zero_to_hero.c | 46 +++++++++++++++++++++ 7 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 test/battle/ability/costar.c diff --git a/test/battle/ability/beads_of_ruin.c b/test/battle/ability/beads_of_ruin.c index 1b2eb2a522..2fc4f9cdc1 100644 --- a/test/battle/ability/beads_of_ruin.c +++ b/test/battle/ability/beads_of_ruin.c @@ -29,3 +29,47 @@ SINGLE_BATTLE_TEST("Beads of Ruin reduces Sp. Def if opposing mon's ability does EXPECT_MUL_EQ(damage[1], Q_4_12(1.33), damage[0]); } } + +SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battlers fainted - Player") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET) { HP(1);} + PLAYER(SPECIES_CHI_YU); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_RUINATION); } + } SCENE { + HP_BAR(opponent, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); + // Everyone faints. + MESSAGE("Go! Chi-Yu!"); + ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN); + MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battlers fainted - Opponent") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1);} + OPPONENT(SPECIES_CHI_YU); + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_RUINATION); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player); + // Everyone faints. + MESSAGE("Go! Wobbuffet!"); + MESSAGE("2 sent out Chi-Yu!"); + ABILITY_POPUP(opponent, ABILITY_BEADS_OF_RUIN); + MESSAGE("Foe Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); + } +} diff --git a/test/battle/ability/costar.c b/test/battle/ability/costar.c new file mode 100644 index 0000000000..7674ab0966 --- /dev/null +++ b/test/battle/ability/costar.c @@ -0,0 +1,8 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Costar copies an ally's stat stages upon entering battle"); + +// Copy from Ruin ability tests +TO_DO_BATTLE_TEST("Costar's message displays correctly after all battlers fainted - Player"); +TO_DO_BATTLE_TEST("Costar's message displays correctly after all battlers fainted - Opponent"); diff --git a/test/battle/ability/supreme_overlord.c b/test/battle/ability/supreme_overlord.c index a01a5d6306..5d24850245 100644 --- a/test/battle/ability/supreme_overlord.c +++ b/test/battle/ability/supreme_overlord.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/battle.h" -DOUBLE_BATTLE_TEST("Supreme Overlord boosts Attack by an additive 10% per fainted mon on the side", s16 damage) +DOUBLE_BATTLE_TEST("Supreme Overlord boosts Attack by an additive 10% per fainted mon on its side upon switch in", s16 damage) { bool32 switchMon = 0; PARAMETRIZE { switchMon = FALSE; } @@ -21,6 +21,10 @@ DOUBLE_BATTLE_TEST("Supreme Overlord boosts Attack by an additive 10% per fainte TURN { SWITCH(playerLeft, 0); } TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); } } SCENE { + if (switchMon) { + ABILITY_POPUP(playerLeft, ABILITY_SUPREME_OVERLORD); + MESSAGE("Kingambit gained strength from the fallen!"); + } ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft); HP_BAR(opponentLeft, captureDamage: &results[i].damage); } FINALLY { @@ -51,6 +55,8 @@ DOUBLE_BATTLE_TEST("Supreme Overlord's boost caps at a 1.5x multipler", s16 dama TURN { SWITCH(playerRight, 3); } TURN { MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } } SCENE { + ABILITY_POPUP(playerRight, ABILITY_SUPREME_OVERLORD); + MESSAGE("Kingambit gained strength from the fallen!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); HP_BAR(opponentLeft, captureDamage: &results[i].damage); } FINALLY { @@ -85,3 +91,52 @@ SINGLE_BATTLE_TEST("Supreme Overlord does not boost attack if party members are EXPECT_EQ(results[0].damage, results[1].damage); } } + +SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all battlers fainted - Player") +{ + // For some reason the Ability Pop Up fails to appear after Explosion. + KNOWN_FAILING; + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET) { HP(1);} + PLAYER(SPECIES_KINGAMBIT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_KOWTOW_CLEAVE); } + } SCENE { + HP_BAR(opponent, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); + // Everyone faints. + MESSAGE("Go! Kingambit!"); + ABILITY_POPUP(player, ABILITY_SUPREME_OVERLORD); + MESSAGE("Kingambit gained strength from the fallen!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all battlers fainted - Opponent") +{ + // For some reason the Ability Pop Up fails to appear after Explosion. + KNOWN_FAILING; + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1);} + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_KINGAMBIT); + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { SWITCH(opponent, 2); MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player); + // Everyone faints. + MESSAGE("Go! Wobbuffet!"); + MESSAGE("2 sent out Kingambit!"); + ABILITY_POPUP(opponent, ABILITY_SUPREME_OVERLORD); + MESSAGE("Foe Kingambit gained strength from the fallen!"); + } +} diff --git a/test/battle/ability/sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c index c6c1ffc4b2..329031f68d 100644 --- a/test/battle/ability/sword_of_ruin.c +++ b/test/battle/ability/sword_of_ruin.c @@ -29,3 +29,47 @@ SINGLE_BATTLE_TEST("Sword of Ruin reduces Defense if opposing mon's ability does EXPECT_MUL_EQ(damage[1], Q_4_12(1.33), damage[0]); } } + +SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battlers fainted - Player") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET) { HP(1);} + PLAYER(SPECIES_CHIEN_PAO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_RUINATION); } + } SCENE { + HP_BAR(opponent, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); + // Everyone faints. + MESSAGE("Go! Chien-Pao!"); + ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN); + MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battlers fainted - Opponent") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1);} + OPPONENT(SPECIES_CHIEN_PAO); + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_RUINATION); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player); + // Everyone faints. + MESSAGE("Go! Wobbuffet!"); + MESSAGE("2 sent out Chien-Pao!"); + ABILITY_POPUP(opponent, ABILITY_SWORD_OF_RUIN); + MESSAGE("Foe Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); + } +} diff --git a/test/battle/ability/tablets_of_ruin.c b/test/battle/ability/tablets_of_ruin.c index d1f9cf3d2e..abdaab7563 100644 --- a/test/battle/ability/tablets_of_ruin.c +++ b/test/battle/ability/tablets_of_ruin.c @@ -29,3 +29,47 @@ SINGLE_BATTLE_TEST("Tablets of Ruin reduces Attack if opposing mon's ability doe EXPECT_MUL_EQ(damage[0], Q_4_12(1.33), damage[1]); } } + +SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battlers fainted - Player") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET) { HP(1);} + PLAYER(SPECIES_WO_CHIEN); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_RUINATION); } + } SCENE { + HP_BAR(opponent, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); + // Everyone faints. + MESSAGE("Go! Wo-Chien!"); + ABILITY_POPUP(player, ABILITY_TABLETS_OF_RUIN); + MESSAGE("Wo-Chien's Tablets of Ruin weakened the Attack of all surrounding Pokémon!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battlers fainted - Opponent") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1);} + OPPONENT(SPECIES_WO_CHIEN); + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_RUINATION); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player); + // Everyone faints. + MESSAGE("Go! Wobbuffet!"); + MESSAGE("2 sent out Wo-Chien!"); + ABILITY_POPUP(opponent, ABILITY_TABLETS_OF_RUIN); + MESSAGE("Foe Wo-Chien's Tablets of Ruin weakened the Attack of all surrounding Pokémon!"); + } +} diff --git a/test/battle/ability/vessel_of_ruin.c b/test/battle/ability/vessel_of_ruin.c index 4d369df691..ce8eae4dab 100644 --- a/test/battle/ability/vessel_of_ruin.c +++ b/test/battle/ability/vessel_of_ruin.c @@ -29,3 +29,47 @@ SINGLE_BATTLE_TEST("Vessel of Ruin reduces Sp. Atk if opposing mon's ability doe EXPECT_MUL_EQ(damage[0], Q_4_12(1.33), damage[1]); } } + +SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battlers fainted - Player") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET) { HP(1);} + PLAYER(SPECIES_TING_LU); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_RUINATION); } + } SCENE { + HP_BAR(opponent, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); + // Everyone faints. + MESSAGE("Go! Ting-Lu!"); + ABILITY_POPUP(player, ABILITY_VESSEL_OF_RUIN); + MESSAGE("Ting-Lu's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battlers fainted - Opponent") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1);} + OPPONENT(SPECIES_TING_LU); + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_RUINATION); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player); + // Everyone faints. + MESSAGE("Go! Wobbuffet!"); + MESSAGE("2 sent out Ting-Lu!"); + ABILITY_POPUP(opponent, ABILITY_VESSEL_OF_RUIN); + MESSAGE("Foe Ting-Lu's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!"); + } +} diff --git a/test/battle/ability/zero_to_hero.c b/test/battle/ability/zero_to_hero.c index 4a186661a6..b4e64d9357 100644 --- a/test/battle/ability/zero_to_hero.c +++ b/test/battle/ability/zero_to_hero.c @@ -136,6 +136,52 @@ SINGLE_BATTLE_TEST("Imposter doesn't apply the heroic transformation message whe } THEN { EXPECT_EQ(player->species, SPECIES_PALAFIN_HERO); } } +SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers fainted - Player") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_PALAFIN_ZERO); + PLAYER(SPECIES_WOBBUFFET) { HP(1);} + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FLIP_TURN); SEND_OUT(player, 1); } + TURN { MOVE(opponent, MOVE_EXPLOSION); SEND_OUT(player, 0); SEND_OUT(opponent, 1); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); + // Everyone faints. + MESSAGE("Go! Palafin!"); + ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO); + MESSAGE("Palafin underwent a heroic transformation!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers fainted - Opponent") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PALAFIN_ZERO); + OPPONENT(SPECIES_WOBBUFFET) { HP(1);} + } WHEN { + TURN { MOVE(opponent, MOVE_FLIP_TURN); SEND_OUT(opponent, 1); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 0); } + TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(player, hp: 0); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, player); + // Everyone faints. + MESSAGE("Go! Wobbuffet!"); + MESSAGE("2 sent out Palafin!"); + ABILITY_POPUP(opponent, ABILITY_ZERO_TO_HERO); + MESSAGE("Foe Palafin underwent a heroic transformation!"); + } +} + // Write Trace test and move this one to that file (including every other ability that can't be copied) SINGLE_BATTLE_TEST("Zero to Hero cannot be copied by Trace") { From 7e43916e57ca98a077ac3ac46acde1ca0fb0d5b4 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 6 May 2024 14:44:37 -0400 Subject: [PATCH 70/71] Version 1.8.3 (#4499) * Moved changelogs to folders by minor version * Version 1.8.3 --- .../ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 3 +- .../ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 3 +- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 3 +- CHANGELOG.md | 51 +++---- README.md | 2 +- docs/changelogs/{ => 0.9.x}/0.9.0.md | 0 docs/changelogs/{ => 1.0.x}/1.0.0.md | 0 docs/changelogs/{ => 1.1.x}/1.1.0.md | 0 docs/changelogs/{ => 1.1.x}/1.1.1.md | 0 docs/changelogs/{ => 1.2.x}/1.2.0.md | 0 docs/changelogs/{ => 1.3.x}/1.3.0.md | 0 docs/changelogs/{ => 1.4.x}/1.4.0.md | 0 docs/changelogs/{ => 1.4.x}/1.4.1.md | 0 docs/changelogs/{ => 1.4.x}/1.4.2.md | 0 docs/changelogs/{ => 1.4.x}/1.4.3.md | 0 docs/changelogs/{ => 1.5.x}/1.5.0.md | 0 docs/changelogs/{ => 1.5.x}/1.5.1.md | 0 docs/changelogs/{ => 1.5.x}/1.5.2.md | 0 docs/changelogs/{ => 1.5.x}/1.5.3.md | 0 docs/changelogs/{ => 1.6.x}/1.6.0.md | 0 docs/changelogs/{ => 1.6.x}/1.6.1.md | 0 docs/changelogs/{ => 1.6.x}/1.6.2.md | 0 docs/changelogs/{ => 1.7.x}/1.7.0.md | 0 docs/changelogs/{ => 1.7.x}/1.7.1.md | 0 docs/changelogs/{ => 1.7.x}/1.7.2.md | 0 docs/changelogs/{ => 1.7.x}/1.7.3.md | 0 docs/changelogs/{ => 1.7.x}/1.7.4.md | 0 docs/changelogs/{ => 1.8.x}/1.8.0.md | 0 docs/changelogs/{ => 1.8.x}/1.8.1.md | 0 docs/changelogs/{ => 1.8.x}/1.8.2.md | 0 docs/changelogs/1.8.x/1.8.3.md | 132 ++++++++++++++++++ docs/changelogs/template.md | 4 +- include/constants/expansion.h | 6 +- 33 files changed, 170 insertions(+), 34 deletions(-) rename docs/changelogs/{ => 0.9.x}/0.9.0.md (100%) rename docs/changelogs/{ => 1.0.x}/1.0.0.md (100%) rename docs/changelogs/{ => 1.1.x}/1.1.0.md (100%) rename docs/changelogs/{ => 1.1.x}/1.1.1.md (100%) rename docs/changelogs/{ => 1.2.x}/1.2.0.md (100%) rename docs/changelogs/{ => 1.3.x}/1.3.0.md (100%) rename docs/changelogs/{ => 1.4.x}/1.4.0.md (100%) rename docs/changelogs/{ => 1.4.x}/1.4.1.md (100%) rename docs/changelogs/{ => 1.4.x}/1.4.2.md (100%) rename docs/changelogs/{ => 1.4.x}/1.4.3.md (100%) rename docs/changelogs/{ => 1.5.x}/1.5.0.md (100%) rename docs/changelogs/{ => 1.5.x}/1.5.1.md (100%) rename docs/changelogs/{ => 1.5.x}/1.5.2.md (100%) rename docs/changelogs/{ => 1.5.x}/1.5.3.md (100%) rename docs/changelogs/{ => 1.6.x}/1.6.0.md (100%) rename docs/changelogs/{ => 1.6.x}/1.6.1.md (100%) rename docs/changelogs/{ => 1.6.x}/1.6.2.md (100%) rename docs/changelogs/{ => 1.7.x}/1.7.0.md (100%) rename docs/changelogs/{ => 1.7.x}/1.7.1.md (100%) rename docs/changelogs/{ => 1.7.x}/1.7.2.md (100%) rename docs/changelogs/{ => 1.7.x}/1.7.3.md (100%) rename docs/changelogs/{ => 1.7.x}/1.7.4.md (100%) rename docs/changelogs/{ => 1.8.x}/1.8.0.md (100%) rename docs/changelogs/{ => 1.8.x}/1.8.1.md (100%) rename docs/changelogs/{ => 1.8.x}/1.8.2.md (100%) create mode 100644 docs/changelogs/1.8.x/1.8.3.md diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 24b57d117e..cdffd6838c 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.2 (Latest release) + - 1.8.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.8.2 - 1.8.1 - 1.8.0 - 1.7.4 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 562468cce1..bf31df9931 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.8.2 (Latest release) + - 1.8.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 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 4658ecfb69..6920d34a83 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.8.2 (Latest release) + - 1.8.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.8.2 - 1.8.1 - 1.8.0 - 1.7.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index dea05701d5..8aeeac5dec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,46 +1,47 @@ # Pokeemerald-Expansion Changelogs ## 1.8.x -- ### [Version 1.8.2](docs/changelogs/1.8.2.md) - Bugfix Release 🧹 -- ### [Version 1.8.1](docs/changelogs/1.8.1.md) - HOTFIX Release 🔥 -- ### [Version 1.8.0](docs/changelogs/1.8.0.md) - Feature 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.4.md) - Bugfix Release 🧹 -- ### [Version 1.7.3](docs/changelogs/1.7.3.md) - Bugfix Release 🧹 -- ### [Version 1.7.2](docs/changelogs/1.7.2.md) - Bugfix Release 🧹 -- ### [Version 1.7.1](docs/changelogs/1.7.1.md) - Bugfix Release 🧹 -- ### [Version 1.7.0](docs/changelogs/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.2.md) - Bugfix Release 🧹 -- ### [Version 1.6.1](docs/changelogs/1.6.1.md) - HOTFIX Release 🔥 -- ### [Version 1.6.0](docs/changelogs/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.3.md) - HOTFIX Release 🔥 -- ### [Version 1.5.2](docs/changelogs/1.5.2.md) - Bugfix Release 🧹 -- ### [Version 1.5.1](docs/changelogs/1.5.1.md) - Bugfix Release 🧹 -- ### [Version 1.5.0](docs/changelogs/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.3.md) - Bugfix Release 🧹 -- ### [Version 1.4.2](docs/changelogs/1.4.2.md) - Bugfix Release 🧹 -- ### [Version 1.4.1](docs/changelogs/1.4.1.md) - HOTFIX Release 🔥 -- ### [Version 1.4.0](docs/changelogs/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.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.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.1.md) - Bugfix Release 🧹 -- ### [Version 1.1.0](docs/changelogs/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.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.0.md) - Retroactive Version 🦕 +- ### [Version 0.9.0](docs/changelogs/0.9.x/0.9.0.md) - Retroactive Version 🦕 diff --git a/README.md b/README.md index d29cd7bede..855ddc7b7f 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ With this, you'll get the latest version of pokeemerald-expansion, plus a couple - Check your current version. - You can check in the debug menu's `Utilities -> Expansion Version` option. - If the option is not available, you possibly have version 1.6.2 or older. In that case, please check the [changelogs](CHANGELOG.md) to determine your version based on the features available on your repository. -- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.8.2, use `git pull RHH expansion/1.8.2`). +- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.8.3, use `git pull RHH expansion/1.8.3`). - ***Important:*** If you are several versions behind, we recommend updating one minor version at a time, skipping directly to the latest patch version (eg, 1.5.3 -> 1.6.2 -> 1.7.4 and so on) - Alternatively, you can update to unreleased versions of the expansion. - ***master (stable):*** It contains unreleased **bugfixes** that will come in the next patch version. To merge, use `git pull RHH master`. diff --git a/docs/changelogs/0.9.0.md b/docs/changelogs/0.9.x/0.9.0.md similarity index 100% rename from docs/changelogs/0.9.0.md rename to docs/changelogs/0.9.x/0.9.0.md diff --git a/docs/changelogs/1.0.0.md b/docs/changelogs/1.0.x/1.0.0.md similarity index 100% rename from docs/changelogs/1.0.0.md rename to docs/changelogs/1.0.x/1.0.0.md diff --git a/docs/changelogs/1.1.0.md b/docs/changelogs/1.1.x/1.1.0.md similarity index 100% rename from docs/changelogs/1.1.0.md rename to docs/changelogs/1.1.x/1.1.0.md diff --git a/docs/changelogs/1.1.1.md b/docs/changelogs/1.1.x/1.1.1.md similarity index 100% rename from docs/changelogs/1.1.1.md rename to docs/changelogs/1.1.x/1.1.1.md diff --git a/docs/changelogs/1.2.0.md b/docs/changelogs/1.2.x/1.2.0.md similarity index 100% rename from docs/changelogs/1.2.0.md rename to docs/changelogs/1.2.x/1.2.0.md diff --git a/docs/changelogs/1.3.0.md b/docs/changelogs/1.3.x/1.3.0.md similarity index 100% rename from docs/changelogs/1.3.0.md rename to docs/changelogs/1.3.x/1.3.0.md diff --git a/docs/changelogs/1.4.0.md b/docs/changelogs/1.4.x/1.4.0.md similarity index 100% rename from docs/changelogs/1.4.0.md rename to docs/changelogs/1.4.x/1.4.0.md diff --git a/docs/changelogs/1.4.1.md b/docs/changelogs/1.4.x/1.4.1.md similarity index 100% rename from docs/changelogs/1.4.1.md rename to docs/changelogs/1.4.x/1.4.1.md diff --git a/docs/changelogs/1.4.2.md b/docs/changelogs/1.4.x/1.4.2.md similarity index 100% rename from docs/changelogs/1.4.2.md rename to docs/changelogs/1.4.x/1.4.2.md diff --git a/docs/changelogs/1.4.3.md b/docs/changelogs/1.4.x/1.4.3.md similarity index 100% rename from docs/changelogs/1.4.3.md rename to docs/changelogs/1.4.x/1.4.3.md diff --git a/docs/changelogs/1.5.0.md b/docs/changelogs/1.5.x/1.5.0.md similarity index 100% rename from docs/changelogs/1.5.0.md rename to docs/changelogs/1.5.x/1.5.0.md diff --git a/docs/changelogs/1.5.1.md b/docs/changelogs/1.5.x/1.5.1.md similarity index 100% rename from docs/changelogs/1.5.1.md rename to docs/changelogs/1.5.x/1.5.1.md diff --git a/docs/changelogs/1.5.2.md b/docs/changelogs/1.5.x/1.5.2.md similarity index 100% rename from docs/changelogs/1.5.2.md rename to docs/changelogs/1.5.x/1.5.2.md diff --git a/docs/changelogs/1.5.3.md b/docs/changelogs/1.5.x/1.5.3.md similarity index 100% rename from docs/changelogs/1.5.3.md rename to docs/changelogs/1.5.x/1.5.3.md diff --git a/docs/changelogs/1.6.0.md b/docs/changelogs/1.6.x/1.6.0.md similarity index 100% rename from docs/changelogs/1.6.0.md rename to docs/changelogs/1.6.x/1.6.0.md diff --git a/docs/changelogs/1.6.1.md b/docs/changelogs/1.6.x/1.6.1.md similarity index 100% rename from docs/changelogs/1.6.1.md rename to docs/changelogs/1.6.x/1.6.1.md diff --git a/docs/changelogs/1.6.2.md b/docs/changelogs/1.6.x/1.6.2.md similarity index 100% rename from docs/changelogs/1.6.2.md rename to docs/changelogs/1.6.x/1.6.2.md diff --git a/docs/changelogs/1.7.0.md b/docs/changelogs/1.7.x/1.7.0.md similarity index 100% rename from docs/changelogs/1.7.0.md rename to docs/changelogs/1.7.x/1.7.0.md diff --git a/docs/changelogs/1.7.1.md b/docs/changelogs/1.7.x/1.7.1.md similarity index 100% rename from docs/changelogs/1.7.1.md rename to docs/changelogs/1.7.x/1.7.1.md diff --git a/docs/changelogs/1.7.2.md b/docs/changelogs/1.7.x/1.7.2.md similarity index 100% rename from docs/changelogs/1.7.2.md rename to docs/changelogs/1.7.x/1.7.2.md diff --git a/docs/changelogs/1.7.3.md b/docs/changelogs/1.7.x/1.7.3.md similarity index 100% rename from docs/changelogs/1.7.3.md rename to docs/changelogs/1.7.x/1.7.3.md diff --git a/docs/changelogs/1.7.4.md b/docs/changelogs/1.7.x/1.7.4.md similarity index 100% rename from docs/changelogs/1.7.4.md rename to docs/changelogs/1.7.x/1.7.4.md diff --git a/docs/changelogs/1.8.0.md b/docs/changelogs/1.8.x/1.8.0.md similarity index 100% rename from docs/changelogs/1.8.0.md rename to docs/changelogs/1.8.x/1.8.0.md diff --git a/docs/changelogs/1.8.1.md b/docs/changelogs/1.8.x/1.8.1.md similarity index 100% rename from docs/changelogs/1.8.1.md rename to docs/changelogs/1.8.x/1.8.1.md diff --git a/docs/changelogs/1.8.2.md b/docs/changelogs/1.8.x/1.8.2.md similarity index 100% rename from docs/changelogs/1.8.2.md rename to docs/changelogs/1.8.x/1.8.2.md diff --git a/docs/changelogs/1.8.x/1.8.3.md b/docs/changelogs/1.8.x/1.8.3.md new file mode 100644 index 0000000000..32fb098a13 --- /dev/null +++ b/docs/changelogs/1.8.x/1.8.3.md @@ -0,0 +1,132 @@ +# Version 1.8.3 + +```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.Y.Z`. +``` + +## 💥 *Softlock/Crash fixes* 💥 +* Fixed AI bug that caused an infinite loop when player mon has only status moves by @Pawkkie and @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4403 +* Temporarely disabled `AI_FLAG_SMART_MON_CHOICES` flag in double battles to prevent a softlock by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4405 +* Fixed debug option "Fill PC Boxes Fast" softlocking the game by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/4428 +* Fixed infinite loop caused by Leftovers with `AI_FLAG_SMART_MON_CHOICES` by @Pokabbie and @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4453 + +## 🧬 General 🧬 +### Changed +* Updated outdated macOS instructions in `INSTALL.md` by @jschoeny in https://github.com/rh-hideout/pokeemerald-expansion/pull/4407 +* Made filepath links in `README.md` relative by @lolbinarycat in https://github.com/rh-hideout/pokeemerald-expansion/pull/4509 +### Fixed +* Fixed abilities not being properly inherited via breeding for species/abilities with IDs over 255 by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4411 +* Fixed Hard Level Caps issues by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4420 + * Fixed Pokémon being able to surpass cap if they gained enough experience at once. + * Fixed Exp. Candies ignoring hard caps. + * Fixed Pokémon gaining 1 experience if they are at the level cap. +* Fixed evolution tracker issues by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4503 + * `EVO_LEVEL_MOVE_TWENTY_TIMES` no longer increases with every move. + * `EVO_LEVEL_RECOIL_DAMAGE_MALE/FEMALE` is no longer updated twice than intended. + +## ✨ Feature Branches ✨ +### ***TheXaman's HGSS Pokédex Plus***: +#### Fixed +* Fixed Pokémon data page not properly handling `u16` Exp. Yields by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4432 + * Cleanup by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4445 + +## 🐉 Pokémon 🐉 +### Changed +* Further Gen 9 Pokémon icon improvements by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4413 + * Original sprites by [ezerart on DeviantArt](https://www.deviantart.com/ezerart/art/Pokemon-Gen-9-Icon-sprites-3DS-Style-944211258). Palette assignments and 2nd frames by kittenchilly. + * Cyclizar, Dipplin, Farigiraf, Glimmet, Glimmora, Greavard, Iron Moth, Revavroom, Tadbulb, Paldean Tauros (all three of them), Tinkaton and Paldean Wooper. +* Further Gen 9 Pokémon front/back sprite improvements by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4414 + * Source: [PokéCommunity](https://www.pokecommunity.com/threads/ds-style-gen-vii-and-beyond-pok%C3%A9mon-sprite-repository-in-64x64.368703/post-10814369) + * Dolliv, Arboliva, Charcadet, Mabosstiff, Tinkatink, Tinkatuff, Tinkaton and Chien-Pao. +### Fixed +* Fixed Lycanroc Dusk and Midnight forms using Midday's backsprite by @Eemeliri in https://github.com/rh-hideout/pokeemerald-expansion/pull/4430 +* Fixed Tatsugiri Droopy back sprite palette by @cafei-uh in https://github.com/rh-hideout/pokeemerald-expansion/pull/4455 + +## ⚔️ Battle General ⚔️ ## +### Changed +* Turned `B_RESTORE_HELD_BATTLE_ITEMS` into a generational config by @LOuroboros in https://github.com/rh-hideout/pokeemerald-expansion/pull/4402 +### Fixed +* Fixed battle Partner trainer class and potential OOB-related issues by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4422 + +## 🤹 Moves 🤹 +### Fixed +* Fixed Quash not properly working when `B_RECALC_TURN_AFTER_ACTIONS` is set to `GEN_8` or greater by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4419 +* Fixed Will-O-Wisp's name capitalization in gMovesInfo by @LOuroboros in https://github.com/rh-hideout/pokeemerald-expansion/pull/4425 +* Fixed Thunder Cage not printing the right battle message by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4426 +* Fixed Diamond Storm missing its effect by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4489 +* Fixed Mortal Spin's description by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4489 +* Fixed Assist being able to call `MOVE_NONE` by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4491 + +## 🎭 Abilities 🎭 +### Changed +* Changed the Embody Aspect defines to use the full form name, to be in line with the species define by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4439 + * Eg. `ABILITY_EMBODY_ASPECT_TEAL` -> `ABILITY_EMBODY_ASPECT_TEAL_MASK`. +### Fixed +* Fixed Intimidate/Supersweet Syrup playing their animation and not printing the right message for battlers at -6 stage Attack/Evasion by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4406 +* Fixed ability pop ups not respecting `abilityPopupOverwrite` in tests by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4423 +* Fixed Dancer copying multi-target moves by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4437 +* Fixed Hospitality triggering on a fainted mon by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4450 +* Fixed Embody Aspect (Teal Mask) boosting Sp. Attack instead of Speed by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4439 +* Fixed lingering long ability popup names by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4456 +* Fixed Fur Coat not being skipped by Mold Breaker abilities by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4459 +* Fixed Color change not being triggered by Future Sight or Doom Desire by @AlexOn1ine and @hedara90 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4472 +* Fixes Magic Bounce only working for battlers in certain positions by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4464 +* Fixed Costar, Zero to Hero, Supreme Overlord and Ruin abilities' switch-in battle messages showing sometimes incorrectly by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4500 + * Cleanup by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4511 + +## 🧶 Items 🧶 +### Fixed +* Fixed typo in Rotom Catalogue description by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/4433 +* Eject item fixes (Eject Button/Pack): + * Fixed regression from 1.8.2 that caused Eject Pack to not trigger upon self-inflicted stat decreases by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4463 + * Fixed Eject Items causing wrong Pokémon taking damage by entry hazards by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/4465 + * Cleanup by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4497 +* Fixed Flute Items being consumed in battle by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4496 +* Fixed Red Card activating if the holder was switched in in the same turn (eg. via Endure) by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4482 + +## 🤖 Battle AI 🤖 +### Changed +* `AI_FLAG_SMART_SWITCHING` flag now automatically sets `AI_FLAG_SMART_MON_CHOICES` as well by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4405 + * Cleanup by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4409 +* Corrected `IsDamageMoveUsable`'s name to `IsDamageMoveUnusable` by @WillKolada in https://github.com/rh-hideout/pokeemerald-expansion/pull/4476 +* Fixed AI not knowing that Steam Roller fails when there's no terrain by @WillKolada in https://github.com/rh-hideout/pokeemerald-expansion/pull/4476 + * Cleanup by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4498 + +## 🧪 Test Runner 🧪 +### Added +* Added missing Color change tests by @hedara90 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4472 +### Changed +### Fixed +* Fixed AI test error messages by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/4404 + * The error messages for `EXPECT_MOVE` and `EXPECT_SWITCH` were backwards, saying, e.g. `Expected MOVE, got SWITCH` when it should say `Expected SWITCH, got MOVE`. +* Fixed typos in Embody Aspect tests by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4439 +* Fixed Battle Test organization by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4504 + * Fixed Cud Chew's file being in the move effect folder. + * Fixed Cud Chew's file name and test names (Cud Chuw). + * Fixed Gastro Acid and Role Play's files being in the ability folder. + * Moved Recoil file to move flags folder. + * Renamed White Herb's file to `restore_stats.c`. + * Renamed Techno Blast's file to `change_type_on_item.c`. + * `semi_invulnerable_moves.c` to `semi_invulnerable.c`. + * `two_turn_moves.c` to `two_turns_attack.c`. + * Combined Burn Up/Double Shock to a single file `fail_if_not_arg_type.c` + * Added Spit Up/Swallow files that point Stockpile's file. + * Moved secondary effect files to their own folder. + * Combinations of secondary effects moved to their own folder + * Split `hit_set_entry_hazards.c` to separate files for Spikes/Stealth Rock. + * Grouped Hex/Venoshock to the same file `double_power_on_arg_status.c` + +## New Contributors +* @jschoeny made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4407 +* @Eemeliri made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4430 +* @cafei-uh made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4455 +* @PhallenTree made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4465 +* @WillKolada made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4476 +* @cawtds made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4503 +* @lolbinarycat made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4509 + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.2...expansion/1.8.3 + + diff --git a/docs/changelogs/template.md b/docs/changelogs/template.md index 6d1ce784a2..b70a44ce54 100644 --- a/docs/changelogs/template.md +++ b/docs/changelogs/template.md @@ -7,7 +7,7 @@ ``` ## 🌋 *IMPORTANT CHANGES* 🌋 -* We deleted the whole repo LOL by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3367 +* N/A ## 💥 *Softlock/Crash fixes* 💥 * N/A @@ -119,7 +119,7 @@ ## New Contributors -* Tony +* N/A **Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.Y.Z...expansion/1.Y.Z diff --git a/include/constants/expansion.h b/include/constants/expansion.h index b72429d619..55ebb01b78 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -1,13 +1,13 @@ #ifndef GUARD_CONSTANTS_EXPANSION_H #define GUARD_CONSTANTS_EXPANSION_H -// 1.8.2 +// 1.8.3 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 8 -#define EXPANSION_VERSION_PATCH 2 +#define EXPANSION_VERSION_PATCH 3 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE FALSE +#define EXPANSION_TAGGED_RELEASE TRUE #endif From eca47fe209ee592b4b07145e33983c40cd515202 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 6 May 2024 14:51:48 -0400 Subject: [PATCH 71/71] Non-tagged release --- docs/changelogs/1.8.x/1.8.3.md | 2 +- include/constants/expansion.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelogs/1.8.x/1.8.3.md b/docs/changelogs/1.8.x/1.8.3.md index 32fb098a13..8449f56383 100644 --- a/docs/changelogs/1.8.x/1.8.3.md +++ b/docs/changelogs/1.8.x/1.8.3.md @@ -3,7 +3,7 @@ ```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.Y.Z`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.8.3`. ``` ## 💥 *Softlock/Crash fixes* 💥 diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 55ebb01b78..e1e228fd9b 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -8,6 +8,6 @@ // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE TRUE +#define EXPANSION_TAGGED_RELEASE FALSE #endif