From cf85abc270dfb3ea6e1409676f11de040247164b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 26 Jun 2025 16:42:12 -0400 Subject: [PATCH 01/25] Remove unused messages --- include/constants/battle_string_ids.h | 10 ---------- src/battle_message.c | 12 +----------- src/battle_tv.c | 2 -- test/text.c | 3 --- 4 files changed, 1 insertion(+), 26 deletions(-) diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 57cf6f70214b..4bb3d5494ca5 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -62,11 +62,8 @@ enum StringID STRINGID_PKMNISPARALYZED, STRINGID_PKMNISALREADYPARALYZED, STRINGID_PKMNHEALEDPARALYSIS, - STRINGID_PKMNDREAMEATEN, STRINGID_STATSWONTINCREASE, STRINGID_STATSWONTDECREASE, - STRINGID_TEAMSTOPPEDWORKING, - STRINGID_FOESTOPPEDWORKING, STRINGID_PKMNISCONFUSED, STRINGID_PKMNHEALEDCONFUSION, STRINGID_PKMNWASCONFUSED, @@ -74,7 +71,6 @@ enum StringID STRINGID_PKMNFELLINLOVE, STRINGID_PKMNINLOVE, STRINGID_PKMNIMMOBILIZEDBYLOVE, - STRINGID_PKMNBLOWNAWAY, STRINGID_PKMNCHANGEDTYPE, STRINGID_PKMNFLINCHED, STRINGID_PKMNREGAINEDHEALTH, @@ -180,7 +176,6 @@ enum StringID STRINGID_PKMNREADYTOHELP, STRINGID_PKMNSWITCHEDITEMS, STRINGID_PKMNCOPIEDFOE, - STRINGID_PKMNMADEWISH, STRINGID_PKMNWISHCAMETRUE, STRINGID_PKMNPLANTEDROOTS, STRINGID_PKMNABSORBEDNUTRIENTS, @@ -251,7 +246,6 @@ enum StringID STRINGID_HAILSTOPPED, STRINGID_FAILEDTOSPITUP, STRINGID_FAILEDTOSWALLOW, - STRINGID_WINDBECAMEHEATWAVE, STRINGID_STATCHANGESGONE, STRINGID_COINSSCATTERED, STRINGID_TOOWEAKFORSUBSTITUTE, @@ -327,7 +321,6 @@ enum StringID STRINGID_PLAYERDEFEATEDTRAINER1, STRINGID_SOOTHINGAROMA, STRINGID_ITEMSCANTBEUSEDNOW, - STRINGID_FORXCOMMAYZ, STRINGID_USINGITEMSTATOFPKMNROSE, STRINGID_PKMNUSEDXTOGETPUMPED, STRINGID_PKMNSXMADEYUSELESS, @@ -344,7 +337,6 @@ enum StringID STRINGID_PKMNFLEDUSINGITS, STRINGID_PKMNFLEDUSING, STRINGID_PKMNWASDRAGGEDOUT, - STRINGID_PREVENTEDFROMWORKING, STRINGID_PKMNSITEMNORMALIZEDSTATUS, STRINGID_TRAINER1USEDITEM, STRINGID_BOXISFULL, @@ -355,8 +347,6 @@ enum StringID STRINGID_STATSWONTDECREASE2, STRINGID_PKMNSXBLOCKSY2, STRINGID_PKMNSXWOREOFF, - STRINGID_PKMNRAISEDDEFALITTLE, - STRINGID_PKMNRAISEDSPDEFALITTLE, STRINGID_THEWALLSHATTERED, STRINGID_PKMNSXPREVENTSYSZ, STRINGID_PKMNSXCUREDITSYPROBLEM, diff --git a/src/battle_message.c b/src/battle_message.c index eb44ca96eeb9..b4bef73e4183 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -221,11 +221,8 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNISPARALYZED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} couldn't move because it's paralyzed!"), [STRINGID_PKMNISALREADYPARALYZED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is already paralyzed!"), [STRINGID_PKMNHEALEDPARALYSIS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} was cured of paralysis!"), - [STRINGID_PKMNDREAMEATEN] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s dream was eaten!"), //not in gen 5+, expansion doesn't use anymore [STRINGID_STATSWONTINCREASE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} won't go any higher!"), [STRINGID_STATSWONTDECREASE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1} won't go any lower!"), - [STRINGID_TEAMSTOPPEDWORKING] = COMPOUND_STRING("Your team's {B_BUFF1} stopped working!"), //unused - [STRINGID_FOESTOPPEDWORKING] = COMPOUND_STRING("The foe's {B_BUFF1} stopped working!"), //unused [STRINGID_PKMNISCONFUSED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is confused!"), [STRINGID_PKMNHEALEDCONFUSION] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} snapped out of its confusion!"), [STRINGID_PKMNWASCONFUSED] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX} became confused!"), @@ -233,7 +230,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNFELLINLOVE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} fell in love!"), [STRINGID_PKMNINLOVE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is in love with {B_SCR_NAME_WITH_PREFIX2}!"), [STRINGID_PKMNIMMOBILIZEDBYLOVE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is immobilized by love!"), - [STRINGID_PKMNBLOWNAWAY] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} was blown away!"), //unused [STRINGID_PKMNCHANGEDTYPE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} transformed into the {B_BUFF1} type!"), [STRINGID_PKMNFLINCHED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} flinched and couldn't move!"), [STRINGID_PKMNREGAINEDHEALTH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s HP was restored."), @@ -325,7 +321,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNFLEDFROMBATTLE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} fled from battle!"), [STRINGID_PKMNFORESAWATTACK] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} foresaw an attack!"), [STRINGID_PKMNTOOKATTACK] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} took the {B_BUFF1} attack!"), - [STRINGID_PKMNATTACK] = COMPOUND_STRING("{B_BUFF1}'s attack!"), //not in gen 5+, expansion doesn't use anymore + [STRINGID_PKMNATTACK] = COMPOUND_STRING("{B_BUFF1}'s attack!"), //not in gen 5+ [STRINGID_PKMNCENTERATTENTION] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} became the center of attention!"), [STRINGID_PKMNCHARGINGPOWER] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} began charging power!"), [STRINGID_NATUREPOWERTURNEDINTO] = COMPOUND_STRING("Nature Power turned into {B_CURRENT_MOVE}!"), @@ -339,7 +335,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNREADYTOHELP] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is ready to help {B_DEF_NAME_WITH_PREFIX2}!"), [STRINGID_PKMNSWITCHEDITEMS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} switched items with its target!"), [STRINGID_PKMNCOPIEDFOE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} copied {B_DEF_NAME_WITH_PREFIX2}'s Ability!"), - [STRINGID_PKMNMADEWISH] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} made a wish!"), //unused [STRINGID_PKMNWISHCAMETRUE] = COMPOUND_STRING("{B_BUFF1}'s wish came true!"), [STRINGID_PKMNPLANTEDROOTS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} planted its roots!"), [STRINGID_PKMNABSORBEDNUTRIENTS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} absorbed nutrients with its roots!"), @@ -410,7 +405,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_HAILSTOPPED] = COMPOUND_STRING("The hail stopped."), [STRINGID_FAILEDTOSPITUP] = COMPOUND_STRING("But it failed to spit up a thing!"), //not in gen 5+, uses "but it failed" [STRINGID_FAILEDTOSWALLOW] = COMPOUND_STRING("But it failed to swallow a thing!"), //not in gen 5+, uses "but it failed" - [STRINGID_WINDBECAMEHEATWAVE] = COMPOUND_STRING("The wind turned into a Heat Wave!"), //unused [STRINGID_STATCHANGESGONE] = COMPOUND_STRING("All stat changes were eliminated!"), [STRINGID_COINSSCATTERED] = COMPOUND_STRING("Coins were scattered everywhere!"), [STRINGID_TOOWEAKFORSUBSTITUTE] = COMPOUND_STRING("But it does not have enough HP left to make a substitute!"), @@ -486,7 +480,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PLAYERDEFEATEDTRAINER1] = sText_PlayerDefeatedLinkTrainerTrainer1, [STRINGID_SOOTHINGAROMA] = COMPOUND_STRING("A soothing aroma wafted through the area!"), [STRINGID_ITEMSCANTBEUSEDNOW] = COMPOUND_STRING("Items can't be used now.{PAUSE 64}"), //not in gen 5+, i think - [STRINGID_FORXCOMMAYZ] = COMPOUND_STRING("For {B_SCR_NAME_WITH_PREFIX2}, {B_LAST_ITEM} {B_BUFF1}"), //not in gen 5+, expansion doesn't use anymore [STRINGID_USINGITEMSTATOFPKMNROSE] = COMPOUND_STRING("Using {B_LAST_ITEM}, the {B_BUFF1} of {B_SCR_NAME_WITH_PREFIX2} {B_BUFF2}"), //todo: update this, will require code changes [STRINGID_PKMNUSEDXTOGETPUMPED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} used the {B_LAST_ITEM} to get pumped!"), [STRINGID_PKMNSXMADEYUSELESS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} made {B_CURRENT_MOVE} useless!"), //not in gen 5+, ability popup @@ -503,7 +496,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNFLEDUSINGITS] = COMPOUND_STRING("{PLAY_SE SE_FLEE}{B_ATK_NAME_WITH_PREFIX} fled using its {B_LAST_ITEM}!\p"), [STRINGID_PKMNFLEDUSING] = COMPOUND_STRING("{PLAY_SE SE_FLEE}{B_ATK_NAME_WITH_PREFIX} fled using {B_ATK_ABILITY}!\p"), //not in gen 5+ [STRINGID_PKMNWASDRAGGEDOUT] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} was dragged out!\p"), - [STRINGID_PREVENTEDFROMWORKING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevented {B_SCR_NAME_WITH_PREFIX2}'s {B_BUFF1} from working!"), //unused [STRINGID_PKMNSITEMNORMALIZEDSTATUS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_LAST_ITEM} normalized its status!"), [STRINGID_TRAINER1USEDITEM] = COMPOUND_STRING("{B_ATK_TRAINER_NAME_WITH_CLASS} used {B_LAST_ITEM}!"), [STRINGID_BOXISFULL] = COMPOUND_STRING("The Box is full! You can't catch any more!\p"), @@ -514,8 +506,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_STATSWONTDECREASE2] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s stats won't go any lower!"), [STRINGID_PKMNSXBLOCKSY2] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} blocks {B_CURRENT_MOVE}!"), //not in gen 5+, ability popup [STRINGID_PKMNSXWOREOFF] = COMPOUND_STRING("{B_ATK_TEAM1} team's {B_BUFF1} wore off!"), - [STRINGID_PKMNRAISEDDEFALITTLE] = COMPOUND_STRING("{B_ATK_PREFIX1}'s {B_CURRENT_MOVE} raised DEFENSE a little!"), //expansion doesn't use anymore - [STRINGID_PKMNRAISEDSPDEFALITTLE] = COMPOUND_STRING("{B_ATK_PREFIX1}'s {B_CURRENT_MOVE} raised SP. DEF a little!"), //expansion doesn't use anymore [STRINGID_THEWALLSHATTERED] = COMPOUND_STRING("The wall shattered!"), //not in gen5+, uses "your teams light screen wore off!" etc instead [STRINGID_PKMNSXPREVENTSYSZ] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} prevents {B_DEF_NAME_WITH_PREFIX2}'s {B_DEF_ABILITY} from working!"), [STRINGID_PKMNSXCUREDITSYPROBLEM] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} cured its {B_BUFF1} problem!"), //not in gen 5+, ability popup diff --git a/src/battle_tv.c b/src/battle_tv.c index 4215d3ead3db..37ed42ff473b 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -480,12 +480,10 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId) } break; case STRINGID_PKMNRAISEDDEF: - case STRINGID_PKMNRAISEDDEFALITTLE: tvPtr->side[atkSide].reflectMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].reflectMoveSlot = moveSlot; break; case STRINGID_PKMNRAISEDSPDEF: - case STRINGID_PKMNRAISEDSPDEFALITTLE: tvPtr->side[atkSide].lightScreenMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->side[atkSide].lightScreenMoveSlot = moveSlot; break; diff --git a/test/text.c b/test/text.c index 0a5a4dd2a2fe..dafd651461ec 100644 --- a/test/text.c +++ b/test/text.c @@ -663,8 +663,6 @@ TEST("Battle strings fit on the battle message window") break; // Buffer Move name to B_BUFF1 case STRINGID_PKMNLEARNEDMOVE2: - case STRINGID_TEAMSTOPPEDWORKING: // Unused - case STRINGID_FOESTOPPEDWORKING: // Unused case STRINGID_PKMNHURTBY: case STRINGID_PKMNFREEDFROM: case STRINGID_PKMNMOVEWASDISABLED: @@ -758,7 +756,6 @@ TEST("Battle strings fit on the battle message window") case STRINGID_PKMNCURIOUSABOUTX: case STRINGID_PKMNENTHRALLEDBYX: case STRINGID_PKMNIGNOREDX: - case STRINGID_PREVENTEDFROMWORKING: case STRINGID_PKMNOBTAINEDX: case STRINGID_ABOUTTOUSEPOLTERGEIST: PREPARE_ITEM_BUFFER(gBattleTextBuff1, longItemName); From 57da4617dad11f61bd91c7c01933effd033e1b91 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 26 Jun 2025 17:44:29 -0400 Subject: [PATCH 02/25] Ability status immunity --- data/battle_scripts_1.s | 13 +------------ include/constants/battle_string_ids.h | 17 ----------------- src/battle_arena.c | 3 --- src/battle_message.c | 16 ---------------- src/battle_util.c | 5 ----- test/battle/ability/immunity.c | 2 +- test/battle/ability/limber.c | 2 +- test/battle/ability/pastel_veil.c | 6 +++--- test/battle/ability/water_bubble.c | 2 +- 9 files changed, 7 insertions(+), 59 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index fc18ab1e76ee..d5e0c989e4d4 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2801,14 +2801,6 @@ BattleScript_AromaVeilProtects: setmoveresultflags MOVE_RESULT_FAILED goto BattleScript_MoveEnd -BattleScript_PastelVeilProtects: - pause B_WAIT_TIME_SHORT - call BattleScript_AbilityPopUp - printstring STRINGID_PASTELVEILPROTECTED - waitmessage B_WAIT_TIME_LONG - setmoveresultflags MOVE_RESULT_FAILED - goto BattleScript_MoveEnd - BattleScript_AbilityProtectsDoesntAffectRet:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp @@ -3164,10 +3156,7 @@ BattleScript_AlreadyPoisoned:: BattleScript_ImmunityProtected:: call BattleScript_AbilityPopUp - pause B_WAIT_TIME_SHORT - printfromtable gStatusPreventionStringIds - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + goto BattleScript_ButItFailed BattleScript_EffectAuroraVeil:: attackcanceler diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 4bb3d5494ca5..60c08111fdb5 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -197,9 +197,7 @@ enum StringID STRINGID_PKMNPREVENTSUSAGE, STRINGID_PKMNRESTOREDHPUSING, STRINGID_PKMNCHANGEDTYPEWITH, - STRINGID_PKMNPREVENTSPARALYSISWITH, STRINGID_PKMNPREVENTSROMANCEWITH, - STRINGID_PKMNPREVENTSPOISONINGWITH, STRINGID_PKMNPREVENTSCONFUSIONWITH, STRINGID_PKMNRAISEDFIREPOWERWITH, STRINGID_PKMNANCHORSITSELFWITH, @@ -302,7 +300,6 @@ enum StringID STRINGID_ITEMALLOWSONLYYMOVE, STRINGID_PKMNHUNGONWITHX, STRINGID_EMPTYSTRING3, - STRINGID_PKMNSXPREVENTSBURNS, STRINGID_PKMNSXBLOCKSY, STRINGID_PKMNSXRESTOREDHPALITTLE2, STRINGID_PKMNSXWHIPPEDUPSANDSTORM, @@ -348,14 +345,12 @@ enum StringID STRINGID_PKMNSXBLOCKSY2, STRINGID_PKMNSXWOREOFF, STRINGID_THEWALLSHATTERED, - STRINGID_PKMNSXPREVENTSYSZ, STRINGID_PKMNSXCUREDITSYPROBLEM, STRINGID_ATTACKERCANTESCAPE, STRINGID_PKMNOBTAINEDX, STRINGID_PKMNOBTAINEDX2, STRINGID_PKMNOBTAINEDXYOBTAINEDZ, STRINGID_BUTNOEFFECT, - STRINGID_PKMNSXHADNOEFFECTONY, STRINGID_TWOENEMIESDEFEATED, STRINGID_TRAINER2LOSETEXT, STRINGID_PKMNINCAPABLEOFPOWER, @@ -588,7 +583,6 @@ enum StringID STRINGID_BROKETHROUGHPROTECTION, STRINGID_ABILITYALLOWSONLYMOVE, STRINGID_SWAPPEDABILITIES, - STRINGID_PASTELVEILPROTECTED, STRINGID_PASTELVEILENTERS, STRINGID_BATTLERTYPECHANGEDTO, STRINGID_BOTHCANNOLONGERESCAPE, @@ -1040,17 +1034,6 @@ enum GotStatusedStringID B_MSG_STATUSED_BY_ABILITY, }; -// gStatusPreventionStringIds -enum StatusPreventionStringID -{ - B_MSG_ABILITY_PREVENTS_MOVE_BURN, - B_MSG_ABILITY_PREVENTS_MOVE_PARALYSIS, - B_MSG_ABILITY_PREVENTS_MOVE_POISON, - B_MSG_ABILITY_PREVENTS_ABILITY_STATUS, - B_MSG_STATUS_HAD_NO_EFFECT, - B_MSG_ABILITY_PASTEL_VEIL, -}; - // gGotDefrostedStringIds enum GotDefrostedStringID { diff --git a/src/battle_arena.c b/src/battle_arena.c index 8474595b0d7f..3708fec02480 100644 --- a/src/battle_arena.c +++ b/src/battle_arena.c @@ -427,14 +427,11 @@ void BattleArena_DeductSkillPoints(u8 battler, enum StringID stringId) case STRINGID_PKMNSXBLOCKSY2: case STRINGID_PKMNSXPREVENTSYLOSS: case STRINGID_PKMNSXMADEYINEFFECTIVE: - case STRINGID_PKMNSXPREVENTSBURNS: case STRINGID_PKMNSXBLOCKSY: case STRINGID_PKMNPROTECTEDBY: case STRINGID_PKMNPREVENTSUSAGE: case STRINGID_PKMNRESTOREDHPUSING: - case STRINGID_PKMNPREVENTSPARALYSISWITH: case STRINGID_PKMNPREVENTSROMANCEWITH: - case STRINGID_PKMNPREVENTSPOISONINGWITH: case STRINGID_PKMNPREVENTSCONFUSIONWITH: case STRINGID_PKMNRAISEDFIREPOWERWITH: case STRINGID_PKMNANCHORSITSELFWITH: diff --git a/src/battle_message.c b/src/battle_message.c index b4bef73e4183..aca93f35d97d 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -356,9 +356,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNPREVENTSUSAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents {B_ATK_NAME_WITH_PREFIX2} from using {B_CURRENT_MOVE}!"), //I don't see this in SV text [STRINGID_PKMNRESTOREDHPUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} restored HP using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNCHANGEDTYPEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} made it the {B_BUFF1} type!"), //not in gen 5+, ability popup - [STRINGID_PKMNPREVENTSPARALYSISWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ABILITY} prevents paralysis!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSROMANCEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents romance!"), //not in gen 5+, ability popup - [STRINGID_PKMNPREVENTSPOISONINGWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ABILITY} prevents poisoning!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSCONFUSIONWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents confusion!"), //not in gen 5+, ability popup [STRINGID_PKMNRAISEDFIREPOWERWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} raised the power of Fire-type moves!"), //not in gen 5+, ability popup [STRINGID_PKMNANCHORSITSELFWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} anchors itself with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup @@ -461,7 +459,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_ITEMALLOWSONLYYMOVE] = COMPOUND_STRING("{B_LAST_ITEM} only allows the use of {B_CURRENT_MOVE}!\p"), [STRINGID_PKMNHUNGONWITHX] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} hung on using its {B_LAST_ITEM}!"), [STRINGID_EMPTYSTRING3] = gText_EmptyString3, - [STRINGID_PKMNSXPREVENTSBURNS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ABILITY} prevents burns!"), //not in gen 5+, ability popup [STRINGID_PKMNSXBLOCKSY] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} blocks {B_CURRENT_MOVE}!"), //not in gen 5+, ability popup [STRINGID_PKMNSXRESTOREDHPALITTLE2] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} restored its HP a little!"), //not in gen 5+, ability popup [STRINGID_PKMNSXWHIPPEDUPSANDSTORM] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} whipped up a sandstorm!"), //not in gen 5+, ability popup @@ -507,14 +504,12 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNSXBLOCKSY2] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} blocks {B_CURRENT_MOVE}!"), //not in gen 5+, ability popup [STRINGID_PKMNSXWOREOFF] = COMPOUND_STRING("{B_ATK_TEAM1} team's {B_BUFF1} wore off!"), [STRINGID_THEWALLSHATTERED] = COMPOUND_STRING("The wall shattered!"), //not in gen5+, uses "your teams light screen wore off!" etc instead - [STRINGID_PKMNSXPREVENTSYSZ] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} prevents {B_DEF_NAME_WITH_PREFIX2}'s {B_DEF_ABILITY} from working!"), [STRINGID_PKMNSXCUREDITSYPROBLEM] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} cured its {B_BUFF1} problem!"), //not in gen 5+, ability popup [STRINGID_ATTACKERCANTESCAPE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} can't escape!"), [STRINGID_PKMNOBTAINEDX] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} obtained {B_BUFF1}."), [STRINGID_PKMNOBTAINEDX2] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} obtained {B_BUFF2}."), [STRINGID_PKMNOBTAINEDXYOBTAINEDZ] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} obtained {B_BUFF1}.\p{B_DEF_NAME_WITH_PREFIX} obtained {B_BUFF2}."), [STRINGID_BUTNOEFFECT] = COMPOUND_STRING("But it had no effect!"), - [STRINGID_PKMNSXHADNOEFFECTONY] = COMPOUND_STRING("Target protected by {B_LAST_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_TWOENEMIESDEFEATED] = sText_TwoInGameTrainersDefeated, [STRINGID_TRAINER2LOSETEXT] = COMPOUND_STRING("{B_TRAINER2_LOSE_TEXT}"), [STRINGID_PKMNINCAPABLEOFPOWER] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} appears incapable of using its power!"), @@ -748,7 +743,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_BROKETHROUGHPROTECTION] = COMPOUND_STRING("It broke through {B_DEF_NAME_WITH_PREFIX2}'s protection!"), [STRINGID_ABILITYALLOWSONLYMOVE] = COMPOUND_STRING("{B_ATK_ABILITY} only allows the use of {B_CURRENT_MOVE}!\p"), [STRINGID_SWAPPEDABILITIES] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} swapped Abilities with its target!"), - [STRINGID_PASTELVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is protected by a pastel veil!"), [STRINGID_PASTELVEILENTERS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} was cured of its poisoning!"), [STRINGID_BATTLERTYPECHANGEDTO] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s type changed to {B_BUFF1}!"), [STRINGID_BOTHCANNOLONGERESCAPE] = COMPOUND_STRING("Neither Pokémon can run away!"), @@ -1323,16 +1317,6 @@ const u16 gBerryEffectStringIds[] = [B_MSG_NORMALIZED_STATUS] = STRINGID_PKMNSITEMNORMALIZEDSTATUS }; -const u16 gStatusPreventionStringIds[] = -{ - [B_MSG_ABILITY_PREVENTS_MOVE_BURN] = STRINGID_PKMNSXPREVENTSBURNS, - [B_MSG_ABILITY_PREVENTS_MOVE_PARALYSIS] = STRINGID_PKMNPREVENTSPARALYSISWITH, - [B_MSG_ABILITY_PREVENTS_MOVE_POISON] = STRINGID_PKMNPREVENTSPOISONINGWITH, - [B_MSG_ABILITY_PREVENTS_ABILITY_STATUS] = STRINGID_PKMNSXPREVENTSYSZ, - [B_MSG_STATUS_HAD_NO_EFFECT] = STRINGID_PKMNSXHADNOEFFECTONY, - [B_MSG_ABILITY_PASTEL_VEIL] = STRINGID_PASTELVEILPROTECTED -}; - const u16 gItemSwapStringIds[] = { [B_MSG_ITEM_SWAP_TAKEN] = STRINGID_PKMNOBTAINEDX, diff --git a/src/battle_util.c b/src/battle_util.c index 0dd60674ca4f..c66fcdc86721 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5645,13 +5645,11 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u abilityAffected = TRUE; battlerDef = sideBattler - 1; abilityDef = ABILITY_PASTEL_VEIL; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PASTEL_VEIL; battleScript = BattleScript_ImmunityProtected; } else if (abilityDef == ABILITY_IMMUNITY) { abilityAffected = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_POISON; battleScript = BattleScript_ImmunityProtected; } break; @@ -5673,7 +5671,6 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u else if (abilityDef == ABILITY_LIMBER) { abilityAffected = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_PARALYSIS; battleScript = BattleScript_ImmunityProtected; } break; @@ -5689,7 +5686,6 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u else if (abilityDef == ABILITY_WATER_VEIL || abilityDef == ABILITY_WATER_BUBBLE) { abilityAffected = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_BURN; battleScript = BattleScript_ImmunityProtected; } else if (abilityDef == ABILITY_THERMAL_EXCHANGE) @@ -5720,7 +5716,6 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u abilityAffected = TRUE; battlerDef = sideBattler - 1; abilityDef = ABILITY_SWEET_VEIL; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; battleScript = BattleScript_ImmunityProtected; } else if (abilityDef == ABILITY_VITAL_SPIRIT || abilityDef == ABILITY_INSOMNIA) diff --git a/test/battle/ability/immunity.c b/test/battle/ability/immunity.c index 81fbe1b8c128..6f630002f9ec 100644 --- a/test/battle/ability/immunity.c +++ b/test/battle/ability/immunity.c @@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Immunity prevents Toxic bad poison") } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponent, ABILITY_IMMUNITY); - MESSAGE("The opposing Snorlax's Immunity prevents poisoning!"); + MESSAGE("But it failed!"); NOT STATUS_ICON(opponent, poison: TRUE); } } diff --git a/test/battle/ability/limber.c b/test/battle/ability/limber.c index f89f9a1e748e..0ab3c6017474 100644 --- a/test/battle/ability/limber.c +++ b/test/battle/ability/limber.c @@ -29,7 +29,7 @@ SINGLE_BATTLE_TEST("Limber prevents paralysis from Thunder Wave") } WHEN { TURN { MOVE(opponent, MOVE_THUNDER_WAVE); } } SCENE { - MESSAGE("Persian's Limber prevents paralysis!"); + MESSAGE("But it failed!"); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player); STATUS_ICON(player, paralysis: TRUE); diff --git a/test/battle/ability/pastel_veil.c b/test/battle/ability/pastel_veil.c index 1c0926ac807d..cbaa3445aee9 100644 --- a/test/battle/ability/pastel_veil.c +++ b/test/battle/ability/pastel_veil.c @@ -80,7 +80,7 @@ SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison") } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponent, ABILITY_PASTEL_VEIL); - MESSAGE("The opposing Ponyta is protected by a pastel veil!"); + MESSAGE("But it failed!"); NOT STATUS_ICON(opponent, badPoison: TRUE); } } @@ -97,7 +97,7 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner - right tar } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponentLeft, ABILITY_PASTEL_VEIL); - MESSAGE("The opposing Wynaut is protected by a pastel veil!"); + MESSAGE("But it failed!"); NOT STATUS_ICON(opponentRight, badPoison: TRUE); } } @@ -114,7 +114,7 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner - left targ } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponentRight, ABILITY_PASTEL_VEIL); - MESSAGE("The opposing Wynaut is protected by a pastel veil!"); + MESSAGE("But it failed!"); NOT STATUS_ICON(opponentLeft, badPoison: TRUE); } } diff --git a/test/battle/ability/water_bubble.c b/test/battle/ability/water_bubble.c index d140dac04048..a495c2c686cd 100644 --- a/test/battle/ability/water_bubble.c +++ b/test/battle/ability/water_bubble.c @@ -12,7 +12,7 @@ SINGLE_BATTLE_TEST("Water Bubble prevents burn from Will-o-Wisp") TURN { MOVE(opponent, MOVE_WILL_O_WISP); } } SCENE { ABILITY_POPUP(player, ABILITY_WATER_BUBBLE); - MESSAGE("Dewpider's Water Bubble prevents burns!"); + MESSAGE("But it failed!"); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, player); STATUS_ICON(player, burn: TRUE); From d2acc2d7d6648afa40bf64ca2839a165d7756b48 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 11 Aug 2025 13:33:56 -0400 Subject: [PATCH 03/25] Changed instances of "But it failed" to "It doesn't affect X" --- data/battle_scripts_1.s | 2 +- test/battle/ability/immunity.c | 2 +- test/battle/ability/limber.c | 2 +- test/battle/ability/pastel_veil.c | 6 +++--- test/battle/ability/water_bubble.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index bde00b4cda6b..a1ff092dba26 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3114,7 +3114,7 @@ BattleScript_AlreadyPoisoned:: BattleScript_ImmunityProtected:: call BattleScript_AbilityPopUp - goto BattleScript_ButItFailed + goto BattleScript_DoesntAffectTargetAtkString BattleScript_EffectAuroraVeil:: attackcanceler diff --git a/test/battle/ability/immunity.c b/test/battle/ability/immunity.c index 6f630002f9ec..f1efc6600451 100644 --- a/test/battle/ability/immunity.c +++ b/test/battle/ability/immunity.c @@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Immunity prevents Toxic bad poison") } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponent, ABILITY_IMMUNITY); - MESSAGE("But it failed!"); + MESSAGE("It doesn't affect the opposing Snorlax…"); NOT STATUS_ICON(opponent, poison: TRUE); } } diff --git a/test/battle/ability/limber.c b/test/battle/ability/limber.c index 0ab3c6017474..fc4e398c1fa5 100644 --- a/test/battle/ability/limber.c +++ b/test/battle/ability/limber.c @@ -29,7 +29,7 @@ SINGLE_BATTLE_TEST("Limber prevents paralysis from Thunder Wave") } WHEN { TURN { MOVE(opponent, MOVE_THUNDER_WAVE); } } SCENE { - MESSAGE("But it failed!"); + MESSAGE("It doesn't affect Persian…"); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player); STATUS_ICON(player, paralysis: TRUE); diff --git a/test/battle/ability/pastel_veil.c b/test/battle/ability/pastel_veil.c index cbaa3445aee9..11bf32bb9a09 100644 --- a/test/battle/ability/pastel_veil.c +++ b/test/battle/ability/pastel_veil.c @@ -80,7 +80,7 @@ SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison") } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponent, ABILITY_PASTEL_VEIL); - MESSAGE("But it failed!"); + MESSAGE("It doesn't affect the opposing Ponyta…"); NOT STATUS_ICON(opponent, badPoison: TRUE); } } @@ -97,7 +97,7 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner - right tar } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponentLeft, ABILITY_PASTEL_VEIL); - MESSAGE("But it failed!"); + MESSAGE("It doesn't affect the opposing Wynaut…"); NOT STATUS_ICON(opponentRight, badPoison: TRUE); } } @@ -114,7 +114,7 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner - left targ } SCENE { MESSAGE("Wobbuffet used Toxic!"); ABILITY_POPUP(opponentRight, ABILITY_PASTEL_VEIL); - MESSAGE("But it failed!"); + MESSAGE("It doesn't affect the opposing Wynaut…"); NOT STATUS_ICON(opponentLeft, badPoison: TRUE); } } diff --git a/test/battle/ability/water_bubble.c b/test/battle/ability/water_bubble.c index a495c2c686cd..1d1d3956d42c 100644 --- a/test/battle/ability/water_bubble.c +++ b/test/battle/ability/water_bubble.c @@ -12,7 +12,7 @@ SINGLE_BATTLE_TEST("Water Bubble prevents burn from Will-o-Wisp") TURN { MOVE(opponent, MOVE_WILL_O_WISP); } } SCENE { ABILITY_POPUP(player, ABILITY_WATER_BUBBLE); - MESSAGE("But it failed!"); + MESSAGE("It doesn't affect Dewpider…"); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, player); STATUS_ICON(player, burn: TRUE); From b501fe7354bcd957396465c621ae7af5959ac5b0 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 11 Aug 2025 13:41:32 -0400 Subject: [PATCH 04/25] Removed B_ABILITY_POP_UP --- data/battle_scripts_1.s | 20 -------------------- include/config/battle.h | 1 - src/battle_interface.c | 3 --- test/test_runner_battle.c | 2 -- 4 files changed, 26 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a1ff092dba26..9c884da0f848 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -206,10 +206,8 @@ BattleScript_EffectDoodle:: BattleScript_EffectDoodle_CopyAbility: trycopyability BS_ATTACKER, BattleScript_MoveEnd BattleScript_EffectDoodle_AfterCopy: -.if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUpOverwriteThenNormal -.endif recordability BS_ATTACKER printstring STRINGID_PKMNCOPIEDFOE waitmessage B_WAIT_TIME_LONG @@ -2223,10 +2221,8 @@ BattleScript_EffectSimpleBeam:: setsimplebeam BattleScript_ButItFailed attackanimation waitanimation -.if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerTarget call BattleScript_AbilityPopUpOverwriteThenNormal -.endif recordability BS_TARGET printstring STRINGID_PKMNACQUIREDSIMPLE waitmessage B_WAIT_TIME_LONG @@ -2325,10 +2321,8 @@ BattleScript_EffectWorrySeed:: tryworryseed BattleScript_ButItFailed attackanimation waitanimation -.if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerTarget call BattleScript_AbilityPopUpOverwriteThenNormal -.endif recordability BS_TARGET printstring STRINGID_PKMNACQUIREDABILITY waitmessage B_WAIT_TIME_LONG @@ -4655,10 +4649,8 @@ BattleScript_EffectRolePlay:: trycopyability BS_ATTACKER, BattleScript_ButItFailed attackanimation waitanimation -.if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUpOverwriteThenNormal -.endif recordability BS_ATTACKER printstring STRINGID_PKMNCOPIEDFOE waitmessage B_WAIT_TIME_LONG @@ -4790,13 +4782,11 @@ BattleScript_EffectSkillSwap:: attackanimation waitanimation jumpiftargetally BattleScript_EffectSkillSwap_AfterAbilityPopUp -.if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUpOverwriteThenNormal copybyte gBattlerAbility, gBattlerTarget copyhword sABILITY_OVERWRITE, gLastUsedAbility call BattleScript_AbilityPopUpOverwriteThenNormal -.endif BattleScript_EffectSkillSwap_AfterAbilityPopUp: recordability BS_ATTACKER recordability BS_TARGET @@ -7084,10 +7074,8 @@ BattleScript_AbilityPopUpTarget:: copybyte gBattlerAbility, gBattlerTarget BattleScript_AbilityPopUp:: tryactivateabilityshield BS_ABILITY_BATTLER - .if B_ABILITY_POP_UP == TRUE showabilitypopup pause B_WAIT_TIME_SHORT - .endif recordability BS_ABILITY_BATTLER sethword sABILITY_OVERWRITE, 0 return @@ -7138,11 +7126,9 @@ BattleScript_MoodyEnd: end3 BattleScript_EmergencyExit:: - .if B_ABILITY_POP_UP == TRUE pause 5 call BattleScript_AbilityPopUpScripting pause B_WAIT_TIME_LONG - .endif playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN waitanimation openpartyscreen BS_SCRIPTING, BattleScript_EmergencyExitRet @@ -7161,11 +7147,9 @@ BattleScript_EmergencyExitRet: return BattleScript_EmergencyExitWild:: - .if B_ABILITY_POP_UP == TRUE pause 5 call BattleScript_AbilityPopUpScripting pause B_WAIT_TIME_LONG - .endif playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN waitanimation setteleportoutcome BS_SCRIPTING @@ -7895,13 +7879,11 @@ BattleScript_CursedBodyActivates:: return BattleScript_MummyActivates:: -.if B_ABILITY_POP_UP == TRUE setbyte sFIXED_ABILITY_POPUP, TRUE call BattleScript_AbilityPopUpTarget copybyte gBattlerAbility, gBattlerAttacker copyhword sABILITY_OVERWRITE, gLastUsedAbility call BattleScript_AbilityPopUpOverwriteThenNormal -.endif recordability BS_TARGET recordability BS_ATTACKER printstring STRINGID_ATTACKERACQUIREDABILITY @@ -7911,14 +7893,12 @@ BattleScript_MummyActivates:: BattleScript_WanderingSpiritActivates:: saveattacker savetarget -.if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerTarget sethword sABILITY_OVERWRITE, ABILITY_WANDERING_SPIRIT call BattleScript_AbilityPopUpOverwriteThenNormal copybyte gBattlerAbility, gBattlerAttacker copyhword sABILITY_OVERWRITE, gLastUsedAbility call BattleScript_AbilityPopUpOverwriteThenNormal -.endif recordability BS_TARGET recordability BS_ATTACKER printstring STRINGID_SWAPPEDABILITIES diff --git a/include/config/battle.h b/include/config/battle.h index 3cfd3193043f..eccc88b11dd6 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -268,7 +268,6 @@ #define B_NEW_TERRAIN_BACKGROUNDS FALSE // If set to TRUE, uses new terrain backgrounds for Electric, Misty, Grassy and Psychic Terrain. // Interface settings -#define B_ABILITY_POP_UP TRUE // In Gen5+, the Pokémon abilities are displayed in a pop-up, when they activate in battle. #define B_FAST_INTRO_PKMN_TEXT TRUE // If set to TRUE, battle intro texts print at the same time as animation of a Pokémon, as opposing to waiting for the animation to end. #define B_FAST_INTRO_NO_SLIDE FALSE // If set to TRUE, the slide animation that happens at the beginning of the battle is skipped. #define B_FAST_HP_DRAIN TRUE // If set to TRUE, HP bars will move faster. diff --git a/src/battle_interface.c b/src/battle_interface.c index 2aee6460cb4e..123f19596919 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -2638,9 +2638,6 @@ void CreateAbilityPopUp(u8 battler, u32 ability, bool32 isDoubleBattle) struct SpriteTemplate template; const s16 (*coords)[2]; - if (!B_ABILITY_POP_UP) - return; - if (gBattleScripting.abilityPopupOverwrite) ability = gBattleScripting.abilityPopupOverwrite; diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index c009219e26f7..e13a31bd9f63 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2548,7 +2548,6 @@ void CloseQueueGroup(u32 sourceLine) void QueueAbility(u32 sourceLine, struct BattlePokemon *battler, struct AbilityEventContext ctx) { -#if B_ABILITY_POP_UP s32 battlerId = battler - gBattleMons; INVALID_IF(!STATE->runScene, "ABILITY_POPUP outside of SCENE"); if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS) @@ -2563,7 +2562,6 @@ void QueueAbility(u32 sourceLine, struct BattlePokemon *battler, struct AbilityE .ability = ctx.ability, }}, }; -#endif } void QueueAnimation(u32 sourceLine, u32 type, u32 id, struct AnimationEventContext ctx) From 02fea82abf801fdc4acf472d595fae6881caf811 Mon Sep 17 00:00:00 2001 From: surskitty Date: Fri, 15 Aug 2025 15:16:40 -0400 Subject: [PATCH 05/25] Cleaning up checks if battler benefits from status. Better logic for freeze/sleep. --- src/battle_ai_util.c | 58 +++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 2712c4499fce..5cd280709e0b 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3367,15 +3367,43 @@ bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move return TRUE; } -static inline bool32 DoesBattlerBenefitFromAllVolatileStatus(u32 battler, u32 ability) +static inline bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 status) { - if (ability == ABILITY_MARVEL_SCALE - || ability == ABILITY_QUICK_FEET - || ability == ABILITY_MAGIC_GUARD - || (ability == ABILITY_GUTS && HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL)) - || HasMoveWithEffect(battler, EFFECT_FACADE) + if (!(status & STATUS1_CAN_MOVE)) + return FALSE; + + if (HasMoveWithEffect(battler, EFFECT_FACADE) || HasMoveWithEffect(battler, EFFECT_PSYCHO_SHIFT)) return TRUE; + + switch (ability) + { + case ABILITY_MAGIC_GUARD: + case ABILITY_MARVEL_SCALE: + case ABILITY_QUICK_FEET: + if (status & STATUS1_BURN) + return !HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL); + return TRUE; + case ABILITY_GUTS: + return HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL); + case ABILITY_POISON_HEAL: + return (status & STATUS1_PSN_ANY); + case ABILITY_TOXIC_BOOST: + if (status & STATUS1_PSN_ANY) + return HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL); + break; + case ABILITY_HEATPROOF: + if (status & STATUS1_BURN) + return !HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL); + break; + case ABILITY_FLARE_BOOST: + if (status & STATUS1_BURN) + return HasMoveWithCategory(battler, DAMAGE_CATEGORY_SPECIAL); + break; + default: + break; + } + return FALSE; } @@ -3383,10 +3411,8 @@ bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef) { u32 abilityDef = gAiLogicData->abilities[battlerDef]; // Battler can be poisoned and has move/ability that synergizes with being poisoned - if (CanBePoisoned(battlerAtk, battlerDef, gAiLogicData->abilities[battlerAtk], abilityDef) && ( - DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef) - || abilityDef == ABILITY_POISON_HEAL - || (abilityDef == ABILITY_TOXIC_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)))) + if (CanBePoisoned(battlerAtk, battlerDef, gAiLogicData->abilities[battlerAtk], abilityDef) + && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_PSN_ANY)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; @@ -3402,10 +3428,8 @@ bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef) bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { // Battler can be burned and has move/ability that synergizes with being burned - if (CanBeBurned(battlerAtk, battlerDef, abilityDef) && ( - DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef) - || abilityDef == ABILITY_HEATPROOF - || (abilityDef == ABILITY_FLARE_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)))) + if (CanBeBurned(battlerAtk, battlerDef, abilityDef) + && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_BURN)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; @@ -3436,7 +3460,7 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { // Battler can be frostbitten and has move/ability that synergizes with being frostbitten if (CanBeFrozen(battlerAtk, battlerDef, abilityDef) - && DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef)) + && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_FROSTBITE)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; @@ -3454,8 +3478,8 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef) bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { // Battler can be paralyzed and has move/ability that synergizes with being paralyzed - if (CanBeParalyzed(battlerAtk, battlerDef, abilityDef) && ( - DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef))) + if (CanBeParalyzed(battlerAtk, battlerDef, abilityDef) + && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_PARALYSIS)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; From c37a6896e26e76ba4190095ea2506d736f99e5df Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 16 Aug 2025 20:56:01 -0400 Subject: [PATCH 06/25] Logic cleanup; making it non-static. --- include/battle_ai_util.h | 1 + src/battle_ai_util.c | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 1b23dd7e1af5..07218d643d6c 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -113,6 +113,7 @@ bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dm s32 AI_DecideKnownAbilityForTurn(u32 battlerId); enum ItemHoldEffect AI_DecideHoldEffectForTurn(u32 battlerId); bool32 DoesBattlerIgnoreAbilityChecks(u32 battlerAtk, u32 atkAbility, u32 move); +bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 status); u32 AI_GetWeather(void); u32 AI_GetSwitchinWeather(struct BattlePokemon battleMon); enum WeatherState IsWeatherActive(u32 flags); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 5cd280709e0b..fcb071021ae8 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3367,13 +3367,25 @@ bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move return TRUE; } -static inline bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 status) +bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 status) { - if (!(status & STATUS1_CAN_MOVE)) - return FALSE; + if (status & STATUS1_SLEEP) + { + if (HasMoveWithEffect(battler, EFFECT_SLEEP_TALK) || HasMoveWithEffect(battler, EFFECT_SNORE)) + return TRUE; + else + return FALSE; + } + + if (status & STATUS1_FREEZE) + { + if (HasThawingMove(battler)) + return TRUE; + else + return FALSE; + } - if (HasMoveWithEffect(battler, EFFECT_FACADE) - || HasMoveWithEffect(battler, EFFECT_PSYCHO_SHIFT)) + if (HasMoveWithEffect(battler, EFFECT_FACADE) || HasMoveWithEffect(battler, EFFECT_PSYCHO_SHIFT)) return TRUE; switch (ability) @@ -3383,6 +3395,8 @@ static inline bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 case ABILITY_QUICK_FEET: if (status & STATUS1_BURN) return !HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL); + if (status & STATUS1_FROSTBITE) + return !HasMoveWithCategory(battler, DAMAGE_CATEGORY_SPECIAL); return TRUE; case ABILITY_GUTS: return HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL); From 68127d0cc937e207e122720b7a2ffb777a8c585e Mon Sep 17 00:00:00 2001 From: surskitty Date: Wed, 20 Aug 2025 19:24:58 -0400 Subject: [PATCH 07/25] Renaming function for clarity. --- include/battle_ai_util.h | 2 +- src/battle_ai_util.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 07218d643d6c..52dfcc266b81 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -113,7 +113,7 @@ bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dm s32 AI_DecideKnownAbilityForTurn(u32 battlerId); enum ItemHoldEffect AI_DecideHoldEffectForTurn(u32 battlerId); bool32 DoesBattlerIgnoreAbilityChecks(u32 battlerAtk, u32 atkAbility, u32 move); -bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 status); +bool32 DoesBattlerBenefitFromNonvolatileStatus(u32 battler, u32 ability, u32 status); u32 AI_GetWeather(void); u32 AI_GetSwitchinWeather(struct BattlePokemon battleMon); enum WeatherState IsWeatherActive(u32 flags); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index fcb071021ae8..f49c2d5fb9c1 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3367,7 +3367,7 @@ bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move return TRUE; } -bool32 DoesBattlerBenefitFromStatus(u32 battler, u32 ability, u32 status) +bool32 DoesBattlerBenefitFromNonvolatileStatus(u32 battler, u32 ability, u32 status) { if (status & STATUS1_SLEEP) { @@ -3426,7 +3426,7 @@ bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef) u32 abilityDef = gAiLogicData->abilities[battlerDef]; // Battler can be poisoned and has move/ability that synergizes with being poisoned if (CanBePoisoned(battlerAtk, battlerDef, gAiLogicData->abilities[battlerAtk], abilityDef) - && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_PSN_ANY)) + && DoesBattlerBenefitFromNonvolatileStatus(battlerDef, abilityDef, STATUS1_PSN_ANY)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; @@ -3443,7 +3443,7 @@ bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { // Battler can be burned and has move/ability that synergizes with being burned if (CanBeBurned(battlerAtk, battlerDef, abilityDef) - && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_BURN)) + && DoesBattlerBenefitFromNonvolatileStatus(battlerDef, abilityDef, STATUS1_BURN)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; @@ -3474,7 +3474,7 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { // Battler can be frostbitten and has move/ability that synergizes with being frostbitten if (CanBeFrozen(battlerAtk, battlerDef, abilityDef) - && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_FROSTBITE)) + && DoesBattlerBenefitFromNonvolatileStatus(battlerDef, abilityDef, STATUS1_FROSTBITE)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; @@ -3493,7 +3493,7 @@ bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { // Battler can be paralyzed and has move/ability that synergizes with being paralyzed if (CanBeParalyzed(battlerAtk, battlerDef, abilityDef) - && DoesBattlerBenefitFromStatus(battlerDef, abilityDef, STATUS1_PARALYSIS)) + && DoesBattlerBenefitFromNonvolatileStatus(battlerDef, abilityDef, STATUS1_PARALYSIS)) { if (battlerAtk == battlerDef) // Targeting self return TRUE; From 2ab1bc87e0d019a7ffd2de6f2afcce68d289ab94 Mon Sep 17 00:00:00 2001 From: Hedara Date: Thu, 28 Aug 2025 16:46:58 +0200 Subject: [PATCH 08/25] Start of 1.14 cycle --- include/constants/expansion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/constants/expansion.h b/include/constants/expansion.h index e4468a82b4ed..2858684d20cd 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -3,8 +3,8 @@ // Last version: 1.13.0 #define EXPANSION_VERSION_MAJOR 1 -#define EXPANSION_VERSION_MINOR 13 -#define EXPANSION_VERSION_PATCH 1 +#define EXPANSION_VERSION_MINOR 14 +#define EXPANSION_VERSION_PATCH 0 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. From a2e1835993fd320e164e9f2cbf69d99d61f0b8a4 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:07:08 +0100 Subject: [PATCH 09/25] Separates FRB and FRZ animations (#7611) --- data/battle_anim_scripts.s | 11 +++++++++++ include/battle_anim_scripts.h | 1 + include/constants/battle_anim.h | 3 ++- src/battle_anim.c | 1 + src/battle_gfx_sfx_util.c | 4 +++- test/battle/hold_effect/cure_status.c | 2 +- test/battle/move_effect_secondary/freeze.c | 6 +++--- test/battle/move_effect_secondary/tri_attack.c | 4 ++-- test/battle/move_effects_combined/flinch_status.c | 2 +- test/battle/status1/frostbite.c | 6 +++--- 10 files changed, 28 insertions(+), 12 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index a741c372eaf2..1d05258dcfdb 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -31084,6 +31084,17 @@ gBattleAnimStatus_Nightmare:: clearmonbg ANIM_DEF_PARTNER end +gBattleAnimStatus_Frostbite:: + playsewithpan SE_M_ICY_WIND, 0 + loadspritegfx ANIM_TAG_ICE_CRYSTALS + monbg ANIM_DEF_PARTNER + splitbgprio ANIM_TARGET + call IceCrystalEffectShort + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_TARGET, 5, 7, 0, RGB(0, 20, 31) + waitforvisualfinish + clearmonbg ANIM_DEF_PARTNER + end + gBattleAnimGeneral_StatsChange:: createvisualtask AnimTask_StatsChange, 5 waitforvisualfinish diff --git a/include/battle_anim_scripts.h b/include/battle_anim_scripts.h index 0bffbda8ead2..193696375df7 100644 --- a/include/battle_anim_scripts.h +++ b/include/battle_anim_scripts.h @@ -948,6 +948,7 @@ extern const u8 gBattleAnimStatus_Paralysis[]; extern const u8 gBattleAnimStatus_Freeze[]; extern const u8 gBattleAnimStatus_Curse[]; extern const u8 gBattleAnimStatus_Nightmare[]; +extern const u8 gBattleAnimStatus_Frostbite[]; // general animations extern const u8 gBattleAnimGeneral_StatsChange[]; diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 3d514b1936cd..38965e034242 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -616,8 +616,9 @@ #define B_ANIM_STATUS_FRZ 6 #define B_ANIM_STATUS_CURSED 7 #define B_ANIM_STATUS_NIGHTMARE 8 +#define B_ANIM_STATUS_FRB 9 -#define NUM_B_ANIMS_STATUS 9 +#define NUM_B_ANIMS_STATUS 10 // Tasks with return values often assign them to gBattleAnimArgs[7]. #define ARG_RET_ID 7 diff --git a/src/battle_anim.c b/src/battle_anim.c index 76e37b7eea12..100d2d376dd8 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -195,6 +195,7 @@ static const u8* const sBattleAnims_StatusConditions[NUM_B_ANIMS_STATUS] = [B_ANIM_STATUS_FRZ] = gBattleAnimStatus_Freeze, [B_ANIM_STATUS_CURSED] = gBattleAnimStatus_Curse, [B_ANIM_STATUS_NIGHTMARE] = gBattleAnimStatus_Nightmare, + [B_ANIM_STATUS_FRB] = gBattleAnimStatus_Frostbite, }; static const u8* const sBattleAnims_General[NUM_B_ANIMS_GENERAL] = diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index e7bf06327da4..bddf1b6d19dc 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -464,8 +464,10 @@ void InitAndLaunchChosenStatusAnimation(u32 battler, bool32 isVolatile, u32 stat gBattleSpritesDataPtr->healthBoxesData[battler].statusAnimActive = 1; if (!isVolatile) { - if (status == STATUS1_FREEZE || status == STATUS1_FROSTBITE) + if (status == STATUS1_FREEZE) LaunchStatusAnimation(battler, B_ANIM_STATUS_FRZ); + else if (status == STATUS1_FROSTBITE) + LaunchStatusAnimation(battler, B_ANIM_STATUS_FRB); else if (status == STATUS1_POISON || status & STATUS1_TOXIC_POISON) LaunchStatusAnimation(battler, B_ANIM_STATUS_PSN); else if (status == STATUS1_BURN) diff --git a/test/battle/hold_effect/cure_status.c b/test/battle/hold_effect/cure_status.c index 0fa0f7ba0754..dabf44e08523 100644 --- a/test/battle/hold_effect/cure_status.c +++ b/test/battle/hold_effect/cure_status.c @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze or frostbite") TURN { MOVE(player, MOVE_ICE_PUNCH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_PUNCH, player); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + ANIMATION(ANIM_TYPE_STATUS, (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ), opponent); FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); FREEZE_OR_FROSTBURN_STATUS(opponent, FALSE); diff --git a/test/battle/move_effect_secondary/freeze.c b/test/battle/move_effect_secondary/freeze.c index 01a4b31c0688..352845766dc0 100644 --- a/test/battle/move_effect_secondary/freeze.c +++ b/test/battle/move_effect_secondary/freeze.c @@ -20,7 +20,7 @@ SINGLE_BATTLE_TEST("Powder Snow inflicts freeze") } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER_SNOW, player); HP_BAR(opponent); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + ANIMATION(ANIM_TYPE_STATUS, (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ), opponent); FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } } @@ -85,11 +85,11 @@ SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types") ANIMATION(ANIM_TYPE_MOVE, MOVE_FREEZING_GLARE, player); HP_BAR(opponent); #if B_STATUS_TYPE_IMMUNITY > GEN_1 - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + ANIMATION(ANIM_TYPE_STATUS, (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ), opponent); FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); #else NONE_OF { - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + ANIMATION(ANIM_TYPE_STATUS, (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ), opponent); FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } #endif diff --git a/test/battle/move_effect_secondary/tri_attack.c b/test/battle/move_effect_secondary/tri_attack.c index ab33561cf03c..bba72b76771a 100644 --- a/test/battle/move_effect_secondary/tri_attack.c +++ b/test/battle/move_effect_secondary/tri_attack.c @@ -15,7 +15,7 @@ SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze") u8 statusAnim; PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; } PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; } - PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; } + PARAMETRIZE { statusAnim = (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ); } PASSES_RANDOMLY(1, 3, RNG_TRI_ATTACK); GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -29,7 +29,7 @@ SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze") ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent); if (statusAnim == B_ANIM_STATUS_BRN) { STATUS_ICON(opponent, burn: TRUE); - } else if (statusAnim == B_ANIM_STATUS_FRZ) { + } else if (statusAnim == (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ)) { FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } else if (statusAnim == B_ANIM_STATUS_PRZ) { STATUS_ICON(opponent, paralysis: TRUE); diff --git a/test/battle/move_effects_combined/flinch_status.c b/test/battle/move_effects_combined/flinch_status.c index 75c9461880da..0d371707260d 100644 --- a/test/battle/move_effects_combined/flinch_status.c +++ b/test/battle/move_effects_combined/flinch_status.c @@ -32,7 +32,7 @@ SINGLE_BATTLE_TEST("Thunder, Ice and Fire Fang inflict status 10% of the time") ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); STATUS_ICON(opponent, paralysis: TRUE); } if (move == MOVE_ICE_FANG) { - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + ANIMATION(ANIM_TYPE_STATUS, (B_USE_FROSTBITE ? B_ANIM_STATUS_FRB : B_ANIM_STATUS_FRZ), opponent); FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE); } if (move == MOVE_FIRE_FANG) { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); diff --git a/test/battle/status1/frostbite.c b/test/battle/status1/frostbite.c index 35503b00a37b..e221a0eae347 100644 --- a/test/battle/status1/frostbite.c +++ b/test/battle/status1/frostbite.c @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Frostbite deals 1/16th (Gen7+) or 1/8th damage to affected P TURN {} } SCENE { MESSAGE("The opposing Wobbuffet was hurt by its frostbite!"); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRB, opponent); HP_BAR(opponent, captureDamage: &frostbiteDamage); } THEN { EXPECT_EQ(frostbiteDamage, opponent->maxHP / ((B_BURN_DAMAGE >= GEN_7) ? 16 : 8)); } } @@ -86,11 +86,11 @@ SINGLE_BATTLE_TEST("Frostbite is healed when the user uses a thawing move") HP_BAR(opponent); if (move == MOVE_EMBER) { MESSAGE("Wobbuffet was hurt by its frostbite!"); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRB, player); } else { NONE_OF { MESSAGE("Wobbuffet was hurt by its frostbite!"); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRB, player); } } } From f07112bda6d62a13d3f17087e5c7e662d4231ea9 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 28 Aug 2025 20:24:27 +0100 Subject: [PATCH 10/25] Text rendering optimizations (#7497) --- include/window.h | 2 +- src/international_string_util.c | 2 +- src/text.c | 51 +++++++++++++++++++++------------ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/include/window.h b/include/window.h index 2dd67b123ffd..fc35f724d90c 100644 --- a/include/window.h +++ b/include/window.h @@ -45,7 +45,7 @@ struct WindowTemplate struct Window { struct WindowTemplate window; - u8 *tileData; + ALIGNED(4) u8 *tileData; }; bool32 InitWindows(const struct WindowTemplate *templates); diff --git a/src/international_string_util.c b/src/international_string_util.c index feba03c97f5a..b5a904c773a0 100644 --- a/src/international_string_util.c +++ b/src/international_string_util.c @@ -220,7 +220,7 @@ void FillWindowTilesByRow(int windowId, int columnStart, int rowStart, int numFi fillSize = numFillTiles * TILE_SIZE_4BPP; windowRowSize = window->window.width * TILE_SIZE_4BPP; - windowTileData = window->tileData + (rowStart * windowRowSize) + (columnStart * TILE_SIZE_4BPP); + windowTileData = (u8 *)window->tileData + (rowStart * windowRowSize) + (columnStart * TILE_SIZE_4BPP); if (numRows > 0) { for (i = numRows; i != 0; i--) diff --git a/src/text.c b/src/text.c index 29ffc5ea3a1f..8166b3530221 100644 --- a/src/text.c +++ b/src/text.c @@ -427,6 +427,13 @@ void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor) u16 *current = sFontHalfRowLookupTable; + if (fgColor == sLastTextFgColor + && bgColor == sLastTextBgColor + && shadowColor == sLastTextShadowColor) + { + return; + } + sLastTextBgColor = bgColor; sLastTextFgColor = fgColor; sLastTextShadowColor = shadowColor; @@ -629,27 +636,35 @@ static u8 UNUSED GetLastTextColor(u8 colorType) } } -inline static void GLYPH_COPY(u8 *windowTiles, u32 widthOffset, u32 j, u32 i, u32 *glyphPixels, s32 width, s32 height) +inline static void GLYPH_COPY(u8 *windowTiles, u32 widthOffset, u32 x0, u32 y0, u32 *glyphPixels, s32 width, s32 height) { - u32 xAdd, yAdd, pixelData, bits, toOrr, dummyX; - u8 *dst; + if (width <= 0) + return; + + u32 widthMask = (1 << (width * 4)) - 1; - xAdd = j + width; - yAdd = i + height; - dummyX = j; - for (; i < yAdd; i++) + u32 shift0 = (x0 % 8) * 4, shift8 = 32 - shift0; + + u32 *alignedWindowTilesX = (u32 *)(windowTiles + ((x0 / 8) * TILE_SIZE_4BPP)); + + u32 y1 = y0 + height; + for (u32 y = y0; y < y1; y++) { - pixelData = *glyphPixels++; - for (j = dummyX; j < xAdd; j++) - { - if ((toOrr = pixelData & 0xF)) - { - dst = windowTiles + ((j / 8) * 32) + ((j % 8) / 2) + ((i / 8) * widthOffset) + ((i % 8) * 4); - bits = ((j & 1) * 4); - *dst = (toOrr << bits) | (*dst & (0xF0 >> bits)); - } - pixelData >>= 4; - } + u32 pixels = *glyphPixels++ & widthMask; + + u32 mask = pixels; + mask = mask | (mask >> 2); + mask = mask | (mask >> 1); + mask = mask & 0x11111111; + mask = mask * 0xF; + + u32 pixels0 = pixels << shift0, pixels8 = pixels >> shift8; + u32 mask0 = mask << shift0, mask8 = mask >> shift8; + + u32 *alignedWindowTiles = (u32 *)((u8 *)alignedWindowTilesX + ((y / 8) * widthOffset) + ((y % 8) * 4)); + + alignedWindowTiles[0] = (alignedWindowTiles[0] & ~mask0) | pixels0; + alignedWindowTiles[8] = (alignedWindowTiles[8] & ~mask8) | pixels8; } } From 9bfae7fa935a8bd18ecc626cac802fbf0e0734af Mon Sep 17 00:00:00 2001 From: surskitty Date: Fri, 29 Aug 2025 04:08:42 -0400 Subject: [PATCH 11/25] AI uses Magnetic Flux. (#7642) --- src/battle_ai_main.c | 23 +++++++++++++++++------ test/battle/ai/can_use_all_moves.c | 2 +- test/battle/move_effect/magnetic_flux.c | 13 +++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index ebbabf93c897..2532424d24a3 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1557,12 +1557,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) ADJUST_SCORE(-8); } - else if (!hasPartner) - { - ADJUST_SCORE(-10); // our stats wont rise from this move - } - - if (hasPartner) + else if (hasPartner) { if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) { @@ -1576,6 +1571,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); // nor our or our partner's ability is plus/minus } } + else + { + ADJUST_SCORE(-10); // our stats wont rise from this move + } break; // stat lowering effects case EFFECT_ATTACK_DOWN: @@ -4178,6 +4177,18 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(-2); // Should be either removed or turned into increasing score case EFFECT_ACUPRESSURE: break; + case EFFECT_MAGNETIC_FLUX: + if (aiData->abilities[battlerAtk] == ABILITY_PLUS || aiData->abilities[battlerAtk] == ABILITY_MINUS) + { + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF)); + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF)); + } + if (hasPartner && (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS)) + { + ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_DEF)); + ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_SPDEF)); + } + break; case EFFECT_ATTACK_ACCURACY_UP: // hone claws ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK)); ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ACC)); diff --git a/test/battle/ai/can_use_all_moves.c b/test/battle/ai/can_use_all_moves.c index 3bf33a98c1b4..4d2ec08630e7 100644 --- a/test/battle/ai/can_use_all_moves.c +++ b/test/battle/ai/can_use_all_moves.c @@ -522,7 +522,6 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 601-700") switch (effect) { //TODO: AI HANDLING - case EFFECT_MAGNETIC_FLUX: case EFFECT_GEAR_UP: case EFFECT_FAIL_IF_NOT_ARG_TYPE: case EFFECT_STUFF_CHEEKS: @@ -541,6 +540,7 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 601-700") // tests exist elsewhere case EFFECT_ELECTRIC_TERRAIN: case EFFECT_PSYCHIC_TERRAIN: + case EFFECT_MAGNETIC_FLUX: case EFFECT_AURORA_VEIL: // Skipped on purpose. diff --git a/test/battle/move_effect/magnetic_flux.c b/test/battle/move_effect/magnetic_flux.c index f574db0089b5..6a23d2082f9b 100644 --- a/test/battle/move_effect/magnetic_flux.c +++ b/test/battle/move_effect/magnetic_flux.c @@ -2,3 +2,16 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Magnetic Flux (Move Effect) test titles") + +AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); } + } +} From 16fa6c8f711592ff7f8395ef65223492078fdc0c Mon Sep 17 00:00:00 2001 From: surskitty Date: Fri, 29 Aug 2025 04:10:59 -0400 Subject: [PATCH 12/25] AI uses Flower Shield. (#7640) --- src/battle_ai_main.c | 24 ++++++++++++++++++++++++ test/battle/ai/can_use_all_moves.c | 2 +- test/battle/move_effect/flower_shield.c | 16 ++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2532424d24a3..92a39a54a6c9 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -4224,6 +4224,30 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(AWFUL_EFFECT); } break; + case EFFECT_FLOWER_SHIELD: + if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS)) + { + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF)); + } + if (hasPartner && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), TYPE_GRASS)) + { + ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_DEF)); + } + if (IS_BATTLER_OF_TYPE(FOE(battlerAtk), TYPE_GRASS)) + { + if (aiData->abilities[FOE(battlerAtk)] == ABILITY_CONTRARY) + ADJUST_SCORE(WEAK_EFFECT); + else + ADJUST_SCORE(AWFUL_EFFECT); + } + if (IS_BATTLER_OF_TYPE(BATTLE_PARTNER(FOE(battlerAtk)), TYPE_GRASS)) + { + if (aiData->abilities[BATTLE_PARTNER(FOE(battlerAtk))] == ABILITY_CONTRARY) + ADJUST_SCORE(WEAK_EFFECT); + else + ADJUST_SCORE(AWFUL_EFFECT); + } + break; case EFFECT_HAZE: if (AnyStatIsRaised(BATTLE_PARTNER(battlerAtk)) || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) diff --git a/test/battle/ai/can_use_all_moves.c b/test/battle/ai/can_use_all_moves.c index 4d2ec08630e7..83ee14affa07 100644 --- a/test/battle/ai/can_use_all_moves.c +++ b/test/battle/ai/can_use_all_moves.c @@ -456,7 +456,6 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 501-600") case EFFECT_ION_DELUGE: case EFFECT_AROMATIC_MIST: case EFFECT_POWDER: - case EFFECT_FLOWER_SHIELD: case EFFECT_ELECTRIFY: //TODO: AI TESTS @@ -467,6 +466,7 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 501-600") case EFFECT_FAIRY_LOCK: // tests exist elsewhere + case EFFECT_FLOWER_SHIELD: case EFFECT_ROTOTILLER: case EFFECT_GRASSY_TERRAIN: case EFFECT_MISTY_TERRAIN: diff --git a/test/battle/move_effect/flower_shield.c b/test/battle/move_effect/flower_shield.c index 9eac08eb4d4f..8e9e8e334c35 100644 --- a/test/battle/move_effect/flower_shield.c +++ b/test/battle/move_effect/flower_shield.c @@ -37,3 +37,19 @@ DOUBLE_BATTLE_TEST("Flower Shield raises the defense of all Grass-type Pokémon" } TO_DO_BATTLE_TEST("Flower Shield fails if there's no Grass-type Pokémon on the field") + +AI_DOUBLE_BATTLE_TEST("AI uses Flower Shield") +{ + GIVEN { + ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS); + ASSUME(GetSpeciesType(SPECIES_WOBBUFFET, 0) != TYPE_GRASS); + ASSUME(GetSpeciesType(SPECIES_WOBBUFFET, 1) != TYPE_GRASS); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_TANGELA) { Moves(MOVE_FLOWER_SHIELD, MOVE_POUND); } + OPPONENT(SPECIES_TANGELA); + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_FLOWER_SHIELD); } + } +} From 3ff23ef212b2bdf8a4e1c080d8ad37ed93b198ca Mon Sep 17 00:00:00 2001 From: surskitty Date: Fri, 29 Aug 2025 14:49:28 -0400 Subject: [PATCH 13/25] AI uses Life Dew. (#7643) --- src/battle_ai_main.c | 20 ++++++++++++++++++++ test/battle/ai/can_use_all_moves.c | 2 +- test/battle/move_effect/life_dew.c | 27 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 92a39a54a6c9..065951bb2aed 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2165,6 +2165,20 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) else if ((AI_GetWeather() & (B_WEATHER_LOW_LIGHT))) ADJUST_SCORE(-3); break; + case EFFECT_LIFE_DEW: + if (AI_BattlerAtMaxHp(battlerAtk)) + { + if (hasPartner) + { + if (AI_BattlerAtMaxHp(BATTLE_PARTNER(battlerAtk))) + ADJUST_SCORE(-10); + } + else + { + ADJUST_SCORE(-10); + } + } + break; case EFFECT_PURIFY: if (!(gBattleMons[battlerDef].status1 & STATUS1_ANY)) ADJUST_SCORE(-10); @@ -4310,6 +4324,12 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) if (ShouldRecover(battlerAtk, battlerDef, move, 50)) ADJUST_SCORE(GOOD_EFFECT); break; + case EFFECT_LIFE_DEW: + if (ShouldRecover(battlerAtk, battlerDef, move, 25)) + ADJUST_SCORE(GOOD_EFFECT); + if (ShouldRecover(BATTLE_PARTNER(battlerAtk), battlerDef, move, 25)) + ADJUST_SCORE(GOOD_EFFECT); + break; case EFFECT_LIGHT_SCREEN: case EFFECT_REFLECT: case EFFECT_AURORA_VEIL: diff --git a/test/battle/ai/can_use_all_moves.c b/test/battle/ai/can_use_all_moves.c index 83ee14affa07..4ebec9394ee1 100644 --- a/test/battle/ai/can_use_all_moves.c +++ b/test/battle/ai/can_use_all_moves.c @@ -595,7 +595,6 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 701-800") { //TODO: AI HANDLING case EFFECT_CLANGOROUS_SOUL: - case EFFECT_LIFE_DEW: case EFFECT_POLTERGEIST: case EFFECT_COACHING: case EFFECT_REVIVAL_BLESSING: @@ -611,6 +610,7 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 701-800") // tests exist elsewhere case EFFECT_COURT_CHANGE: case EFFECT_DOODLE: + case EFFECT_LIFE_DEW: // Skipped on purpose. case EFFECT_PROTECT: diff --git a/test/battle/move_effect/life_dew.c b/test/battle/move_effect/life_dew.c index 493c4c73e74f..1969f3454c91 100644 --- a/test/battle/move_effect/life_dew.c +++ b/test/battle/move_effect/life_dew.c @@ -84,3 +84,30 @@ DOUBLE_BATTLE_TEST("Life Dew only works on partner if user is at full hp") HP_BAR(playerRight); } } + +AI_SINGLE_BATTLE_TEST("AI uses Life Dew if it outheals your damage and outspeeds (singles)") +{ + PASSES_RANDOMLY(100, 100, RNG_AI_SHOULD_RECOVER); + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Moves(MOVE_SCALD, MOVE_LIFE_DEW); HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_LIFE_DEW); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI uses Life Dew if it outheals your damage and outspeeds (doubles)") +{ + PASSES_RANDOMLY(100, 100, RNG_AI_SHOULD_RECOVER); + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_TACKLE); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Moves(MOVE_SCALD, MOVE_LIFE_DEW); HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Moves(MOVE_SCALD); HP(1); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE); MOVE(playerRight, MOVE_TACKLE); EXPECT_MOVE(opponentLeft, MOVE_LIFE_DEW); } + } +} + From e1381dd7b5118069422080f4ac69365156d68ca9 Mon Sep 17 00:00:00 2001 From: surskitty Date: Fri, 29 Aug 2025 14:49:50 -0400 Subject: [PATCH 14/25] AI uses Gear Up. (#7641) --- src/battle_ai_main.c | 23 +++++++++++++++++------ test/battle/ai/can_use_all_moves.c | 4 ++-- test/battle/move_effect/gear_up.c | 13 +++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 065951bb2aed..8961f52b0701 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1526,12 +1526,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-8); break; } - else if (!hasPartner) - { - ADJUST_SCORE(-10); // no partner and our stats wont rise, so don't use - } - - if (hasPartner) + else if (hasPartner) { if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) { @@ -1544,6 +1539,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); // nor our or our partner's ability is plus/minus } } + else + { + ADJUST_SCORE(-10); // no partner and our stats wont rise, so don't use + } break; case EFFECT_ACUPRESSURE: if (DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || AreBattlersStatsMaxed(battlerDef)) @@ -4191,6 +4190,18 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(-2); // Should be either removed or turned into increasing score case EFFECT_ACUPRESSURE: break; + case EFFECT_GEAR_UP: + if (aiData->abilities[battlerAtk] == ABILITY_PLUS || aiData->abilities[battlerAtk] == ABILITY_MINUS) + { + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK)); + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK)); + } + if (hasPartner && (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS)) + { + ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_ATK)); + ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_SPATK)); + } + break; case EFFECT_MAGNETIC_FLUX: if (aiData->abilities[battlerAtk] == ABILITY_PLUS || aiData->abilities[battlerAtk] == ABILITY_MINUS) { diff --git a/test/battle/ai/can_use_all_moves.c b/test/battle/ai/can_use_all_moves.c index 4ebec9394ee1..f91658b3f7ef 100644 --- a/test/battle/ai/can_use_all_moves.c +++ b/test/battle/ai/can_use_all_moves.c @@ -522,7 +522,6 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 601-700") switch (effect) { //TODO: AI HANDLING - case EFFECT_GEAR_UP: case EFFECT_FAIL_IF_NOT_ARG_TYPE: case EFFECT_STUFF_CHEEKS: case EFFECT_NO_RETREAT: @@ -540,8 +539,9 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 601-700") // tests exist elsewhere case EFFECT_ELECTRIC_TERRAIN: case EFFECT_PSYCHIC_TERRAIN: - case EFFECT_MAGNETIC_FLUX: case EFFECT_AURORA_VEIL: + case EFFECT_GEAR_UP: + case EFFECT_MAGNETIC_FLUX: // Skipped on purpose. case EFFECT_PROTECT: diff --git a/test/battle/move_effect/gear_up.c b/test/battle/move_effect/gear_up.c index 03aba9a39582..051205a2ba57 100644 --- a/test/battle/move_effect/gear_up.c +++ b/test/battle/move_effect/gear_up.c @@ -2,3 +2,16 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Gear Up (Move Effect) test titles") + +AI_DOUBLE_BATTLE_TEST("AI uses Gear Up") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); } + } +} From 70068bf314ac6bb4ab9547b04494a2e59834c30b Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Sat, 30 Aug 2025 21:39:02 +0100 Subject: [PATCH 15/25] Battle debug menu now checks correct parties depending on battler party (#7652) --- src/battle_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/battle_debug.c b/src/battle_debug.c index 149cb4065ce9..e350638aa588 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -752,7 +752,8 @@ static void PutMovesPointsText(struct BattleDebugMenu *data) if (gAiLogicData->shouldSwitch & (1u << data->aiBattlerId)) { - u32 switchMon = GetMonData(&gEnemyParty[gAiLogicData->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES); + struct Pokemon *party = GetBattlerParty(data->aiBattlerId); + u32 switchMon = GetMonData(&party[gAiLogicData->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, COMPOUND_STRING("Switching to "), 74, 64, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, gSpeciesInfo[switchMon].speciesName, 74 + 68, 64, 0, NULL); } From 4fa26b2296fa9e3f157c3b7e1e8a046c8748c10b Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 30 Aug 2025 16:40:44 -0400 Subject: [PATCH 16/25] Improved move additional effect handling; now accounts for Shield Dust. (#7650) --- src/battle_ai_main.c | 28 +++++++++++++++++++++++----- src/battle_ai_util.c | 4 ++++ test/battle/ai/ai_check_viability.c | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 8961f52b0701..91e8046b7c99 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5661,7 +5661,8 @@ case EFFECT_GUARD_SPLIT: break; case MOVE_EFFECT_SPD_MINUS_1: case MOVE_EFFECT_SPD_MINUS_2: - ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, STAT_SPEED)); + if (CanLowerStat(battlerAtk, battlerDef, aiData, STAT_SPEED)) + ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, STAT_SPEED)); break; case MOVE_EFFECT_ATK_MINUS_1: case MOVE_EFFECT_DEF_MINUS_1: @@ -5669,18 +5670,35 @@ case EFFECT_GUARD_SPLIT: case MOVE_EFFECT_SP_DEF_MINUS_1: case MOVE_EFFECT_ACC_MINUS_1: case MOVE_EFFECT_EVS_MINUS_1: - if (aiData->abilities[battlerDef] != ABILITY_CONTRARY) - ADJUST_SCORE(DECENT_EFFECT); + { + u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1; + if (CanLowerStat(battlerAtk, battlerDef, aiData, statId)) + ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); break; + } case MOVE_EFFECT_ATK_MINUS_2: case MOVE_EFFECT_DEF_MINUS_2: case MOVE_EFFECT_SP_ATK_MINUS_2: case MOVE_EFFECT_SP_DEF_MINUS_2: case MOVE_EFFECT_ACC_MINUS_2: case MOVE_EFFECT_EVS_MINUS_2: - if (aiData->abilities[battlerDef] != ABILITY_CONTRARY) - ADJUST_SCORE(DECENT_EFFECT); + { + u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2; + if (CanLowerStat(battlerAtk, battlerDef, aiData, statId)) + ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); break; + } + case MOVE_EFFECT_LOWER_ATTACK_SIDE: + case MOVE_EFFECT_LOWER_DEFENSE_SIDE: + case MOVE_EFFECT_LOWER_SPEED_SIDE: + case MOVE_EFFECT_LOWER_SP_ATK_SIDE: + case MOVE_EFFECT_LOWER_SP_DEF_SIDE: + { + u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_LOWER_ATTACK_SIDE; + if (CanLowerStat(battlerAtk, battlerDef, aiData, statId)) + ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); + break; + } case MOVE_EFFECT_POISON: IncreasePoisonScore(battlerAtk, battlerDef, move, &score); break; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 84d247a617cc..c3d4b669659d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2056,6 +2056,10 @@ bool32 CanLowerStat(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData, case ABILITY_WHITE_SMOKE: case ABILITY_FULL_METAL_BODY: return FALSE; + case ABILITY_SHIELD_DUST: + if (!IsBattleMoveStatus(move) && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) + return FALSE; + break; default: break; } diff --git a/test/battle/ai/ai_check_viability.c b/test/battle/ai/ai_check_viability.c index 4dff0e08ca0b..6d16d4fb6eb6 100644 --- a/test/battle/ai/ai_check_viability.c +++ b/test/battle/ai/ai_check_viability.c @@ -391,3 +391,21 @@ AI_SINGLE_BATTLE_TEST("AI uses Wide Guard against Earthquake when opponent would TURN { MOVE(player, MOVE_EARTHQUAKE); EXPECT_MOVE(opponent, MOVE_WIDE_GUARD); } } } + +AI_SINGLE_BATTLE_TEST("AI sees Shield Dust immunity to additional effects") +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_SHIELD_DUST; } + PARAMETRIZE { ability = ABILITY_TINTED_LENS; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_VENOMOTH) { Ability(ability); Moves(MOVE_CELEBRATE, MOVE_POUND); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CHILLING_WATER, MOVE_BRINE); } + } WHEN { + if (ability == ABILITY_SHIELD_DUST) + TURN { EXPECT_MOVE(opponent, MOVE_BRINE); } + else + TURN { EXPECT_MOVE(opponent, MOVE_CHILLING_WATER); } + } +} From cdaba9076edab66247b06d6006a8c873de2b7013 Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 30 Aug 2025 17:18:47 -0400 Subject: [PATCH 17/25] Correcting test AI won't use status moves if partner chose Helping Hand (#7649) --- test/battle/ai/ai_doubles.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index ff880fb2e23c..444f065a944c 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -69,11 +69,11 @@ AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose He } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); - PLAYER(SPECIES_WOBBUFFET); - PLAYER(SPECIES_WOBBUFFET); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, statusMove, MOVE_WATER_GUN); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, statusMove, MOVE_WATER_GUN); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HELPING_HAND); } - OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SCRATCH, statusMove); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SCRATCH, statusMove, MOVE_WATER_GUN); } } WHEN { TURN { NOT_EXPECT_MOVE(opponentRight, statusMove); SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:playerLeft); From 674cd136595cb5cd700c0bc869f0b85c75a227fa Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Sun, 31 Aug 2025 14:53:23 -0400 Subject: [PATCH 18/25] Added sortType to Douse Drive (#7664) --- src/data/items.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/items.h b/src/data/items.h index d86a610eb4b1..9927191d6495 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -5033,6 +5033,7 @@ const struct Item gItemsInfo[] = "Techno Blast to\n" "Water-type."), .pocket = POCKET_ITEMS, + .sortType = ITEM_TYPE_DRIVE, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .secondaryId = TYPE_WATER, From 406e5b1def8c08d47de86b5be0148e4af5e120b9 Mon Sep 17 00:00:00 2001 From: surskitty Date: Mon, 1 Sep 2025 13:02:56 -0400 Subject: [PATCH 19/25] IncreaseStatUpScore adjustments for Simple, +3 moves, Acupressure, max move effects (#7662) --- include/battle_ai_main.h | 5 ++ src/battle_ai_main.c | 23 ++++++++- src/battle_ai_util.c | 109 +++++++++++++++++++++++++-------------- 3 files changed, 96 insertions(+), 41 deletions(-) diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index b3bc57c4e9b6..4330f9d2bca0 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -23,6 +23,11 @@ enum StatChange STAT_CHANGE_SPEED_2, STAT_CHANGE_SPATK_2, STAT_CHANGE_SPDEF_2, + STAT_CHANGE_ATK_3, + STAT_CHANGE_DEF_3, + STAT_CHANGE_SPEED_3, + STAT_CHANGE_SPATK_3, + STAT_CHANGE_SPDEF_3, STAT_CHANGE_ACC, STAT_CHANGE_EVASION }; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 91e8046b7c99..8d9397fec7bb 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -4119,13 +4119,15 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK_2)); break; case EFFECT_DEFENSE_UP: - case EFFECT_DEFENSE_UP_3: ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF)); break; case EFFECT_STUFF_CHEEKS: case EFFECT_DEFENSE_UP_2: ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF_2)); break; + case EFFECT_DEFENSE_UP_3: + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF_3)); + break; case EFFECT_SPEED_UP: ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED)); break; @@ -4137,9 +4139,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK)); break; case EFFECT_SPECIAL_ATTACK_UP_2: - case EFFECT_SPECIAL_ATTACK_UP_3: ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK_2)); break; + case EFFECT_SPECIAL_ATTACK_UP_3: + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK_3)); + break; case EFFECT_SPECIAL_DEFENSE_UP: ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF)); break; @@ -4188,7 +4192,10 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_BIDE: if (aiData->hpPercents[battlerAtk] < 90) ADJUST_SCORE(-2); // Should be either removed or turned into increasing score + // treat as offense booster case EFFECT_ACUPRESSURE: + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK_2)); + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK_2)); break; case EFFECT_GEAR_UP: if (aiData->abilities[battlerAtk] == ABILITY_PLUS || aiData->abilities[battlerAtk] == ABILITY_MINUS) @@ -5688,6 +5695,18 @@ case EFFECT_GUARD_SPLIT: ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); break; } + case MOVE_EFFECT_RAISE_TEAM_ATTACK: + case MOVE_EFFECT_RAISE_TEAM_DEFENSE: + case MOVE_EFFECT_RAISE_TEAM_SPEED: + case MOVE_EFFECT_RAISE_TEAM_SP_ATK: + case MOVE_EFFECT_RAISE_TEAM_SP_DEF: + { + enum StatChange StageStatId = STAT_CHANGE_ATK + additionalEffect->moveEffect - MOVE_EFFECT_RAISE_TEAM_ATTACK; + ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, StageStatId)); + if (hasPartner) + ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, StageStatId)); + break; + } case MOVE_EFFECT_LOWER_ATTACK_SIDE: case MOVE_EFFECT_LOWER_DEFENSE_SIDE: case MOVE_EFFECT_LOWER_SPEED_SIDE: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index c3d4b669659d..71ecb3da321f 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -4480,18 +4480,23 @@ static u32 GetStatBeingChanged(enum StatChange statChange) { case STAT_CHANGE_ATK: case STAT_CHANGE_ATK_2: + case STAT_CHANGE_ATK_3: return STAT_ATK; case STAT_CHANGE_DEF: case STAT_CHANGE_DEF_2: + case STAT_CHANGE_DEF_3: return STAT_DEF; case STAT_CHANGE_SPEED: case STAT_CHANGE_SPEED_2: + case STAT_CHANGE_SPEED_3: return STAT_SPEED; case STAT_CHANGE_SPATK: case STAT_CHANGE_SPATK_2: + case STAT_CHANGE_SPATK_3: return STAT_SPATK; case STAT_CHANGE_SPDEF: case STAT_CHANGE_SPDEF_2: + case STAT_CHANGE_SPDEF_3: return STAT_SPDEF; case STAT_CHANGE_ACC: return STAT_ACC; @@ -4501,6 +4506,34 @@ static u32 GetStatBeingChanged(enum StatChange statChange) return 0; // STAT_HP, should never be getting changed } +static u32 GetStagesOfStatChange(enum StatChange statChange) +{ + switch(statChange) + { + case STAT_CHANGE_ATK: + case STAT_CHANGE_DEF: + case STAT_CHANGE_SPEED: + case STAT_CHANGE_SPATK: + case STAT_CHANGE_SPDEF: + case STAT_CHANGE_ACC: + case STAT_CHANGE_EVASION: + return 1; + case STAT_CHANGE_ATK_2: + case STAT_CHANGE_DEF_2: + case STAT_CHANGE_SPEED_2: + case STAT_CHANGE_SPATK_2: + case STAT_CHANGE_SPDEF_2: + return 2; + case STAT_CHANGE_ATK_3: + case STAT_CHANGE_DEF_3: + case STAT_CHANGE_SPEED_3: + case STAT_CHANGE_SPATK_3: + case STAT_CHANGE_SPDEF_3: + return 3; + } + return 0; // STAT_HP, should never be getting changed +} + static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, enum StatChange statChange, bool32 considerContrary) { enum AIScore tempScore = NO_INCREASE; @@ -4510,6 +4543,7 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, u32 shouldSetUp = ((noOfHitsToFaint >= 2 && aiIsFaster) || (noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS); u32 i; u32 statId = GetStatBeingChanged(statChange); + u32 stages = GetStagesOfStatChange(statChange); if (considerContrary && gAiLogicData->abilities[battlerAtk] == ABILITY_CONTRARY) return NO_INCREASE; @@ -4531,7 +4565,7 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, return NO_INCREASE; // Don't increase stats if opposing battler has Opportunist - if (gAiLogicData->abilities[battlerDef] == ABILITY_OPPORTUNIST) + if (HasBattlerSideAbility(battlerDef, ABILITY_OPPORTUNIST, gAiLogicData)) return NO_INCREASE; // Don't increase stats if opposing battler has Encore @@ -4556,6 +4590,10 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, if (HasMoveThatChangesKOThreshold(battlerDef, noOfHitsToFaint, aiIsFaster)) return NO_INCREASE; + // Stat stages are effectively doubled under Simple. + if (gAiLogicData->abilities[battlerAtk] == ABILITY_SIMPLE) + stages *= 2; + // Predicting switch if (IsBattlerPredictedToSwitch(battlerDef)) { @@ -4574,73 +4612,62 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, tempScore += WEAK_EFFECT; } - switch (statChange) + switch (statId) { - case STAT_CHANGE_ATK: + case STAT_ATK: if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && shouldSetUp) - tempScore += DECENT_EFFECT; + { + if (stages == 1) + tempScore += DECENT_EFFECT; + else + tempScore += GOOD_EFFECT; + } break; - case STAT_CHANGE_DEF: + case STAT_DEF: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)) { if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_STALL) - tempScore += DECENT_EFFECT; - else tempScore += WEAK_EFFECT; + if (stages == 1) + tempScore += WEAK_EFFECT; + else + tempScore += DECENT_EFFECT; } break; - case STAT_CHANGE_SPEED: + case STAT_SPEED: if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) - tempScore += DECENT_EFFECT; - break; - case STAT_CHANGE_SPATK: - if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL) && shouldSetUp) - tempScore += DECENT_EFFECT; - break; - case STAT_CHANGE_SPDEF: - if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)) { - if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_STALL) + if (stages == 1) tempScore += DECENT_EFFECT; else - tempScore += WEAK_EFFECT; + tempScore += GOOD_EFFECT; } break; - case STAT_CHANGE_ATK_2: - if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && shouldSetUp) - tempScore += GOOD_EFFECT; - break; - case STAT_CHANGE_DEF_2: - if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)) + case STAT_SPATK: + if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL) && shouldSetUp) { - if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_STALL) - tempScore += GOOD_EFFECT; - else + if (stages == 1) tempScore += DECENT_EFFECT; + else + tempScore += GOOD_EFFECT; } break; - case STAT_CHANGE_SPEED_2: - if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) - tempScore += GOOD_EFFECT; - break; - case STAT_CHANGE_SPATK_2: - if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL) && shouldSetUp) - tempScore += GOOD_EFFECT; - break; - case STAT_CHANGE_SPDEF_2: + case STAT_SPDEF: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)) { if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_STALL) - tempScore += GOOD_EFFECT; + tempScore += WEAK_EFFECT; + if (stages == 1) + tempScore += WEAK_EFFECT; else tempScore += DECENT_EFFECT; } break; - case STAT_CHANGE_ACC: + case STAT_ACC: if (gBattleMons[battlerAtk].statStages[statId] <= 3) // Increase only if necessary tempScore += DECENT_EFFECT; break; - case STAT_CHANGE_EVASION: + case STAT_EVASION: if (noOfHitsToFaint > 3 || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) tempScore += GOOD_EFFECT; else @@ -4648,6 +4675,10 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, break; } + // if already inclined to boost, be slightly more likely to if boost levels matter + if (tempScore > 0 && HasMoveWithEffect(battlerAtk, EFFECT_STORED_POWER)) + tempScore += WEAK_EFFECT; + return tempScore; } From 927f1898bb4c33f2151d077dd3c049fcf797291a Mon Sep 17 00:00:00 2001 From: surskitty Date: Mon, 1 Sep 2025 13:43:12 -0400 Subject: [PATCH 20/25] AI handling for Coaching. (#7661) --- src/battle_ai_main.c | 14 ++++++++++++++ test/battle/ai/ai_doubles.c | 5 +++-- test/battle/move_effect/coaching.c | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 8d9397fec7bb..dd837faffa0b 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3174,6 +3174,20 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) || HasMoveWithCriticalHitChance(battlerAtkPartner)) ADJUST_SCORE(GOOD_EFFECT); break; + case EFFECT_COACHING: + if (!hasPartner + || !HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)) + { + ADJUST_SCORE(WORST_EFFECT); + } + else + { + ADJUST_SCORE(IncreaseStatUpScore(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtk), STAT_CHANGE_ATK)); + ADJUST_SCORE(IncreaseStatUpScore(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtk), STAT_CHANGE_DEF)); + ADJUST_SCORE(IncreaseStatUpScore(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtkPartner), STAT_CHANGE_ATK)); + ADJUST_SCORE(IncreaseStatUpScore(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtkPartner), STAT_CHANGE_DEF)); + } + break; default: break; } // our effect relative to partner diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index 444f065a944c..fcef1f9d0afc 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -72,10 +72,11 @@ AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose He AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, statusMove, MOVE_WATER_GUN); } PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, statusMove, MOVE_WATER_GUN); } - OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HELPING_HAND); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HELPING_HAND, MOVE_EXPLOSION); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SCRATCH, statusMove, MOVE_WATER_GUN); } } WHEN { - TURN { NOT_EXPECT_MOVE(opponentRight, statusMove); + TURN { EXPECT_MOVE(opponentLeft, MOVE_HELPING_HAND); + NOT_EXPECT_MOVE(opponentRight, statusMove); SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:playerLeft); SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:playerRight); SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:opponentLeft); diff --git a/test/battle/move_effect/coaching.c b/test/battle/move_effect/coaching.c index 7245f8854bd8..642727de51ce 100644 --- a/test/battle/move_effect/coaching.c +++ b/test/battle/move_effect/coaching.c @@ -117,3 +117,23 @@ DOUBLE_BATTLE_TEST("Coaching fails if there's no ally") MESSAGE("But it failed!"); } } + +AI_DOUBLE_BATTLE_TEST("AI uses Coaching") +{ + u32 move; + PARAMETRIZE { move = MOVE_HEADBUTT; } + PARAMETRIZE { move = MOVE_DAZZLING_GLEAM; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_COACHING, MOVE_POUND); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(move); } + } WHEN { + if (move == MOVE_HEADBUTT) + TURN { EXPECT_MOVE(opponentLeft, MOVE_COACHING); } + else + TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_COACHING); } + } +} From 0a0d574998d854cba9a2a1d8becfc97ed9e1b964 Mon Sep 17 00:00:00 2001 From: surskitty Date: Mon, 1 Sep 2025 15:34:31 -0400 Subject: [PATCH 21/25] Simplifying calls to IsBattlerTrapped; treats being unable to switch as trappedness (#7671) --- src/battle_ai_main.c | 12 ++++++------ src/battle_ai_util.c | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index dd837faffa0b..7c0c98bf4630 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2979,7 +2979,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) else if (GetBattleMoveCategory(move) == DAMAGE_CATEGORY_STATUS && (CountUsablePartyMons(battlerAtk) < 1 || gAiLogicData->mostSuitableMonId[battlerAtk] == PARTY_SIZE - || (!AI_CanBattlerEscape(battlerAtk) && IsBattlerTrapped(battlerDef, battlerAtk)))) + || IsBattlerTrapped(battlerDef, battlerAtk))) ADJUST_SCORE(-30); } @@ -4432,7 +4432,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) break; ADJUST_SCORE(GOOD_EFFECT); if (!HasDamagingMove(battlerDef) - || (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)) + || IsBattlerTrapped(battlerAtk, battlerDef) || aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) ADJUST_SCORE(DECENT_EFFECT); break; @@ -4542,7 +4542,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_CURSE: if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST)) { - if (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)) + if (IsBattlerTrapped(battlerAtk, battlerDef)) ADJUST_SCORE(GOOD_EFFECT); else ADJUST_SCORE(WEAK_EFFECT); @@ -4640,7 +4640,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); break; case EFFECT_PERISH_SONG: - if (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)) + if (IsBattlerTrapped(battlerAtk, battlerDef)) ADJUST_SCORE(GOOD_EFFECT); break; case EFFECT_SANDSTORM: @@ -4799,7 +4799,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) break; // Don't use if the attract won't have a change to activate if (gBattleMons[battlerDef].status1 & STATUS1_ANY || gBattleMons[battlerDef].volatiles.confusionTurns > 0 - || (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef))) + || IsBattlerTrapped(battlerAtk, battlerDef)) ADJUST_SCORE(GOOD_EFFECT); else ADJUST_SCORE(DECENT_EFFECT); @@ -6049,7 +6049,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor || CountUsablePartyMons(battlerAtk) == 0 || !IsBattleMoveStatus(move) || !HasMoveWithEffect(battlerAtk, EFFECT_BATON_PASS) - || (!AI_CanBattlerEscape(battlerAtk) && IsBattlerTrapped(battlerDef, battlerAtk))) + || IsBattlerTrapped(battlerAtk, battlerDef)) return score; enum BattleMoveEffects effect = GetMoveEffect(move); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 71ecb3da321f..3997797b134c 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -469,6 +469,9 @@ bool32 AI_CanBattlerEscape(u32 battler) bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef) { + if (AI_CanBattlerEscape(battlerDef)) + return FALSE; + if (gBattleMons[battlerDef].volatiles.wrapped) return TRUE; if (gBattleMons[battlerDef].volatiles.escapePrevention) @@ -489,6 +492,9 @@ bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef) && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_STEEL)) return TRUE; + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && CountUsablePartyMons(battlerDef) == 0) + return TRUE; + return FALSE; } From 84f5aa6dae62da212efa2e8de11585bad189a031 Mon Sep 17 00:00:00 2001 From: surskitty Date: Mon, 1 Sep 2025 15:43:04 -0400 Subject: [PATCH 22/25] Helping Hand bugfix; used FOE instead of BATTLE_OPPOSITE. (#7659) --- src/battle_ai_main.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index ebbabf93c897..0623faf8644e 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1,3 +1,5 @@ +// Note that FOE specifically returns the left-side battler; BATTLE_OPPOSITE is the diagonal. + #include "global.h" #include "main.h" #include "malloc.h" @@ -3098,45 +3100,44 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } else { - u32 ownHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtk, FOE(battlerAtk), AI_ATTACKING); - u32 partnerHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtkPartner, FOE(battlerAtk), AI_ATTACKING); - u32 ownHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtk, FOE(battlerAtkPartner), AI_ATTACKING); - u32 partnerHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtkPartner, FOE(battlerAtkPartner), AI_ATTACKING); + u32 ownHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtk, BATTLE_OPPOSITE(battlerAtk), AI_ATTACKING); + u32 partnerHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtk), AI_ATTACKING); + u32 ownHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtk, BATTLE_OPPOSITE(battlerAtkPartner), AI_ATTACKING); + u32 partnerHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtkPartner), AI_ATTACKING); if (hasTwoOpponents) { // Might be about to die - if (CanTargetFaintAi(FOE(battlerAtk), battlerAtk) && CanTargetFaintAi(FOE(battlerAtkPartner), battlerAtk) - && AI_IsSlower(battlerAtk, FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY) - && AI_IsSlower(battlerAtk, FOE(battlerAtkPartner), move, predictedMove, DONT_CONSIDER_PRIORITY)) + if (CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtk), battlerAtk) && CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtkPartner), battlerAtk) + && AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY) + && AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtkPartner), move, predictedMove, DONT_CONSIDER_PRIORITY)) ADJUST_SCORE(GOOD_EFFECT); if (ownHitsToKOFoe1 > partnerHitsToKOFoe1 && partnerHitsToKOFoe1 > 1 && ownHitsToKOFoe2 > partnerHitsToKOFoe2 && partnerHitsToKOFoe2 > 1) ADJUST_SCORE(GOOD_EFFECT); } - else if (IsBattlerAlive(FOE(battlerAtk))) + else if (IsBattlerAlive(BATTLE_OPPOSITE(battlerAtk))) { // Might be about to die - if (CanTargetFaintAi(FOE(battlerAtk), battlerAtk) - && AI_IsSlower(battlerAtk, FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)) + if (CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtk), battlerAtk) + && AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)) ADJUST_SCORE(GOOD_EFFECT); if (ownHitsToKOFoe1 > partnerHitsToKOFoe1 && partnerHitsToKOFoe1 > 1) ADJUST_SCORE(GOOD_EFFECT); } - else if (IsBattlerAlive(FOE(battlerAtkPartner))) + else if (IsBattlerAlive(BATTLE_OPPOSITE(battlerAtkPartner))) { // Might be about to die - if (CanTargetFaintAi(FOE(battlerAtkPartner), battlerAtk) - && AI_IsSlower(battlerAtk, FOE(battlerAtkPartner), move, predictedMove, DONT_CONSIDER_PRIORITY)) + if (CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtkPartner), battlerAtk) + && AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtkPartner), move, predictedMove, DONT_CONSIDER_PRIORITY)) ADJUST_SCORE(GOOD_EFFECT); if (ownHitsToKOFoe2 > partnerHitsToKOFoe2 && partnerHitsToKOFoe2 > 1) ADJUST_SCORE(GOOD_EFFECT); - } - + } } break; case EFFECT_PERISH_SONG: From 2f75c6a264760946a22b385679cd3547ac0a1fd8 Mon Sep 17 00:00:00 2001 From: surskitty Date: Tue, 2 Sep 2025 05:31:35 -0400 Subject: [PATCH 23/25] Score adjustments towards guaranteed stat drops. (#7670) --- src/battle_ai_main.c | 12 ++++++++++-- src/battle_ai_util.c | 16 +++++++++------- test/battle/ability/minds_eye.c | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 7c0c98bf4630..e0fb048d9bfc 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5685,11 +5685,20 @@ case EFFECT_GUARD_SPLIT: if (CanLowerStat(battlerAtk, battlerDef, aiData, STAT_SPEED)) ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, STAT_SPEED)); break; + case MOVE_EFFECT_ACC_MINUS_1: + case MOVE_EFFECT_ACC_MINUS_2: + if (CanLowerStat(battlerAtk, battlerDef, aiData, STAT_ACC)) + { + u32 scoreIncrease = IncreaseStatDownScore(battlerAtk, battlerDef, STAT_ACC); + if (scoreIncrease == WEAK_EFFECT) + scoreIncrease = DECENT_EFFECT; + ADJUST_SCORE(scoreIncrease); + } + break; case MOVE_EFFECT_ATK_MINUS_1: case MOVE_EFFECT_DEF_MINUS_1: case MOVE_EFFECT_SP_ATK_MINUS_1: case MOVE_EFFECT_SP_DEF_MINUS_1: - case MOVE_EFFECT_ACC_MINUS_1: case MOVE_EFFECT_EVS_MINUS_1: { u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1; @@ -5701,7 +5710,6 @@ case EFFECT_GUARD_SPLIT: case MOVE_EFFECT_DEF_MINUS_2: case MOVE_EFFECT_SP_ATK_MINUS_2: case MOVE_EFFECT_SP_DEF_MINUS_2: - case MOVE_EFFECT_ACC_MINUS_2: case MOVE_EFFECT_EVS_MINUS_2: { u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 3997797b134c..04eba48677cf 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2113,13 +2113,15 @@ u32 IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, u32 stat) tempScore += DECENT_EFFECT; break; case STAT_DEF: - if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL)) + if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) + || HasMoveWithCategory(BATTLE_PARTNER(battlerAtk), DAMAGE_CATEGORY_PHYSICAL)) tempScore += DECENT_EFFECT; break; case STAT_SPEED: { u32 predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); - if (AI_IsSlower(battlerAtk, battlerDef, gAiThinkingStruct->moveConsidered, predictedMoveSpeedCheck, DONT_CONSIDER_PRIORITY)) + if (AI_IsSlower(battlerAtk, battlerDef, MOVE_NONE, predictedMoveSpeedCheck, DONT_CONSIDER_PRIORITY) + || AI_IsSlower(BATTLE_PARTNER(battlerAtk), battlerDef, MOVE_NONE, predictedMoveSpeedCheck, DONT_CONSIDER_PRIORITY)) tempScore += DECENT_EFFECT; break; } @@ -2128,16 +2130,16 @@ u32 IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, u32 stat) tempScore += DECENT_EFFECT; break; case STAT_SPDEF: - if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL)) + if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL) + || HasMoveWithCategory(BATTLE_PARTNER(battlerAtk), DAMAGE_CATEGORY_SPECIAL)) tempScore += DECENT_EFFECT; break; case STAT_ACC: - if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) - tempScore += WEAK_EFFECT; + tempScore += WEAK_EFFECT; + if (IsBattlerTrapped(battlerAtk, battlerDef)) + tempScore += DECENT_EFFECT; if (gBattleMons[battlerDef].volatiles.leechSeed) tempScore += WEAK_EFFECT; - if (gBattleMons[battlerDef].volatiles.root) - tempScore += WEAK_EFFECT; if (gBattleMons[battlerDef].volatiles.cursed) tempScore += WEAK_EFFECT; break; diff --git a/test/battle/ability/minds_eye.c b/test/battle/ability/minds_eye.c index 0300c2e4d521..3772a9df1dd9 100644 --- a/test/battle/ability/minds_eye.c +++ b/test/battle/ability/minds_eye.c @@ -60,7 +60,7 @@ AI_SINGLE_BATTLE_TEST("AI doesn't use accuracy-lowering moves if it knows that t if (abilityAI == ABILITY_MOLD_BREAKER) { SCORE_GT(opponent, MOVE_SAND_ATTACK, MOVE_CELEBRATE); } else { - SCORE_EQ(opponent, MOVE_SAND_ATTACK, MOVE_CELEBRATE); + SCORE_LT_VAL(opponent, MOVE_SAND_ATTACK, AI_SCORE_DEFAULT); } } } SCENE { From cfcdd5f776857a28d2b23d6cf26f0ad56a26a0b9 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 2 Sep 2025 14:59:33 +0200 Subject: [PATCH 24/25] Fix crashing tests duplicating tests from another thread (#7683) Co-authored-by: Hedara --- test/test_runner.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_runner.c b/test/test_runner.c index fe9a5eccb6aa..218061d63f0c 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -235,6 +235,8 @@ void CB2_TestRunner(void) } else { + // Cost must be assigned to the test that crashed, otherwise tests will be desynched + AssignCostToRunner(); gTestRunnerState.state = STATE_REPORT_RESULT; gTestRunnerState.result = TEST_RESULT_CRASH; } From 619ee562fad99e45cbfedbc6d7f629c3731bdb9c Mon Sep 17 00:00:00 2001 From: Hedara Date: Tue, 2 Sep 2025 15:18:41 +0200 Subject: [PATCH 25/25] Mark failing test that was masked --- test/battle/ai/ai_doubles.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index fcef1f9d0afc..b867eca0872e 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -621,6 +621,7 @@ AI_DOUBLE_BATTLE_TEST("AI uses Helping Hand if it's about to die") AI_DOUBLE_BATTLE_TEST("AI uses Helping Hand if the ally does notably more damage") { + KNOWN_FAILING; // Failure was masked by test runner issues GIVEN { ASSUME(GetMoveEffect(MOVE_HELPING_HAND) == EFFECT_HELPING_HAND); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT);