From 34ba9b4e0d4f710f4f0961a7b73902266c06f38a Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 28 Nov 2022 14:36:19 -0500 Subject: [PATCH 01/88] convert a few various to callnatives --- asm/macros/battle_script.inc | 50 ++++++------ include/constants/battle_script_commands.h | 5 -- src/battle_script_commands.c | 95 ++++++++++++++-------- src/battle_z_move.c | 4 +- 4 files changed, 88 insertions(+), 66 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 27c5a86c41..0fa2212292 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1310,6 +1310,33 @@ callnative BS_CalcMetalBurstDmg .4byte \ptr .endm + + .macro setzeffect + callnative BS_SetZEffect + .endm + + @ Used by effects that may proc Symbiosis but do not call removeitem. + .macro trysymbiosis + callnative BS_TrySymbiosis + .endm + + @ returns TRUE or FALSE to gBattleCommunication[0] + .macro canteleport battler:req + callnative BS_CanTeleport + .byte \battler + .endm + + @ returns B_SIDE_x to gBattleCommunication[0] + .macro getbattlerside battler:req + callnative BS_GetBattlerSide + .byte \battler + .endm + + .macro checkparentalbondcounter counter:req, ptr:req + callnative BS_CheckParentalBondCounter + .byte \counter + .4byte \ptr + .endm @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req @@ -1791,10 +1818,6 @@ various \battler, VARIOUS_TRY_ACTIVATE_GRIM_NEIGH .endm - .macro setzeffect - various BS_ATTACKER, VARIOUS_SET_Z_EFFECT - .endm - .macro consumeberry battler:req, frombattler:req various \battler, VARIOUS_CONSUME_BERRY .byte \frombattler @@ -1986,20 +2009,6 @@ various BS_ATTACKER, VARIOUS_SWAP_SIDE_STATUSES .endm - .macro canteleport battler:req - various \battler, VARIOUS_CAN_TELEPORT - .endm - - .macro getbattlerside battler:req - various \battler, VARIOUS_GET_BATTLER_SIDE - .endm - - .macro checkparentalbondcounter counter:req, ptr:req - various BS_ATTACKER, VARIOUS_CHECK_PARENTAL_BOND_COUNTER - .byte \counter - .4byte \ptr - .endm - @ helpful macros .macro setstatchanger stat:req, stages:req, down:req setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7 @@ -2152,8 +2161,3 @@ .macro skydropyawn various 0, VARIOUS_SKY_DROP_YAWN .endm - - @ Used by effects that may proc Symbiosis but do not call removeitem. - .macro trysymbiosis - various BS_ATTACKER, VARIOUS_TRY_SYMBIOSIS - .endm diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 06a09fc179..e8f25f77f3 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -241,11 +241,6 @@ #define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 150 #define VARIOUS_SET_BEAK_BLAST 151 #define VARIOUS_SWAP_SIDE_STATUSES 152 -#define VARIOUS_SET_Z_EFFECT 153 -#define VARIOUS_TRY_SYMBIOSIS 154 -#define VARIOUS_CAN_TELEPORT 155 -#define VARIOUS_GET_BATTLER_SIDE 156 -#define VARIOUS_CHECK_PARENTAL_BOND_COUNTER 157 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b7551ab9ee..770a5444a3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9434,9 +9434,6 @@ static void Cmd_various(void) gBattlescriptCurrInstr += 7; // exit if loop failed (failsafe) } return; - case VARIOUS_SET_Z_EFFECT: - SetZEffect(); //handles battle script jumping internally - return; case VARIOUS_MOVEEND_ITEM_EFFECTS: if (ItemBattleEffects(ITEMEFFECT_NORMAL, gActiveBattler, FALSE)) return; @@ -10035,39 +10032,6 @@ static void Cmd_various(void) case VARIOUS_SWAP_SIDE_STATUSES: CourtChangeSwapSideStatuses(); break; - case VARIOUS_TRY_SYMBIOSIS: //called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem. - if (SYMBIOSIS_CHECK(gActiveBattler, BATTLE_PARTNER(gActiveBattler))) - { - BestowItem(BATTLE_PARTNER(gActiveBattler), gActiveBattler); - gLastUsedAbility = gBattleMons[BATTLE_PARTNER(gActiveBattler)].ability; - gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(gActiveBattler); - gBattlerAttacker = gActiveBattler; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; - return; - } - break; - case VARIOUS_CAN_TELEPORT: - gBattleCommunication[0] = CanTeleport(gActiveBattler); - break; - case VARIOUS_GET_BATTLER_SIDE: - if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) - gBattleCommunication[0] = B_SIDE_PLAYER; - else - gBattleCommunication[0] = B_SIDE_OPPONENT; - break; - case VARIOUS_CHECK_PARENTAL_BOND_COUNTER: - { - // Some effects should only happen on the first or second strike of Parental Bond, - // so a way to check this in battle scripts is useful - u8 counter = T1_READ_8(gBattlescriptCurrInstr + 3); - if (gSpecialStatuses[gBattlerAttacker].parentalBondState == counter && gBattleMons[gBattlerTarget].hp != 0) - gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 4); - else - gBattlescriptCurrInstr += 8; - return; - } - break; } // End of switch (gBattlescriptCurrInstr[2]) gBattlescriptCurrInstr += 3; @@ -14861,3 +14825,62 @@ static bool8 IsFinalStrikeEffect(u16 move) } return FALSE; } + +// 10 bytes long (callnative(5) + counter(1) + ptr(4)) +void BS_CheckParentalBondCounter(void) +{ + // Some effects should only happen on the first or second strike of Parental Bond, + // so a way to check this in battle scripts is useful + u8 counter = T1_READ_8(gBattlescriptCurrInstr + 5); + if (gSpecialStatuses[gBattlerAttacker].parentalBondState == counter && gBattleMons[gBattlerTarget].hp != 0) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 6); + else + gBattlescriptCurrInstr += 10; +} + +// 6 bytes long (callnative(5) + battler(1)) +void BS_GetBattlerSide(void) +{ + u8 battler = gBattlescriptCurrInstr[5]; + if (GetBattlerSide(battler) == B_SIDE_PLAYER) + gBattleCommunication[0] = B_SIDE_PLAYER; + else + gBattleCommunication[0] = B_SIDE_OPPONENT; + + gBattlescriptCurrInstr += 6; +} + +// 6 bytes long (callnative(5) + battler(1)) +void BS_CanTeleport(void) +{ + u8 battler = gBattlescriptCurrInstr[5]; + gBattleCommunication[0] = CanTeleport(battler); + gBattlescriptCurrInstr += 6; +} + +// 5 bytes long +void BS_TrySymbiosis(void) +{ + //called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem. + gActiveBattler = gBattlerAttacker; + if (SYMBIOSIS_CHECK(gBattlerAttacker, BATTLE_PARTNER(gActiveBattler))) + { + BestowItem(BATTLE_PARTNER(gActiveBattler), gActiveBattler); + gLastUsedAbility = gBattleMons[BATTLE_PARTNER(gActiveBattler)].ability; + gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(gActiveBattler); + gBattlerAttacker = gActiveBattler; + BattleScriptPushCursor(gBattlescriptCurrInstr + 5); + gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; + } + else + { + gBattlescriptCurrInstr += 5; + } +} + +void BS_SetZEffect(void) +{ + SetZEffect(); // Handles battle script jumping internally +} + + diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 2eccd82f52..e27304853c 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -593,7 +593,7 @@ const u8 *GetZMoveName(u16 move) return gZMoveNames[0]; // Failsafe } -#define Z_EFFECT_BS_LENGTH 3 +#define Z_EFFECT_BS_LENGTH 5 // This function kinda cheats by setting a return battle script to after the setzeffect various command // and then jumping to a z effect script void SetZEffect(void) @@ -684,7 +684,7 @@ void SetZEffect(void) gBattlescriptCurrInstr = BattleScript_StatUpZMove; break; default: - gBattlescriptCurrInstr += 3; + gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH; break; } From cd7abea6fe50f9e75a4712ee5061b378707b4834 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 28 Nov 2022 14:47:56 -0500 Subject: [PATCH 02/88] fix BS_TrySymbiosis --- src/battle_script_commands.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 770a5444a3..55487eab76 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -14869,13 +14869,12 @@ void BS_TrySymbiosis(void) gLastUsedAbility = gBattleMons[BATTLE_PARTNER(gActiveBattler)].ability; gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(gActiveBattler); gBattlerAttacker = gActiveBattler; - BattleScriptPushCursor(gBattlescriptCurrInstr + 5); + BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; + return; } - else - { - gBattlescriptCurrInstr += 5; - } + + gBattlescriptCurrInstr += 5; } void BS_SetZEffect(void) From 4b799f2571bc6ba49c190e1bf908566d99992d95 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 28 Nov 2022 15:10:42 -0500 Subject: [PATCH 03/88] optimize BS_GetBattlerSide --- src/battle_script_commands.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 55487eab76..7ad6567373 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -14841,12 +14841,7 @@ void BS_CheckParentalBondCounter(void) // 6 bytes long (callnative(5) + battler(1)) void BS_GetBattlerSide(void) { - u8 battler = gBattlescriptCurrInstr[5]; - if (GetBattlerSide(battler) == B_SIDE_PLAYER) - gBattleCommunication[0] = B_SIDE_PLAYER; - else - gBattleCommunication[0] = B_SIDE_OPPONENT; - + gBattleCommunication[0] = GetBattlerSide(gBattlescriptCurrInstr[5]); gBattlescriptCurrInstr += 6; } From f6089a00578ca9e5779f30b4b64f94349f9234e3 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Tue, 7 Mar 2023 22:34:57 +0100 Subject: [PATCH 04/88] Mega Evolution after switching and based on speed --- data/battle_scripts_1.s | 8 +-- data/scripts/debug.inc | 1 + include/battle.h | 3 +- include/battle_util.h | 1 - src/battle_main.c | 143 +++++++++++++++++++++---------------- src/battle_util.c | 5 +- src/data/trainer_parties.h | 16 ++--- test/mega_evolution.c | 38 ++++++++++ 8 files changed, 138 insertions(+), 77 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8b93edb78a..ef4a739a54 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -561,7 +561,7 @@ BattleScript_ShellTrapSetUp:: playanimation BS_ATTACKER, B_ANIM_SHELL_TRAP_SETUP, NULL printstring STRINGID_PREPARESHELLTRAP waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_EffectShellTrap:: attackcanceler @@ -635,7 +635,7 @@ BattleScript_BeakBlastSetUp:: playanimation BS_ATTACKER, B_ANIM_BEAK_BLAST_SETUP, NULL printstring STRINGID_HEATUPBEAK waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_BeakBlastBurn:: setbyte cMULTISTRING_CHOOSER, 0 @@ -7825,7 +7825,7 @@ BattleScript_FocusPunchSetUp:: playanimation BS_ATTACKER, B_ANIM_FOCUS_PUNCH_SETUP printstring STRINGID_PKMNTIGHTENINGFOCUS waitmessage B_WAIT_TIME_LONG - end2 + end3 BattleScript_MegaEvolution:: printstring STRINGID_MEGAEVOREACTING @@ -7840,7 +7840,7 @@ BattleScript_MegaEvolutionAfeterString: printstring STRINGID_MEGAEVOEVOLVED waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER - end2 + end3 BattleScript_WishMegaEvolution:: printstring STRINGID_FERVENTWISHREACHED diff --git a/data/scripts/debug.inc b/data/scripts/debug.inc index 191fe69327..a6f68b1f70 100644 --- a/data/scripts/debug.inc +++ b/data/scripts/debug.inc @@ -55,6 +55,7 @@ Debug_FlagsNotSetMessage_Text: .string "'include/constants/overworld{UNDERSCORE}config.h'!$" Debug_Script_1:: + multi_fixed_2_vs_2 TRAINER_ROXANNE_2, Debug_FlagsNotSetMessage_Text, TRAINER_ROXANNE_3, Debug_FlagsNotSetMessage_Text, TRAINER_ROXANNE_4, 0 end Debug_Script_2:: diff --git a/include/battle.h b/include/battle.h index 83b552e7e1..1ff9ed8cef 100644 --- a/include/battle.h +++ b/include/battle.h @@ -535,7 +535,7 @@ struct BattleStruct u8 dynamicMoveType; u8 wrappedBy[MAX_BATTLERS_COUNT]; u16 assistPossibleMoves[PARTY_SIZE * MAX_MON_MOVES]; // Each of mons can know max 4 moves. - u8 focusPunchBattlerId; + u8 focusPunchBattlers; // as bits u8 battlerPreventingSwitchout; u8 moneyMultiplier:6; u8 moneyMultiplierItem:1; @@ -656,6 +656,7 @@ struct BattleStruct u8 attackerBeforeBounce:2; u8 beatUpSlot:3; bool8 hitSwitchTargetFailed:1; + bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects. u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit. u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching) bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face. diff --git a/include/battle_util.h b/include/battle_util.h index 0661081e43..327ca35ac2 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -195,7 +195,6 @@ bool32 IsHealBlockPreventingMove(u32 battler, u32 move); bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId); bool32 IsPartnerMonFromSameTrainer(u8 battlerId); u8 GetSplitBasedOnStats(u8 battlerId); -void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast); bool32 TestSheerForceFlag(u8 battler, u16 move); void TryRestoreStolenItems(void); bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item); diff --git a/src/battle_main.c b/src/battle_main.c index 9ea3b66446..4a9910e70e 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4773,9 +4773,6 @@ static void SetActionsAndBattlersTurnOrder(void) turnOrderId++; } } - gBattleMainFunc = CheckMegaEvolutionBeforeTurn; - gBattleStruct->mega.battlerId = 0; - return; } else { @@ -4821,8 +4818,8 @@ static void SetActionsAndBattlersTurnOrder(void) } } } - gBattleMainFunc = CheckMegaEvolutionBeforeTurn; - gBattleStruct->mega.battlerId = 0; + gBattleMainFunc = CheckQuickClaw_CustapBerryActivation; + gBattleStruct->quickClawBattlerId = 0; } static void TurnValuesCleanUp(bool8 var0) @@ -4871,48 +4868,96 @@ void SpecialStatusesClear(void) memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); } -static void CheckMegaEvolutionBeforeTurn(void) +static void PopulateArrayWithBattlers(u8 *battlers) { - if (!(gHitMarker & HITMARKER_RUN)) - { - while (gBattleStruct->mega.battlerId < gBattlersCount) - { - gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId; - gBattleStruct->mega.battlerId++; - if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler] - && !(gProtectStructs[gActiveBattler].noValidMoves)) - { - struct Pokemon *mon = GetBattlerPartyData(gActiveBattler); + u32 i; + for (i = 0; i < gBattlersCount; i++) + battlers[i] = i; +} +static bool32 TryDoMegaEvosBeforeMoves(void) +{ + if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->mega.toEvolve) + { + u32 i; + struct Pokemon *mon; + u8 megaOrder[MAX_BATTLERS_COUNT]; + + PopulateArrayWithBattlers(megaOrder); + SortBattlersBySpeed(megaOrder, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleStruct->mega.toEvolve & gBitTable[megaOrder[i]] + && !(gProtectStructs[megaOrder[i]].noValidMoves)) + { + gActiveBattler = gBattlerAttacker = megaOrder[i]; gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]); gLastUsedItem = gBattleMons[gActiveBattler].item; + mon = GetBattlerPartyData(gActiveBattler); if (GetWishMegaEvolutionSpecies(GetMonData(mon, MON_DATA_SPECIES), GetMonData(mon, MON_DATA_MOVE1), GetMonData(mon, MON_DATA_MOVE2), GetMonData(mon, MON_DATA_MOVE3), GetMonData(mon, MON_DATA_MOVE4))) BattleScriptExecute(BattleScript_WishMegaEvolution); else BattleScriptExecute(BattleScript_MegaEvolution); - return; + return TRUE; } } } - #if B_MEGA_EVO_TURN_ORDER <= GEN_6 - gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; - #else - gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved + #if B_MEGA_EVO_TURN_ORDER >= GEN_7 + TryChangeTurnOrder(); // This will just do nothing if no mon has mega evolved. #endif + return FALSE; +} + +static bool32 TryDoMoveEffectsBeforeMoves(void) +{ + if (!(gHitMarker & HITMARKER_RUN)) + { + u32 i; + struct Pokemon *mon; + u8 battlers[MAX_BATTLERS_COUNT]; + + PopulateArrayWithBattlers(battlers); + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + if (!(gBattleStruct->focusPunchBattlers & gBitTable[battlers[i]]) + && !(gBattleMons[battlers[i]].status1 & STATUS1_SLEEP) + && !(gDisableStructs[battlers[i]].truantCounter) + && !(gProtectStructs[battlers[i]].noValidMoves)) + { + gBattleStruct->focusPunchBattlers |= gBitTable[battlers[i]]; + gActiveBattler = gBattlerAttacker = battlers[i]; + switch (gChosenMoveByBattler[gActiveBattler]) + { + case MOVE_FOCUS_PUNCH: + BattleScriptExecute(BattleScript_FocusPunchSetUp); + return TRUE; + case MOVE_BEAK_BLAST: + BattleScriptExecute(BattleScript_BeakBlastSetUp); + return TRUE; + case MOVE_SHELL_TRAP: + BattleScriptExecute(BattleScript_ShellTrapSetUp); + return TRUE; + } + } + } + } + + return FALSE; } // In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves static void TryChangeTurnOrder(void) { - s32 i, j; + u32 i, j; for (i = 0; i < gBattlersCount - 1; i++) { for (j = i + 1; j < gBattlersCount; j++) { - u8 battler1 = gBattlerByTurnOrder[i]; - u8 battler2 = gBattlerByTurnOrder[j]; + u32 battler1 = gBattlerByTurnOrder[i]; + u32 battler2 = gBattlerByTurnOrder[j]; + if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE) { @@ -4921,42 +4966,6 @@ static void TryChangeTurnOrder(void) } } } - gBattleMainFunc = CheckChosenMoveForEffectsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; -} - -static void CheckChosenMoveForEffectsBeforeTurnStarts(void) -{ - u32 i; - - if (!(gHitMarker & HITMARKER_RUN)) - { - while (gBattleStruct->focusPunchBattlerId < gBattlersCount) - { - gActiveBattler = gBattlerAttacker = gBattleStruct->focusPunchBattlerId; - gBattleStruct->focusPunchBattlerId++; - if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) - && !(gDisableStructs[gBattlerAttacker].truantCounter) - && !(gProtectStructs[gActiveBattler].noValidMoves)) - { - switch (gChosenMoveByBattler[gActiveBattler]) - { - case MOVE_FOCUS_PUNCH: - BattleScriptExecute(BattleScript_FocusPunchSetUp); - return; - case MOVE_BEAK_BLAST: - BattleScriptExecute(BattleScript_BeakBlastSetUp); - return; - case MOVE_SHELL_TRAP: - BattleScriptExecute(BattleScript_ShellTrapSetUp); - return; - } - } - } - } - - gBattleMainFunc = CheckQuickClaw_CustapBerryActivation; - gBattleStruct->quickClawBattlerId = 0; } static void CheckQuickClaw_CustapBerryActivation(void) @@ -5011,6 +5020,8 @@ static void CheckQuickClaw_CustapBerryActivation(void) gCurrentTurnActionNumber = 0; gCurrentActionFuncId = gActionsByTurnOrder[0]; gBattleStruct->dynamicMoveType = 0; + gBattleStruct->effectsBeforeUsingMoveDone = FALSE; + gBattleStruct->focusPunchBattlers = 0; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattleStruct->ateBoost[i] = FALSE; @@ -5029,6 +5040,16 @@ static void RunTurnActionsFunctions(void) if (gBattleOutcome != 0) gCurrentActionFuncId = B_ACTION_FINISHED; + // Mega Evolve / Focus Punch-like moves after switching, items, running, but before using a move. + if (gCurrentActionFuncId == B_ACTION_USE_MOVE && !gBattleStruct->effectsBeforeUsingMoveDone) + { + if (TryDoMegaEvosBeforeMoves()) + return; + else if (TryDoMoveEffectsBeforeMoves()) + return; + gBattleStruct->effectsBeforeUsingMoveDone = TRUE; + } + *(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber; sTurnActionsFuncsTable[gCurrentActionFuncId](); diff --git a/src/battle_util.c b/src/battle_util.c index 9520e761f7..e709733251 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1674,7 +1674,8 @@ void BattleScriptPushCursor(void) void BattleScriptPop(void) { - gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; + if (gBattleResources->battleScriptsStack->size != 0) + gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; } static bool32 IsGravityPreventingMove(u32 move) @@ -10567,7 +10568,7 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) { int i, j, currSpeed, currBattler; - u16 speeds[4] = {0}; + u16 speeds[MAX_BATTLERS_COUNT] = {0}; for (i = 0; i < gBattlersCount; i++) speeds[i] = GetBattlerTotalSpeedStat(battlers[i]); diff --git a/src/data/trainer_parties.h b/src/data/trainer_parties.h index 1759120b7f..b4f6175799 100644 --- a/src/data/trainer_parties.h +++ b/src/data/trainer_parties.h @@ -10301,9 +10301,9 @@ static const struct TrainerMonNoItemDefaultMoves sParty_MayRustboroTorchic[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne2[] = { { .iv = 255, - .lvl = 32, - .species = SPECIES_GOLEM, - .heldItem = ITEM_NONE, + .lvl = 5, + .species = SPECIES_AMPHAROS, + .heldItem = ITEM_AMPHAROSITE, .moves = {MOVE_PROTECT, MOVE_ROLLOUT, MOVE_MAGNITUDE, MOVE_EXPLOSION} }, { @@ -10333,8 +10333,8 @@ static const struct TrainerMonItemCustomMoves sParty_Roxanne3[] = { { .iv = 255, .lvl = 37, - .species = SPECIES_OMANYTE, - .heldItem = ITEM_NONE, + .species = SPECIES_MANECTRIC, + .heldItem = ITEM_MANECTITE, .moves = {MOVE_PROTECT, MOVE_ICE_BEAM, MOVE_ROCK_SLIDE, MOVE_SURF} }, { @@ -10370,9 +10370,9 @@ static const struct TrainerMonItemCustomMoves sParty_Roxanne3[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne4[] = { { .iv = 255, - .lvl = 42, - .species = SPECIES_OMASTAR, - .heldItem = ITEM_NONE, + .lvl = 100, + .species = SPECIES_GARDEVOIR, + .heldItem = ITEM_GARDEVOIRITE, .moves = {MOVE_PROTECT, MOVE_ICE_BEAM, MOVE_ROCK_SLIDE, MOVE_SURF} }, { diff --git a/test/mega_evolution.c b/test/mega_evolution.c index 13e9cd5b22..49861daaa8 100644 --- a/test/mega_evolution.c +++ b/test/mega_evolution.c @@ -17,6 +17,44 @@ SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite") } } +DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - opponent faster") +{ + GIVEN { + PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3);} + OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(3);} + OPPONENT(SPECIES_WOBBUFFET) { Speed(4);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); } + } SCENE { + MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft); + MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!"); + MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft); + MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!"); + } +} + +DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - player faster") +{ + GIVEN { + PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3);} + OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2);} + OPPONENT(SPECIES_WOBBUFFET) { Speed(4);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); } + } SCENE { + MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft); + MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!"); + MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft); + MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!"); + } +} + SINGLE_BATTLE_TEST("Rayquaza can Mega Evolve knowing Dragon Ascent") { GIVEN { From 51c8edd8255532727a57022ebc98a570815ed5fb Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 8 Mar 2023 13:22:53 +0100 Subject: [PATCH 05/88] Add tests for Mega Evo, Focus Punch, Beak Blast --- src/battle_message.c | 6 +- src/battle_script_commands.c | 3 + src/data/trainer_parties.h | 22 +++---- test/mega_evolution.c | 49 ++++++++++++++ test/move_effect_beak_blast.c | 113 +++++++++++++++++++++++++++++++++ test/move_effect_focus_punch.c | 76 ++++++++++++++++++++++ 6 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 test/move_effect_beak_blast.c create mode 100644 test/move_effect_focus_punch.c diff --git a/src/battle_message.c b/src/battle_message.c index cf57247bf8..705fdc3264 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3027,7 +3027,11 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battlerId) toCpy = gSaveBlock2Ptr->playerName; break; case B_POSITION_PLAYER_RIGHT: - if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & (BATTLE_TYPE_RECORDED | BATTLE_TYPE_MULTI)) + if ((gBattleTypeFlags & BATTLE_TYPE_RECORDED) && !(gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER))) + { + toCpy = gLinkPlayers[0].name; + } + else if ((gBattleTypeFlags & BATTLE_TYPE_LINK) && gBattleTypeFlags & (BATTLE_TYPE_RECORDED | BATTLE_TYPE_MULTI)) { toCpy = gLinkPlayers[2].name; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 126ca06fd1..caad6393c3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5464,6 +5464,9 @@ static void Cmd_moveend(void) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN; + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].status1), &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BeakBlastBurn; effect = 1; diff --git a/src/data/trainer_parties.h b/src/data/trainer_parties.h index b4f6175799..76c3b6ca43 100644 --- a/src/data/trainer_parties.h +++ b/src/data/trainer_parties.h @@ -10301,31 +10301,31 @@ static const struct TrainerMonNoItemDefaultMoves sParty_MayRustboroTorchic[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne2[] = { { .iv = 255, - .lvl = 5, - .species = SPECIES_AMPHAROS, - .heldItem = ITEM_AMPHAROSITE, - .moves = {MOVE_PROTECT, MOVE_ROLLOUT, MOVE_MAGNITUDE, MOVE_EXPLOSION} + .lvl = 100, + .species = SPECIES_BEEDRILL, + .heldItem = 0, + .moves = {MOVE_FLING} }, { .iv = 255, - .lvl = 35, + .lvl = 100, .species = SPECIES_KABUTO, .heldItem = ITEM_SITRUS_BERRY, .moves = {MOVE_SWORDS_DANCE, MOVE_ICE_BEAM, MOVE_SURF, MOVE_ROCK_SLIDE} }, { .iv = 255, - .lvl = 35, - .species = SPECIES_ONIX, + .lvl = 100, + .species = SPECIES_THUNDURUS, .heldItem = ITEM_NONE, - .moves = {MOVE_IRON_TAIL, MOVE_EXPLOSION, MOVE_ROAR, MOVE_ROCK_SLIDE} + .moves = {MOVE_THUNDER, MOVE_EXPLOSION, MOVE_ROAR, MOVE_ROCK_SLIDE} }, { .iv = 255, - .lvl = 37, - .species = SPECIES_NOSEPASS, + .lvl = 99, + .species = SPECIES_SABLEYE, .heldItem = ITEM_SITRUS_BERRY, - .moves = {MOVE_DOUBLE_TEAM, MOVE_EXPLOSION, MOVE_PROTECT, MOVE_ROCK_SLIDE} + .moves = {MOVE_DOUBLE_TEAM, MOVE_EXPLOSION, MOVE_DARK_PULSE, MOVE_IRON_HEAD} } }; diff --git a/test/mega_evolution.c b/test/mega_evolution.c index 49861daaa8..8d6af1d21d 100644 --- a/test/mega_evolution.c +++ b/test/mega_evolution.c @@ -104,3 +104,52 @@ SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn orde ASSUME(player->speed == 45); } } + +DOUBLE_BATTLE_TEST("Mega Evolution happens after switching, but before Focus Punch-like Moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE);} + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, megaEvolve: TRUE, target:opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target:opponentLeft); } + TURN {}; + } SCENE { + MESSAGE("2 withdrew Wobbuffet!"); + MESSAGE("2 sent out Wobbuffet!"); + + MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerRight); + MESSAGE("Venusaur has Mega Evolved into Mega Venusaur!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerRight); + MESSAGE("Venusaur is tightening its focus!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerLeft); + MESSAGE("Wobbuffet is tightening its focus!"); + } +} + +SINGLE_BATTLE_TEST("Regular Mega Evolution and Fervent Wish Mega Evolution can happen on the same turn") +{ + GIVEN { + PLAYER(SPECIES_RAYQUAZA) { Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); Speed(3);} + OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2);} + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(opponent, MOVE_CELEBRATE, megaEvolve: TRUE); } + } SCENE { + MESSAGE("1's fervent wish has reached Rayquaza!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player); + MESSAGE("Rayquaza has Mega Evolved into Mega Rayquaza!"); + + MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_RAYQUAZA_MEGA); + EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA); + } +} diff --git a/test/move_effect_beak_blast.c b/test/move_effect_beak_blast.c new file mode 100644 index 0000000000..448eb6973f --- /dev/null +++ b/test/move_effect_beak_blast.c @@ -0,0 +1,113 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_BEAK_BLAST].effect == EFFECT_BEAK_BLAST); +} + +DOUBLE_BATTLE_TEST("Beak Blast's charging message is shown before other moves are used") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_BEAK_BLAST].priority < 0); + PLAYER(SPECIES_WYNAUT) {Speed(10); } + PLAYER(SPECIES_WOBBUFFET) {Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_BEAK_BLAST, target:opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, playerLeft); + MESSAGE("Wynaut started heating up its beak!"); + + MESSAGE("Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + MESSAGE("Foe Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + MESSAGE("Foe Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + + MESSAGE("Wynaut used Beak Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, playerLeft); + HP_BAR(opponentLeft); + } +} + +DOUBLE_BATTLE_TEST("Beak Blast burns all who make contact with the pokemon") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_BEAK_BLAST].priority < 0); + ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT); + PLAYER(SPECIES_WYNAUT) {Speed(10); } + PLAYER(SPECIES_WOBBUFFET) {Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); MOVE(opponentRight, MOVE_TACKLE, target:playerLeft); MOVE(playerLeft, MOVE_BEAK_BLAST, target:opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, playerLeft); + MESSAGE("Wynaut started heating up its beak!"); + + MESSAGE("Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + + MESSAGE("Foe Wobbuffet used Tackle!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentLeft); + MESSAGE("Foe Wobbuffet was burned!"); + STATUS_ICON(opponentLeft, burn: TRUE); + + MESSAGE("Foe Wobbuffet used Tackle!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight); + MESSAGE("Foe Wobbuffet was burned!"); + STATUS_ICON(opponentRight, burn: TRUE); + + MESSAGE("Wynaut used Beak Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, playerLeft); + HP_BAR(opponentLeft); + } +} + +SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used") +{ + u32 move; + bool32 burn; + PARAMETRIZE { move = MOVE_TACKLE; burn = TRUE; } + PARAMETRIZE { move = MOVE_WATER_GUN; burn = FALSE; } + PARAMETRIZE { move = MOVE_LEER; burn = FALSE; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].flags & FLAG_MAKES_CONTACT); + ASSUME(!(gBattleMoves[MOVE_WATER_GUN].flags & FLAG_MAKES_CONTACT)); + ASSUME(!(gBattleMoves[MOVE_LEER].flags & FLAG_MAKES_CONTACT)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BEAK_BLAST); MOVE(opponent, move); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player); + MESSAGE("Wobbuffet started heating up its beak!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + + if (burn) { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + MESSAGE("Foe Wobbuffet was burned!"); + STATUS_ICON(opponent, burn: TRUE); + } + else { + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + MESSAGE("Foe Wobbuffet was burned!"); + STATUS_ICON(opponent, burn: TRUE); + } + } + + MESSAGE("Wobbuffet used Beak Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, player); + HP_BAR(opponent); + } +} diff --git a/test/move_effect_focus_punch.c b/test/move_effect_focus_punch.c new file mode 100644 index 0000000000..62c5003001 --- /dev/null +++ b/test/move_effect_focus_punch.c @@ -0,0 +1,76 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH); +} + +SINGLE_BATTLE_TEST("Focus Punch activates only if not damaged") +{ + u32 move; + bool32 activate; + PARAMETRIZE { move = MOVE_TACKLE; activate = FALSE; } + PARAMETRIZE { move = MOVE_WATER_GUN; activate = FALSE; } + PARAMETRIZE { move = MOVE_LEER; activate = TRUE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FOCUS_PUNCH); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, player); + MESSAGE("Wobbuffet is tightening its focus!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + + if (activate) { + MESSAGE("Wobbuffet used Focus Punch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, player); + HP_BAR(opponent); + } else { + MESSAGE("Wobbuffet lost its focus and couldn't move!"); + NONE_OF { + MESSAGE("Wobbuffet used Focus Punch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, player); + HP_BAR(opponent); + } + } + } +} + +DOUBLE_BATTLE_TEST("Focus Punch activation is based on Speed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(2) ;} + PLAYER(SPECIES_WYNAUT) {Speed(3) ;} + OPPONENT(SPECIES_WOBBUFFET) {Speed(1) ;} + OPPONENT(SPECIES_WYNAUT) {Speed(5) ;} + } WHEN { + TURN { MOVE(opponentRight, MOVE_FOCUS_PUNCH, target:playerLeft); MOVE(playerRight, MOVE_FOCUS_PUNCH, target:opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target:opponentLeft); MOVE(opponentLeft, MOVE_FOCUS_PUNCH, target:playerLeft); } + } + SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, opponentRight); + MESSAGE("Foe Wynaut is tightening its focus!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerRight); + MESSAGE("Wynaut is tightening its focus!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerLeft); + MESSAGE("Wobbuffet is tightening its focus!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, opponentLeft); + MESSAGE("Foe Wobbuffet is tightening its focus!"); + + MESSAGE("Foe Wynaut used Focus Punch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, opponentRight); + HP_BAR(playerLeft); + + MESSAGE("Wynaut used Focus Punch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_PUNCH, playerRight); + HP_BAR(opponentLeft); + + MESSAGE("Wobbuffet lost its focus and couldn't move!"); + MESSAGE("Foe Wobbuffet lost its focus and couldn't move!"); + } +} From bf4735e87c613599b299986fc8e64b120e35a111 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 8 Mar 2023 13:41:08 +0100 Subject: [PATCH 06/88] remove test changes --- data/scripts/debug.inc | 1 - src/data/trainer_parties.h | 32 ++++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/data/scripts/debug.inc b/data/scripts/debug.inc index a6f68b1f70..191fe69327 100644 --- a/data/scripts/debug.inc +++ b/data/scripts/debug.inc @@ -55,7 +55,6 @@ Debug_FlagsNotSetMessage_Text: .string "'include/constants/overworld{UNDERSCORE}config.h'!$" Debug_Script_1:: - multi_fixed_2_vs_2 TRAINER_ROXANNE_2, Debug_FlagsNotSetMessage_Text, TRAINER_ROXANNE_3, Debug_FlagsNotSetMessage_Text, TRAINER_ROXANNE_4, 0 end Debug_Script_2:: diff --git a/src/data/trainer_parties.h b/src/data/trainer_parties.h index 76c3b6ca43..1759120b7f 100644 --- a/src/data/trainer_parties.h +++ b/src/data/trainer_parties.h @@ -10301,31 +10301,31 @@ static const struct TrainerMonNoItemDefaultMoves sParty_MayRustboroTorchic[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne2[] = { { .iv = 255, - .lvl = 100, - .species = SPECIES_BEEDRILL, - .heldItem = 0, - .moves = {MOVE_FLING} + .lvl = 32, + .species = SPECIES_GOLEM, + .heldItem = ITEM_NONE, + .moves = {MOVE_PROTECT, MOVE_ROLLOUT, MOVE_MAGNITUDE, MOVE_EXPLOSION} }, { .iv = 255, - .lvl = 100, + .lvl = 35, .species = SPECIES_KABUTO, .heldItem = ITEM_SITRUS_BERRY, .moves = {MOVE_SWORDS_DANCE, MOVE_ICE_BEAM, MOVE_SURF, MOVE_ROCK_SLIDE} }, { .iv = 255, - .lvl = 100, - .species = SPECIES_THUNDURUS, + .lvl = 35, + .species = SPECIES_ONIX, .heldItem = ITEM_NONE, - .moves = {MOVE_THUNDER, MOVE_EXPLOSION, MOVE_ROAR, MOVE_ROCK_SLIDE} + .moves = {MOVE_IRON_TAIL, MOVE_EXPLOSION, MOVE_ROAR, MOVE_ROCK_SLIDE} }, { .iv = 255, - .lvl = 99, - .species = SPECIES_SABLEYE, + .lvl = 37, + .species = SPECIES_NOSEPASS, .heldItem = ITEM_SITRUS_BERRY, - .moves = {MOVE_DOUBLE_TEAM, MOVE_EXPLOSION, MOVE_DARK_PULSE, MOVE_IRON_HEAD} + .moves = {MOVE_DOUBLE_TEAM, MOVE_EXPLOSION, MOVE_PROTECT, MOVE_ROCK_SLIDE} } }; @@ -10333,8 +10333,8 @@ static const struct TrainerMonItemCustomMoves sParty_Roxanne3[] = { { .iv = 255, .lvl = 37, - .species = SPECIES_MANECTRIC, - .heldItem = ITEM_MANECTITE, + .species = SPECIES_OMANYTE, + .heldItem = ITEM_NONE, .moves = {MOVE_PROTECT, MOVE_ICE_BEAM, MOVE_ROCK_SLIDE, MOVE_SURF} }, { @@ -10370,9 +10370,9 @@ static const struct TrainerMonItemCustomMoves sParty_Roxanne3[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne4[] = { { .iv = 255, - .lvl = 100, - .species = SPECIES_GARDEVOIR, - .heldItem = ITEM_GARDEVOIRITE, + .lvl = 42, + .species = SPECIES_OMASTAR, + .heldItem = ITEM_NONE, .moves = {MOVE_PROTECT, MOVE_ICE_BEAM, MOVE_ROCK_SLIDE, MOVE_SURF} }, { From 74ec59cace57d0036512688aaf39bac2647fc8a4 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Tue, 21 Mar 2023 18:11:49 -0400 Subject: [PATCH 07/88] add sAnimHideHpBoxes to fix healthbox reappearing from e.g. B_ANIM_SWITCH_OUT_OPPONENT_MON --- src/battle_anim.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/battle_anim.c b/src/battle_anim.c index fd81152e1c..de9a97ce28 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -119,6 +119,7 @@ EWRAM_DATA u8 gBattleAnimAttacker = 0; EWRAM_DATA u8 gBattleAnimTarget = 0; EWRAM_DATA u16 gAnimBattlerSpecies[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gAnimCustomPanning = 0; +EWRAM_DATA static bool8 sAnimHideHpBoxes = FALSE; #include "data/battle_anim.h" @@ -232,7 +233,6 @@ void LaunchBattleAnimation(u32 animType, u32 animId) { s32 i; const u8 *const *animsTable; - bool32 hideHpBoxes; if (gTestRunnerEnabled) { @@ -261,7 +261,7 @@ void LaunchBattleAnimation(u32 animType, u32 animId) break; } - hideHpBoxes = !(animType == ANIM_TYPE_MOVE && animId == MOVE_TRANSFORM); + sAnimHideHpBoxes = !(animType == ANIM_TYPE_MOVE && animId == MOVE_TRANSFORM); if (animType != ANIM_TYPE_MOVE) { switch (animId) @@ -276,10 +276,10 @@ void LaunchBattleAnimation(u32 animType, u32 animId) case B_ANIM_MEGA_EVOLUTION: case B_ANIM_PRIMAL_REVERSION: case B_ANIM_GULP_MISSILE: - hideHpBoxes = TRUE; + sAnimHideHpBoxes = TRUE; break; default: - hideHpBoxes = FALSE; + sAnimHideHpBoxes = FALSE; break; } } @@ -287,7 +287,7 @@ void LaunchBattleAnimation(u32 animType, u32 animId) if (!IsContest()) { InitPrioritiesForVisibleBattlers(); - UpdateOamPriorityInAllHealthboxes(0, hideHpBoxes); + UpdateOamPriorityInAllHealthboxes(0, sAnimHideHpBoxes); for (i = 0; i < MAX_BATTLERS_COUNT; i++) { if (GetBattlerSide(i) != B_SIDE_PLAYER) @@ -762,7 +762,8 @@ static void Cmd_end(void) if (!IsContest()) { InitPrioritiesForVisibleBattlers(); - UpdateOamPriorityInAllHealthboxes(1, TRUE); + UpdateOamPriorityInAllHealthboxes(1, sAnimHideHpBoxes); + sAnimHideHpBoxes = FALSE; } gAnimScriptActive = FALSE; } From afc4b4cdee2d5596a6262084bd795281f887d7b9 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Tue, 28 Mar 2023 15:07:37 -0400 Subject: [PATCH 08/88] add mirror herb --- data/battle_scripts_1.s | 29 ++++++++++++++++++++++----- include/battle.h | 1 + include/battle_scripts.h | 2 ++ include/constants/battle_string_ids.h | 3 ++- src/battle_message.c | 2 ++ src/battle_script_commands.c | 14 +++++++++++++ src/battle_util.c | 26 ++++++++++++++++++++++++ 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 200a044610..c8bd537358 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -9980,23 +9980,42 @@ BattleScript_PrintPlayerForfeitedLinkBattle:: end2 BattleScript_TotemFlaredToLife:: - playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE + playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE, NULL printstring STRINGID_AURAFLAREDTOLIFE waitmessage B_WAIT_TIME_LONG - goto BattleScript_ApplyTotemVarBoost + call BattleScript_ApplyTotemVarBoost + end2 + +@ remove the mirror herb, do totem loop +BattleScript_MirrorHerbCopyStatChangeEnd2:: + call BattleScript_MirrorHerbCopyStatChange + end2 + +BattleScript_MirrorHerbCopyStatChange:: + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL + printstring STRINGID_MIRRORHERBCOPIED + waitmessage B_WAIT_TIME_LONG + removeitem BS_SCRIPTING + call BattleScript_TotemVar_Ret + copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe + return BattleScript_TotemVar:: + call BattleScript_TotemVar_Ret + end2 + +BattleScript_TotemVar_Ret:: gettotemboost BattleScript_ApplyTotemVarBoost BattleScript_TotemVarEnd: - end2 + return BattleScript_ApplyTotemVarBoost: statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_TotemVarEnd setgraphicalstatchangevalues playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 -BattleScript_TotemVarPrintStatMsg: printfromtable gStatUpStringIds waitmessage B_WAIT_TIME_LONG - goto BattleScript_TotemVar @loop until stats bitfield is empty + goto BattleScript_TotemVar_Ret @loop until stats bitfield is empty + BattleScript_AnnounceAirLockCloudNine:: call BattleScript_AbilityPopUp diff --git a/include/battle.h b/include/battle.h index 491273b80b..f1818d583c 100644 --- a/include/battle.h +++ b/include/battle.h @@ -145,6 +145,7 @@ struct ProtectStruct u16 quash:1; u16 shellTrap:1; u16 silkTrapped:1; + u16 eatMirrorHerb:1; u32 physicalDmg; u32 specialDmg; u8 physicalBattlerId; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index c78fcafe48..248f281196 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -1,6 +1,8 @@ #ifndef GUARD_BATTLE_SCRIPTS_H #define GUARD_BATTLE_SCRIPTS_H +extern const u8 BattleScript_MirrorHerbCopyStatChange[]; +extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[]; extern const u8 BattleScript_NotAffected[]; extern const u8 BattleScript_HitFromCritCalc[]; extern const u8 BattleScript_MoveEnd[]; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index b941763340..4051a3a6d1 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -647,8 +647,9 @@ #define STRINGID_STEALTHROCKDISAPPEAREDFROMTEAM 645 #define STRINGID_COULDNTFULLYPROTECT 646 #define STRINGID_STOCKPILEDEFFECTWOREOFF 647 +#define STRINGID_MIRRORHERBCOPIED 648 -#define BATTLESTRINGS_COUNT 648 +#define BATTLESTRINGS_COUNT 649 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_message.c b/src/battle_message.c index 2ecfac0c7d..9ab40cd4e9 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -782,9 +782,11 @@ static const u8 sText_PrepareShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set a she static const u8 sText_ShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap didn't work!"); static const u8 sText_CouldntFullyProtect[] = _("{B_DEF_NAME_WITH_PREFIX} couldn't fully protect\nitself and got hurt!"); static const u8 sText_StockpiledEffectWoreOff[] = _("{B_ATK_NAME_WITH_PREFIX}'s stockpiled\neffect wore off!"); +static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_MIRRORHERBCOPIED - BATTLESTRINGS_TABLE_START] = sText_MirrorHerbCopied, [STRINGID_STOCKPILEDEFFECTWOREOFF - BATTLESTRINGS_TABLE_START] = sText_StockpiledEffectWoreOff, [STRINGID_COULDNTFULLYPROTECT - BATTLESTRINGS_TABLE_START] = sText_CouldntFullyProtect, [STRINGID_ATTACKERGAINEDSTRENGTHFROMTHEFALLEN - BATTLESTRINGS_TABLE_START] = sText_AttackerGainedStrengthFromTheFallen, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 28bf60c39b..72a332af98 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12080,6 +12080,20 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr { gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); gProtectStructs[gActiveBattler].statRaised = TRUE; + + // check mirror herb + for (index = 0; index < gBattlersCount; index++) + { + if (GetBattlerSide(index) == GetBattlerSide(gActiveBattler)) + continue; // Only triggers on opposing side + if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB + && gBattleMons[index].statStages[statId] < MAX_STAT_STAGE) + { + gProtectStructs[index].eatMirrorHerb = 1; + gTotemBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk + gTotemBoosts[index].statChanges[statId - 1] = statValue; + } + } } } diff --git a/src/battle_util.c b/src/battle_util.c index dea28b5d83..e92ccfdbdc 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6877,6 +6877,26 @@ static bool32 GetMentalHerbEffect(u8 battlerId) return ret; } +static u8 TryConsumeMirrorHerb(u8 battlerId, bool32 execute) +{ + u8 effect = 0; + + if (gProtectStructs[battlerId].eatMirrorHerb) { + gLastUsedItem = gBattleMons[battlerId].item; + gBattleScripting.savedBattler = gBattlerAttacker; + gBattleScripting.battler = gBattlerAttacker = battlerId; + gProtectStructs[battlerId].eatMirrorHerb = 0; + if (execute) { + BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2); + } else { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MirrorHerbCopyStatChange; + } + effect = ITEM_STATS_CHANGE; + } + return effect; +} + static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) { u8 effect = 0; @@ -7058,6 +7078,9 @@ static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) effect = ITEM_EFFECT_OTHER; } break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(battlerId, FALSE); + break; } return effect; @@ -7580,6 +7603,9 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) if (!moveTurn) effect = TrySetMicleBerry(battlerId, gLastUsedItem, TRUE); break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(battlerId, TRUE); + break; } if (effect != 0) From f8bbcdfe73bafea1fa8db869e4fcdfe4c492c948 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Tue, 28 Mar 2023 15:12:54 -0400 Subject: [PATCH 09/88] minor fix to mirror herb message --- src/battle_message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_message.c b/src/battle_message.c index 9ab40cd4e9..140b8b3c47 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -782,7 +782,7 @@ static const u8 sText_PrepareShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set a she static const u8 sText_ShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap didn't work!"); static const u8 sText_CouldntFullyProtect[] = _("{B_DEF_NAME_WITH_PREFIX} couldn't fully protect\nitself and got hurt!"); static const u8 sText_StockpiledEffectWoreOff[] = _("{B_ATK_NAME_WITH_PREFIX}'s stockpiled\neffect wore off!"); -static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!"); +static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { From 76f02774ea645eb285731b49f9c829bc17341b8b Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 20 Apr 2023 20:35:22 +0100 Subject: [PATCH 10/88] Detect memory leaks in tests Can use KNOWN_LEAKING; to specify that a test is known to leak memory. The location information is available in regular game builds. Thus it is available for use in debugging leaks in-game too. In the future we should consider replacing it with NULL if NDEBUG is defined. This is not currently possible because the tests do not force NDEBUG to be undefined. --- gflib/malloc.c | 69 +++++++++++++++++++-------------------- gflib/malloc.h | 41 +++++++++++++++++++++-- include/gba/defines.h | 3 ++ include/global.h | 3 ++ test/test.h | 5 +++ test/test_battle.h | 1 + test/test_runner.c | 26 +++++++++++++++ test/test_runner_battle.c | 10 ++++++ 8 files changed, 120 insertions(+), 38 deletions(-) diff --git a/gflib/malloc.c b/gflib/malloc.c index d0b9497635..dcfac6ee81 100644 --- a/gflib/malloc.c +++ b/gflib/malloc.c @@ -1,37 +1,18 @@ #include "global.h" +#include "malloc.h" static void *sHeapStart; static u32 sHeapSize; -#define MALLOC_SYSTEM_ID 0xA3A3 - -struct MemBlock { - // Whether this block is currently allocated. - bool16 flag; - - // Magic number used for error checking. Should equal MALLOC_SYSTEM_ID. - u16 magic; - - // Size of the block (not including this header struct). - u32 size; - - // Previous block pointer. Equals sHeapStart if this is the first block. - struct MemBlock *prev; - - // Next block pointer. Equals sHeapStart if this is the last block. - struct MemBlock *next; - - // Data in the memory block. (Arrays of length 0 are a GNU extension.) - u8 data[0]; -}; - void PutMemBlockHeader(void *block, struct MemBlock *prev, struct MemBlock *next, u32 size) { struct MemBlock *header = (struct MemBlock *)block; - header->flag = FALSE; + header->allocated = FALSE; + header->locationHi = 0; header->magic = MALLOC_SYSTEM_ID; header->size = size; + header->locationLo = 0; header->prev = prev; header->next = next; } @@ -41,7 +22,7 @@ void PutFirstMemBlockHeader(void *block, u32 size) PutMemBlockHeader(block, (struct MemBlock *)block, (struct MemBlock *)block, size - sizeof(struct MemBlock)); } -void *AllocInternal(void *heapStart, u32 size) +void *AllocInternal(void *heapStart, u32 size, const char *location) { struct MemBlock *pos = (struct MemBlock *)heapStart; struct MemBlock *head = pos; @@ -55,14 +36,14 @@ void *AllocInternal(void *heapStart, u32 size) for (;;) { // Loop through the blocks looking for unused block that's big enough. - if (!pos->flag) { + if (!pos->allocated) { foundBlockSize = pos->size; if (foundBlockSize >= size) { if (foundBlockSize - size < 2 * sizeof(struct MemBlock)) { // The block isn't much bigger than the requested size, // so just use it. - pos->flag = TRUE; + pos->allocated = TRUE; } else { // The block is significantly bigger than the requested // size, so split the rest into a separate block. @@ -71,7 +52,7 @@ void *AllocInternal(void *heapStart, u32 size) splitBlock = (struct MemBlock *)(pos->data + size); - pos->flag = TRUE; + pos->allocated = TRUE; pos->size = size; PutMemBlockHeader(splitBlock, pos, pos->next, foundBlockSize); @@ -82,6 +63,9 @@ void *AllocInternal(void *heapStart, u32 size) splitBlock->next->prev = splitBlock; } + pos->locationHi = ((uintptr_t)location) >> 14; + pos->locationLo = (uintptr_t)location; + return pos->data; } } @@ -98,12 +82,12 @@ void FreeInternal(void *heapStart, void *pointer) if (pointer) { struct MemBlock *head = (struct MemBlock *)heapStart; struct MemBlock *block = (struct MemBlock *)((u8 *)pointer - sizeof(struct MemBlock)); - block->flag = FALSE; + block->allocated = FALSE; // If the freed block isn't the last one, merge with the next block // if it's not in use. if (block->next != head) { - if (!block->next->flag) { + if (!block->next->allocated) { block->size += sizeof(struct MemBlock) + block->next->size; block->next->magic = 0; block->next = block->next->next; @@ -115,7 +99,7 @@ void FreeInternal(void *heapStart, void *pointer) // If the freed block isn't the first one, merge with the previous block // if it's not in use. if (block != head) { - if (!block->prev->flag) { + if (!block->prev->allocated) { block->prev->next = block->next; if (block->next != head) @@ -128,9 +112,9 @@ void FreeInternal(void *heapStart, void *pointer) } } -void *AllocZeroedInternal(void *heapStart, u32 size) +void *AllocZeroedInternal(void *heapStart, u32 size, const char *location) { - void *mem = AllocInternal(heapStart, size); + void *mem = AllocInternal(heapStart, size, location); if (mem != NULL) { if (size & 3) @@ -175,14 +159,14 @@ void InitHeap(void *heapStart, u32 heapSize) PutFirstMemBlockHeader(heapStart, heapSize); } -void *Alloc(u32 size) +void *Alloc_(u32 size, const char *location) { - return AllocInternal(sHeapStart, size); + return AllocInternal(sHeapStart, size, location); } -void *AllocZeroed(u32 size) +void *AllocZeroed_(u32 size, const char *location) { - return AllocZeroedInternal(sHeapStart, size); + return AllocZeroedInternal(sHeapStart, size, location); } void Free(void *pointer) @@ -207,3 +191,16 @@ bool32 CheckHeap() return TRUE; } + +const struct MemBlock *HeapHead(void) +{ + return (const struct MemBlock *)sHeapStart; +} + +const char *MemBlockLocation(const struct MemBlock *block) +{ + if (!block->allocated) + return NULL; + + return (const char *)(ROM_START | (block->locationHi << 14) | block->locationLo); +} diff --git a/gflib/malloc.h b/gflib/malloc.h index 851db83a62..f484b15806 100644 --- a/gflib/malloc.h +++ b/gflib/malloc.h @@ -11,11 +11,48 @@ #define TRY_FREE_AND_SET_NULL(ptr) if (ptr != NULL) FREE_AND_SET_NULL(ptr) +#define MALLOC_SYSTEM_ID 0xA3A3 + +struct MemBlock +{ + // Whether this block is currently allocated. + u16 allocated:1; + + u16 unused_00:4; + + // High 11 bits of location pointer. + u16 locationHi:11; + + // Magic number used for error checking. Should equal MALLOC_SYSTEM_ID. + u16 magic; + + // Size of the block (not including this header struct). + u32 size:18; + + // Low 14 bits of location pointer. + u32 locationLo:14; + + // Previous block pointer. Equals sHeapStart if this is the first block. + struct MemBlock *prev; + + // Next block pointer. Equals sHeapStart if this is the last block. + struct MemBlock *next; + + // Data in the memory block. (Arrays of length 0 are a GNU extension.) + u8 data[0]; +}; + extern u8 gHeap[]; -void *Alloc(u32 size); -void *AllocZeroed(u32 size); +#define Alloc(size) Alloc_(size, __FILE__ ":" STR(__LINE__)) +#define AllocZeroed(size) AllocZeroed_(size, __FILE__ ":" STR(__LINE__)) + +void *Alloc_(u32 size, const char *location); +void *AllocZeroed_(u32 size, const char *location); void Free(void *pointer); void InitHeap(void *pointer, u32 size); +const struct MemBlock *HeapHead(void); +const char *MemBlockLocation(const struct MemBlock *block); + #endif // GUARD_ALLOC_H diff --git a/include/gba/defines.h b/include/gba/defines.h index 82caf56e68..9959a17ef8 100644 --- a/include/gba/defines.h +++ b/include/gba/defines.h @@ -22,6 +22,9 @@ #define INTR_CHECK (*(u16 *)0x3007FF8) #define INTR_VECTOR (*(void **)0x3007FFC) +#define ROM_START 0x8000000 +#define ROM_END 0xA000000 + #define EWRAM_START 0x02000000 #define EWRAM_END (EWRAM_START + 0x40000) #define IWRAM_START 0x03000000 diff --git a/include/global.h b/include/global.h index c20f2ed427..eca45bdb63 100644 --- a/include/global.h +++ b/include/global.h @@ -147,6 +147,9 @@ #define CAT(a, b) CAT_(a, b) #define CAT_(a, b) a ## b +#define STR(a) STR_(a) +#define STR_(a) #a + // Converts a string to a compound literal, essentially making it a pointer to const u8 #define COMPOUND_STRING(str) (const u8[]) _(str) diff --git a/test/test.h b/test/test.h index dfa1f12b11..bf3e0b0041 100644 --- a/test/test.h +++ b/test/test.h @@ -46,6 +46,7 @@ struct TestRunnerState u8 result; u8 expectedResult; + bool8 expectLeaks:1; u32 timeoutSeconds; }; @@ -69,6 +70,7 @@ extern struct TestRunnerState gTestRunnerState; void CB2_TestRunner(void); void Test_ExpectedResult(enum TestResult); +void Test_ExpectLeaks(bool32); void Test_ExitWithResult(enum TestResult, const char *fmt, ...); s32 MgbaPrintf_(const char *fmt, ...); @@ -160,6 +162,9 @@ s32 MgbaPrintf_(const char *fmt, ...); #define KNOWN_FAILING \ Test_ExpectedResult(TEST_RESULT_FAIL) +#define KNOWN_LEAKING \ + Test_ExpectLeaks(TRUE) + #define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter) #define TO_DO \ diff --git a/test/test_battle.h b/test/test_battle.h index 967bb661dd..3597e9d167 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -615,6 +615,7 @@ struct BattleTestRunnerState bool8 runThen:1; bool8 runFinally:1; bool8 runningFinally:1; + bool8 tearDownBattle:1; struct BattleTestData data; u8 *results; u8 checkProgressParameter; diff --git a/test/test_runner.c b/test/test_runner.c index ef9f2da738..64cb20e269 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -111,6 +111,7 @@ void CB2_TestRunner(void) gTestRunnerState.state = STATE_REPORT_RESULT; gTestRunnerState.result = TEST_RESULT_PASS; gTestRunnerState.expectedResult = TEST_RESULT_PASS; + gTestRunnerState.expectLeaks = FALSE; if (gTestRunnerHeadless) gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS; else @@ -141,6 +142,26 @@ void CB2_TestRunner(void) if (gTestRunnerState.test->runner->tearDown) gTestRunnerState.test->runner->tearDown(gTestRunnerState.test->data); + if (!gTestRunnerState.expectLeaks) + { + const struct MemBlock *head = HeapHead(); + const struct MemBlock *block = head; + do + { + if (block->allocated) + { + const char *location = MemBlockLocation(block); + if (location) + MgbaPrintf_("%s: %d bytes not freed", location, block->size); + else + MgbaPrintf_(": %d bytes not freed", block->size); + gTestRunnerState.result = TEST_RESULT_FAIL; + } + block = block->next; + } + while (block != head); + } + if (gTestRunnerState.test->runner == &gAssumptionsRunner) { if (gTestRunnerState.result != TEST_RESULT_PASS) @@ -238,6 +259,11 @@ void Test_ExpectedResult(enum TestResult result) gTestRunnerState.expectedResult = result; } +void Test_ExpectLeaks(bool32 expectLeaks) +{ + gTestRunnerState.expectLeaks = expectLeaks; +} + static void FunctionTest_SetUp(void *data) { (void)data; diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 365120f137..8654f78cd0 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -893,6 +893,15 @@ static void BattleTest_TearDown(void *data) { if (STATE) { + // Free resources that aren't cleaned up when the battle was + // aborted unexpectedly. + if (STATE->tearDownBattle) + { + FreeMonSpritesGfx(); + FreeBattleSpritesData(); + FreeBattleResources(); + FreeAllWindowBuffers(); + } FREE_AND_SET_NULL(STATE->results); FREE_AND_SET_NULL(STATE); } @@ -923,6 +932,7 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result } else { + STATE->tearDownBattle = TRUE; return FALSE; } } From c81a985b5b76779c9419289dddb57ef047bfae70 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 20 Apr 2023 21:45:16 +0100 Subject: [PATCH 11/88] Fix USE_ITEM explicit targets --- include/recorded_battle.h | 1 + src/battle_controller_recorded_opponent.c | 1 + src/battle_controller_recorded_player.c | 1 + test/test_runner_battle.c | 8 +++++--- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/recorded_battle.h b/include/recorded_battle.h index d04e89c362..ea66096d7f 100644 --- a/include/recorded_battle.h +++ b/include/recorded_battle.h @@ -45,6 +45,7 @@ enum RECORDED_PARTY_INDEX, RECORDED_BATTLE_PALACE_ACTION, RECORDED_ITEM_ID, + RECORDED_ITEM_TARGET, }; extern u32 gRecordedBattleRngSeed; diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index e7fc6fd84a..bb41b42d65 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -1441,6 +1441,7 @@ static void RecordedOpponentHandleChooseItem(void) u8 byte1 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; + gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedOpponentBufferExecCompleted(); } diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 87f5758399..efbd60f18b 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -1465,6 +1465,7 @@ static void RecordedPlayerHandleChooseItem(void) u8 byte1 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; + gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedPlayerBufferExecCompleted(); } diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 365120f137..2a44cc018b 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1540,9 +1540,11 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, (ctx.itemId >> 8) & 0xFF); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, ctx.itemId & 0xFF); if (ctx.explicitPartyIndex) - gBattleStruct->itemPartyIndex[battlerId] = ctx.partyIndex; - if (ctx.explicitMove) - gBattleStruct->itemPartyIndex[battlerId] = i; + PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, ctx.partyIndex); + else if (ctx.explicitMove) + PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, i); + else + PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, 0); DATA.actionBattlers |= 1 << battlerId; } From e1e8277fbaa9897c981e7619473df49c6cbe04a2 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 21 Apr 2023 00:29:58 +0200 Subject: [PATCH 12/88] batch 1 --- test/item_effect_cure_status.c | 101 +++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/item_effect_cure_status.c diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c new file mode 100644 index 0000000000..db8e56a45d --- /dev/null +++ b/test/item_effect_cure_status.c @@ -0,0 +1,101 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Paralyze Heal heals a Pokemon from being paralyzed") +{ + GIVEN { + ASSUME(gItems[ITEM_PARALYZE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_PARALYZE_HEAL, partyIndex: 0); } + } SCENE { + MESSAGE("Paralyze Heal cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being poisoned") +{ + GIVEN { + ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PSN_ANY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } + } SCENE { + MESSAGE("Antidote cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Awakening heals a Pokemon from being asleep") +{ + GIVEN { + ASSUME(gItems[ITEM_AWAKENING].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_AWAKENING, partyIndex: 0); } + } SCENE { + MESSAGE("Awakening cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Burn Heal heals a Pokemon from being burned") +{ + GIVEN { + ASSUME(gItems[ITEM_BURN_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_BURN_HEAL, partyIndex: 0); } + } SCENE { + MESSAGE("Burn Heal cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Ice Heal heals a Pokemon from being paralyzed") +{ + GIVEN { + ASSUME(gItems[ITEM_ICE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ICE_HEAL, partyIndex: 0); } + } SCENE { + MESSAGE("Ice Heal cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Full Heal heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); } + } SCENE { + MESSAGE("Full Heal cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Full Heal heals a Pokemon from being confused") +{ + GIVEN { + ASSUME(gItems[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GENGAR); + } WHEN { + TURN { MOVE(opponent, MOVE_CONFUSE_RAY); } + TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); } + } SCENE { + MESSAGE("Full Heal cured Wobbuffet's status!"); + } +} From 94cb569c4790b0b4fe5a0bd6d814e15cfeb75312 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:25:58 +0200 Subject: [PATCH 13/88] local specialties tests --- test/item_effect_cure_status.c | 222 ++++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 5 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index db8e56a45d..9a850a1311 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -18,7 +18,20 @@ SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); - PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PSN_ANY); } + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } + } SCENE { + MESSAGE("Antidote cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being badly poisoned") +{ + GIVEN { + ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } @@ -86,16 +99,215 @@ SINGLE_BATTLE_TEST("Full Heal heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Full Heal heals a Pokemon from being confused") +SINGLE_BATTLE_TEST("Pewter Crunchies heals a Pokemon from any primary status") { + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } GIVEN { - ASSUME(gItems[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); + ASSUME(gItems[ITEM_PEWTER_CRUNCHIES].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_PEWTER_CRUNCHIES, partyIndex: 0); } + } SCENE { + MESSAGE("PewtrCrnches cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Lava Cookies heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_LAVA_COOKIE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_LAVA_COOKIE, partyIndex: 0); } + } SCENE { + MESSAGE("Lava Cookie cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Rage Candy Bar heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_RAGE_CANDY_BAR].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_RAGE_CANDY_BAR, partyIndex: 0); } + } SCENE { + MESSAGE("RageCandyBar cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Old Gateu heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_OLD_GATEAU].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_OLD_GATEAU, partyIndex: 0); } + } SCENE { + MESSAGE("Old Gateau cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Casteliacone heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_CASTELIACONE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_CASTELIACONE, partyIndex: 0); } + } SCENE { + MESSAGE("Casteliacone cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Lumiose Galette heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_LUMIOSE_GALETTE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_LUMIOSE_GALETTE, partyIndex: 0); } + } SCENE { + MESSAGE("LumioseGlete cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Shalour Sable heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_SHALOUR_SABLE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_SHALOUR_SABLE, partyIndex: 0); } + } SCENE { + MESSAGE("ShalourSable cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Big Malasada heals a Pokemon from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_BIG_MALASADA].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_BIG_MALASADA, partyIndex: 0); } + } SCENE { + MESSAGE("Big Malasada cured Wobbuffet's status!"); + } +} + +SINGLE_BATTLE_TEST("Full Heal and local specialties heal a Pokemon from being confused") +{ + u16 item; + PARAMETRIZE { item = ITEM_FULL_HEAL; } + PARAMETRIZE { item = ITEM_PEWTER_CRUNCHIES; } + PARAMETRIZE { item = ITEM_LAVA_COOKIE; } + PARAMETRIZE { item = ITEM_RAGE_CANDY_BAR; } + PARAMETRIZE { item = ITEM_OLD_GATEAU; } + PARAMETRIZE { item = ITEM_CASTELIACONE; } + PARAMETRIZE { item = ITEM_LUMIOSE_GALETTE; } + PARAMETRIZE { item = ITEM_SHALOUR_SABLE; } + PARAMETRIZE { item = ITEM_BIG_MALASADA; } + GIVEN { + ASSUME(gItems[item].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_GENGAR); } WHEN { TURN { MOVE(opponent, MOVE_CONFUSE_RAY); } - TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); } + TURN { USE_ITEM(player, item, partyIndex: 0); } } SCENE { - MESSAGE("Full Heal cured Wobbuffet's status!"); + switch (item) + { + case ITEM_FULL_HEAL: + MESSAGE("Full Heal cured Wobbuffet's status!"); + break; + case ITEM_PEWTER_CRUNCHIES: + MESSAGE("PewtrCrnches cured Wobbuffet's status!"); + break; + case ITEM_LAVA_COOKIE: + MESSAGE("Lava Cookie cured Wobbuffet's status!"); + break; + case ITEM_RAGE_CANDY_BAR: + MESSAGE("RageCandyBar cured Wobbuffet's status!"); + break; + case ITEM_OLD_GATEAU: + MESSAGE("Old Gateau cured Wobbuffet's status!"); + break; + case ITEM_CASTELIACONE: + MESSAGE("Casteliacone cured Wobbuffet's status!"); + break; + case ITEM_LUMIOSE_GALETTE: + MESSAGE("LumioseGlete cured Wobbuffet's status!"); + break; + case ITEM_SHALOUR_SABLE: + MESSAGE("ShalourSable cured Wobbuffet's status!"); + break; + case ITEM_BIG_MALASADA: + MESSAGE("Big Malasada cured Wobbuffet's status!"); + break; + } } } From 301a12fe98c9d892ace9de3f6d085ed8df5b35cc Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:51:43 +0200 Subject: [PATCH 14/88] fix battleUsage for heal powder --- src/data/items.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/items.h b/src/data/items.h index 84436be55b..2df5c36387 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -526,7 +526,7 @@ const struct Item gItems[] = .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_Medicine, - .battleUsage = EFFECT_ITEM_RESTORE_HP, + .battleUsage = EFFECT_ITEM_CURE_STATUS, .flingPower = 30, }, From 1cc28c89ef6b23bb37555e40d02839684dc80a91 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 21 Apr 2023 20:36:41 +0200 Subject: [PATCH 15/88] X item tests --- test/item_effect_increase_stat.c | 125 ++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/test/item_effect_increase_stat.c b/test/item_effect_increase_stat.c index b459e3ac88..7fcbe72794 100644 --- a/test/item_effect_increase_stat.c +++ b/test/item_effect_increase_stat.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("X-Attack sharply raises battler's Attack stat", s16 damage) +SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -23,3 +23,126 @@ SINGLE_BATTLE_TEST("X-Attack sharply raises battler's Attack stat", s16 damage) EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); } } + +SINGLE_BATTLE_TEST("X Defense sharply raises battler's Defense stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_X_DEFENSE].battleUsage == EFFECT_ITEM_INCREASE_STAT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_X_DEFENSE); } + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + MESSAGE("Foe Wobbuffet used Tackle!"); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + if (B_X_ITEMS_BUFF >= GEN_7) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + else + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("X Sp. Atk sharply raises battler's Sp. Attack stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_X_SP_ATK].battleUsage == EFFECT_ITEM_INCREASE_STAT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_X_SP_ATK); } + TURN { MOVE(player, MOVE_DISARMING_VOICE); } + } SCENE { + MESSAGE("Wobbuffet used DisrmngVoice!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + if (B_X_ITEMS_BUFF >= GEN_7) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + else + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("X Sp. Def sharply raises battler's Sp. Defense stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_X_SP_DEF].battleUsage == EFFECT_ITEM_INCREASE_STAT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_X_SP_DEF); } + TURN { MOVE(opponent, MOVE_DISARMING_VOICE); } + } SCENE { + MESSAGE("Foe Wobbuffet used DisrmngVoice!"); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + if (B_X_ITEMS_BUFF >= GEN_7) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + else + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_X_SPEED].battleUsage == EFFECT_ITEM_INCREASE_STAT); + if (B_X_ITEMS_BUFF >= GEN_7) + { + PLAYER(SPECIES_WOBBUFFET) { Speed(3); }; + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }; + } + else + { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); }; + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }; + } + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_X_SPEED); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + if (useItem) + { + MESSAGE("Wobbuffet used Tackle!"); + MESSAGE("Foe Wobbuffet used Tackle!"); + } else + { + MESSAGE("Foe Wobbuffet used Tackle!"); + MESSAGE("Wobbuffet used Tackle!"); + } + } +} + +SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat", s16 damage) +{ + + ASSUME(gBattleMoves[MOVE_SING].accuracy == 55); + if (B_X_ITEMS_BUFF >= GEN_7) + PASSES_RANDOMLY(gBattleMoves[MOVE_SING].accuracy * 5 / 3, 100, RNG_ACCURACY); + else + PASSES_RANDOMLY(gBattleMoves[MOVE_SING].accuracy * 4 / 3, 100, RNG_ACCURACY); + GIVEN { + ASSUME(gItems[ITEM_X_ACCURACY].battleUsage == EFFECT_ITEM_INCREASE_STAT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_X_ACCURACY); } + TURN { MOVE(player, MOVE_SING); } + } SCENE { + MESSAGE("Wobbuffet used Sing!"); + MESSAGE("Foe Wobbuffet fell asleep!"); + } +} From b4aee00432275f757d519640b497b9ee7d78e136 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 21 Apr 2023 20:57:19 +0200 Subject: [PATCH 16/88] replace pokemon with battler in string --- test/item_effect_cure_status.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index 9a850a1311..9747d18be6 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Paralyze Heal heals a Pokemon from being paralyzed") +SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed") { GIVEN { ASSUME(gItems[ITEM_PARALYZE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Paralyze Heal heals a Pokemon from being paralyzed") } } -SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being poisoned") +SINGLE_BATTLE_TEST("Antidote heals a battler from being poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being poisoned") } } -SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being badly poisoned") +SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -40,7 +40,7 @@ SINGLE_BATTLE_TEST("Antidote heals a Pokemon from being badly poisoned") } } -SINGLE_BATTLE_TEST("Awakening heals a Pokemon from being asleep") +SINGLE_BATTLE_TEST("Awakening heals a battler from being asleep") { GIVEN { ASSUME(gItems[ITEM_AWAKENING].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -53,7 +53,7 @@ SINGLE_BATTLE_TEST("Awakening heals a Pokemon from being asleep") } } -SINGLE_BATTLE_TEST("Burn Heal heals a Pokemon from being burned") +SINGLE_BATTLE_TEST("Burn Heal heals a battler from being burned") { GIVEN { ASSUME(gItems[ITEM_BURN_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -66,7 +66,7 @@ SINGLE_BATTLE_TEST("Burn Heal heals a Pokemon from being burned") } } -SINGLE_BATTLE_TEST("Ice Heal heals a Pokemon from being paralyzed") +SINGLE_BATTLE_TEST("Ice Heal heals a battler from being paralyzed") { GIVEN { ASSUME(gItems[ITEM_ICE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("Ice Heal heals a Pokemon from being paralyzed") } } -SINGLE_BATTLE_TEST("Full Heal heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -99,7 +99,7 @@ SINGLE_BATTLE_TEST("Full Heal heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Pewter Crunchies heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -119,7 +119,7 @@ SINGLE_BATTLE_TEST("Pewter Crunchies heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Lava Cookies heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -139,7 +139,7 @@ SINGLE_BATTLE_TEST("Lava Cookies heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Rage Candy Bar heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -159,7 +159,7 @@ SINGLE_BATTLE_TEST("Rage Candy Bar heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Old Gateu heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -179,7 +179,7 @@ SINGLE_BATTLE_TEST("Old Gateu heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Casteliacone heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -199,7 +199,7 @@ SINGLE_BATTLE_TEST("Casteliacone heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Lumiose Galette heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -219,7 +219,7 @@ SINGLE_BATTLE_TEST("Lumiose Galette heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Shalour Sable heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -239,7 +239,7 @@ SINGLE_BATTLE_TEST("Shalour Sable heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Big Malasada heals a Pokemon from any primary status") +SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -259,7 +259,7 @@ SINGLE_BATTLE_TEST("Big Malasada heals a Pokemon from any primary status") } } -SINGLE_BATTLE_TEST("Full Heal and local specialties heal a Pokemon from being confused") +SINGLE_BATTLE_TEST("Full Heal and local specialties heal a battler from being confused") { u16 item; PARAMETRIZE { item = ITEM_FULL_HEAL; } From cbac6d0e87bc1c99a5cb70c0c78971306bc1eadd Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 21 Apr 2023 20:57:30 +0200 Subject: [PATCH 17/88] Full Restore test --- test/item_effect_heal_and_cure_status.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/item_effect_heal_and_cure_status.c diff --git a/test/item_effect_heal_and_cure_status.c b/test/item_effect_heal_and_cure_status.c new file mode 100644 index 0000000000..742f432f50 --- /dev/null +++ b/test/item_effect_heal_and_cure_status.c @@ -0,0 +1,25 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } + } SCENE { + MESSAGE("Full Restore restored Wobbuffet's health!"); + } FINALLY { + EXPECT_EQ(player->hp, player->maxHP); + EXPECT_EQ(player->status1, STATUS1_NONE); + } +} \ No newline at end of file From 38476bd22c2ef28ab3f5401244178cc7d5e1056c Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 21 Apr 2023 21:37:02 +0200 Subject: [PATCH 18/88] fix Full Restore not healing confusion --- data/battle_scripts_2.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index dee482d1d3..ea1a67a680 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -76,7 +76,7 @@ BattleScript_ItemCureStatus:: BattleScript_ItemHealAndCureStatus:: call BattleScript_UseItemMessage itemrestorehp - curestatus BS_ATTACKER + itemcurestatus printstring STRINGID_ITEMRESTOREDSPECIESHEALTH waitmessage B_WAIT_TIME_LONG bichalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT From 4bad450ecfe113e51c7ce1400ca577b906435ada Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 21 Apr 2023 21:57:00 +0200 Subject: [PATCH 19/88] Full Restore confusion test PLS READ COMMENT THIS IS KNOWN_FAILING --- test/item_effect_heal_and_cure_status.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/item_effect_heal_and_cure_status.c b/test/item_effect_heal_and_cure_status.c index 742f432f50..d9bf0cf7c8 100644 --- a/test/item_effect_heal_and_cure_status.c +++ b/test/item_effect_heal_and_cure_status.c @@ -22,4 +22,25 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s EXPECT_EQ(player->hp, player->maxHP); EXPECT_EQ(player->status1, STATUS1_NONE); } -} \ No newline at end of file +} + +SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") +{ + // known failing bc even though it passes there is an underlying bug still that is only obvious in the replay. see #2938 + // this also means that this test is likely not very good so I advice this to be redone + KNOWN_FAILING; + GIVEN { + ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300);}; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN{ MOVE(opponent, MOVE_CONFUSE_RAY); } + TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } + TURN{ MOVE(player, MOVE_TACKLE); } + } SCENE { + MESSAGE("Full Restore restored Wobbuffet's health!"); + NONE_OF { MESSAGE("Wobbuffet is confused!"); } + } FINALLY { + EXPECT_EQ(player->hp, player->maxHP); + } +} From ff609f2761d69e1d2d0fa042a54fbe9dba95af0b Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Fri, 21 Apr 2023 22:51:33 +0200 Subject: [PATCH 20/88] healing items tests --- test/item_effect_restore_hp.c | 182 ++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index 971ec2400b..d5d652f65b 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -17,6 +17,188 @@ SINGLE_BATTLE_TEST("Potion restores a battler's HP by 20") } } +SINGLE_BATTLE_TEST("Super Potion restores a battler's HP by 50") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_SUPER_POTION, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -50); + } +} + +SINGLE_BATTLE_TEST("Hyper Potion restores a battler's HP by 200") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_HYPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_HYPER_POTION, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -200); + } +} + +SINGLE_BATTLE_TEST("Max Potion restores a battler's HP fully") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_MAX_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_MAX_POTION, partyIndex: 0); } + } FINALLY { + EXPECT_EQ(player->hp, player->maxHP); + } +} + +SINGLE_BATTLE_TEST("Fresh Water restores a battler's HP by 30") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_FRESH_WATER].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_FRESH_WATER, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -30); + } +} + +SINGLE_BATTLE_TEST("Soda Pop restores a battler's HP by 50") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_SODA_POP].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_SODA_POP, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -50); + } +} + +SINGLE_BATTLE_TEST("Lemonade restores a battler's HP by 70") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_LEMONADE].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(200); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_LEMONADE, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -70); + } +} + +SINGLE_BATTLE_TEST("Moomoo Milk restores a battler's HP by 100") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_MOOMOO_MILK].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(200); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_MOOMOO_MILK, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -100); + } +} + +SINGLE_BATTLE_TEST("Energy Powder restores a battler's HP by 50(60)") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_ENERGY_POWDER].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(200); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ENERGY_POWDER, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + #if I_HEALTH_RECOVERY >= GEN_7 + EXPECT_EQ(damage, -60); + #else + EXPECT_EQ(damage, -50); + #endif + } +} + +SINGLE_BATTLE_TEST("Energy Root restores a battler's HP by 120(200)") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_ENERGY_ROOT].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(300); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ENERGY_ROOT, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + #if I_HEALTH_RECOVERY >= GEN_7 + EXPECT_EQ(damage, -120); + #else + EXPECT_EQ(damage, -200); + #endif + } +} + +SINGLE_BATTLE_TEST("Sweet Heart restores a battler's HP by 20") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_SWEET_HEART].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_SWEET_HEART, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -20); + } +} + +SINGLE_BATTLE_TEST("Oran Berry restores a battler's HP by 10") +{ + s16 damage; + GIVEN { + ASSUME(gItems[ITEM_ORAN_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ORAN_BERRY, partyIndex: 0); } + } SCENE { + HP_BAR(player, captureDamage: &damage); + } FINALLY { + EXPECT_EQ(damage, -10); + } +} + SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP") { s16 damage; From 929e4749dabafb2e90a97a2b9fa8bd93c33dd2f7 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sat, 22 Apr 2023 02:34:50 +0200 Subject: [PATCH 21/88] temporarily rename test string for easier test runs --- test/item_effect_cure_status.c | 32 ++++++++++++++++---------------- test/item_effect_restore_hp.c | 26 +++++++++++++------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index 9747d18be6..f014a29b78 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed") +SINGLE_BATTLE_TEST("Cure Status: Paralyze Heal heals a battler from being paralyzed") { GIVEN { ASSUME(gItems[ITEM_PARALYZE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed") } } -SINGLE_BATTLE_TEST("Antidote heals a battler from being poisoned") +SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Antidote heals a battler from being poisoned") } } -SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned") +SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being badly poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -40,7 +40,7 @@ SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned") } } -SINGLE_BATTLE_TEST("Awakening heals a battler from being asleep") +SINGLE_BATTLE_TEST("Cure Status: Awakening heals a battler from being asleep") { GIVEN { ASSUME(gItems[ITEM_AWAKENING].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -53,7 +53,7 @@ SINGLE_BATTLE_TEST("Awakening heals a battler from being asleep") } } -SINGLE_BATTLE_TEST("Burn Heal heals a battler from being burned") +SINGLE_BATTLE_TEST("Cure Status: Burn Heal heals a battler from being burned") { GIVEN { ASSUME(gItems[ITEM_BURN_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -66,7 +66,7 @@ SINGLE_BATTLE_TEST("Burn Heal heals a battler from being burned") } } -SINGLE_BATTLE_TEST("Ice Heal heals a battler from being paralyzed") +SINGLE_BATTLE_TEST("Cure Status: Ice Heal heals a battler from being paralyzed") { GIVEN { ASSUME(gItems[ITEM_ICE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("Ice Heal heals a battler from being paralyzed") } } -SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -99,7 +99,7 @@ SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -119,7 +119,7 @@ SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Lava Cookies heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -139,7 +139,7 @@ SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Rage Candy Bar heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -159,7 +159,7 @@ SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Old Gateu heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -179,7 +179,7 @@ SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Casteliacone heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -199,7 +199,7 @@ SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Lumiose Galette heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -219,7 +219,7 @@ SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Shalour Sable heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -239,7 +239,7 @@ SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status") +SINGLE_BATTLE_TEST("Cure Status: Big Malasada heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -259,7 +259,7 @@ SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status") } } -SINGLE_BATTLE_TEST("Full Heal and local specialties heal a battler from being confused") +SINGLE_BATTLE_TEST("Cure Status: Full Heal and local specialties heal a battler from being confused") { u16 item; PARAMETRIZE { item = ITEM_FULL_HEAL; } diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index d5d652f65b..8898c772b5 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Potion restores a battler's HP by 20") +SINGLE_BATTLE_TEST("Restore HP: Potion restores a battler's HP by 20") { s16 damage; GIVEN { @@ -17,7 +17,7 @@ SINGLE_BATTLE_TEST("Potion restores a battler's HP by 20") } } -SINGLE_BATTLE_TEST("Super Potion restores a battler's HP by 50") +SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 50") { s16 damage; GIVEN { @@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Super Potion restores a battler's HP by 50") } } -SINGLE_BATTLE_TEST("Hyper Potion restores a battler's HP by 200") +SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 200") { s16 damage; GIVEN { @@ -49,7 +49,7 @@ SINGLE_BATTLE_TEST("Hyper Potion restores a battler's HP by 200") } } -SINGLE_BATTLE_TEST("Max Potion restores a battler's HP fully") +SINGLE_BATTLE_TEST("Restore HP: Max Potion restores a battler's HP fully") { s16 damage; GIVEN { @@ -63,7 +63,7 @@ SINGLE_BATTLE_TEST("Max Potion restores a battler's HP fully") } } -SINGLE_BATTLE_TEST("Fresh Water restores a battler's HP by 30") +SINGLE_BATTLE_TEST("Restore HP: Fresh Water restores a battler's HP by 30") { s16 damage; GIVEN { @@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("Fresh Water restores a battler's HP by 30") } } -SINGLE_BATTLE_TEST("Soda Pop restores a battler's HP by 50") +SINGLE_BATTLE_TEST("Restore HP: Soda Pop restores a battler's HP by 50") { s16 damage; GIVEN { @@ -95,7 +95,7 @@ SINGLE_BATTLE_TEST("Soda Pop restores a battler's HP by 50") } } -SINGLE_BATTLE_TEST("Lemonade restores a battler's HP by 70") +SINGLE_BATTLE_TEST("Restore HP: Lemonade restores a battler's HP by 70") { s16 damage; GIVEN { @@ -111,7 +111,7 @@ SINGLE_BATTLE_TEST("Lemonade restores a battler's HP by 70") } } -SINGLE_BATTLE_TEST("Moomoo Milk restores a battler's HP by 100") +SINGLE_BATTLE_TEST("Restore HP: Moomoo Milk restores a battler's HP by 100") { s16 damage; GIVEN { @@ -127,7 +127,7 @@ SINGLE_BATTLE_TEST("Moomoo Milk restores a battler's HP by 100") } } -SINGLE_BATTLE_TEST("Energy Powder restores a battler's HP by 50(60)") +SINGLE_BATTLE_TEST("Restore HP: Energy Powder restores a battler's HP by 60(50)") { s16 damage; GIVEN { @@ -147,7 +147,7 @@ SINGLE_BATTLE_TEST("Energy Powder restores a battler's HP by 50(60)") } } -SINGLE_BATTLE_TEST("Energy Root restores a battler's HP by 120(200)") +SINGLE_BATTLE_TEST("Restore HP: Energy Root restores a battler's HP by 120(200)") { s16 damage; GIVEN { @@ -167,7 +167,7 @@ SINGLE_BATTLE_TEST("Energy Root restores a battler's HP by 120(200)") } } -SINGLE_BATTLE_TEST("Sweet Heart restores a battler's HP by 20") +SINGLE_BATTLE_TEST("Restore HP: Sweet Heart restores a battler's HP by 20") { s16 damage; GIVEN { @@ -183,7 +183,7 @@ SINGLE_BATTLE_TEST("Sweet Heart restores a battler's HP by 20") } } -SINGLE_BATTLE_TEST("Oran Berry restores a battler's HP by 10") +SINGLE_BATTLE_TEST("Restore HP: Oran Berry restores a battler's HP by 10") { s16 damage; GIVEN { @@ -199,7 +199,7 @@ SINGLE_BATTLE_TEST("Oran Berry restores a battler's HP by 10") } } -SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP") +SINGLE_BATTLE_TEST("Restore HP: Sitrus Berry restores a battler's HP") { s16 damage; GIVEN { From 42215c94e7953375a3a4c0acde8ebac57d67963f Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sat, 22 Apr 2023 02:39:46 +0200 Subject: [PATCH 22/88] actually working restore hp tests --- test/item_effect_restore_hp.c | 106 ++++++++++++++-------------------- 1 file changed, 43 insertions(+), 63 deletions(-) diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index 8898c772b5..67c5eb202f 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -1,51 +1,48 @@ #include "global.h" #include "test_battle.h" +#define TEST_HP 1 +#define MAX_HP 400 + SINGLE_BATTLE_TEST("Restore HP: Potion restores a battler's HP by 20") { s16 damage; GIVEN { ASSUME(gItems[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_POTION, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -20); + HP_BAR(player, hp: TEST_HP + 20); } } -SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 50") +SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 60") { s16 damage; GIVEN { ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_SUPER_POTION, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -50); + HP_BAR(player, hp: TEST_HP + 60); } } -SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 200") +SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 120") { s16 damage; GIVEN { ASSUME(gItems[ITEM_HYPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(400); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_HYPER_POTION, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -200); + HP_BAR(player, hp: TEST_HP + 120); } } @@ -54,12 +51,12 @@ SINGLE_BATTLE_TEST("Restore HP: Max Potion restores a battler's HP fully") s16 damage; GIVEN { ASSUME(gItems[ITEM_MAX_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_MAX_POTION, partyIndex: 0); } - } FINALLY { - EXPECT_EQ(player->hp, player->maxHP); + } SCENE { + HP_BAR(player, hp: MAX_HP); } } @@ -68,14 +65,12 @@ SINGLE_BATTLE_TEST("Restore HP: Fresh Water restores a battler's HP by 30") s16 damage; GIVEN { ASSUME(gItems[ITEM_FRESH_WATER].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_FRESH_WATER, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -30); + HP_BAR(player, hp: TEST_HP + 30); } } @@ -84,14 +79,12 @@ SINGLE_BATTLE_TEST("Restore HP: Soda Pop restores a battler's HP by 50") s16 damage; GIVEN { ASSUME(gItems[ITEM_SODA_POP].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_SODA_POP, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -50); + HP_BAR(player, hp: TEST_HP + 50); } } @@ -100,14 +93,12 @@ SINGLE_BATTLE_TEST("Restore HP: Lemonade restores a battler's HP by 70") s16 damage; GIVEN { ASSUME(gItems[ITEM_LEMONADE].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(200); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_LEMONADE, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -70); + HP_BAR(player, hp: TEST_HP + 70); } } @@ -116,14 +107,12 @@ SINGLE_BATTLE_TEST("Restore HP: Moomoo Milk restores a battler's HP by 100") s16 damage; GIVEN { ASSUME(gItems[ITEM_MOOMOO_MILK].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(200); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_MOOMOO_MILK, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -100); + HP_BAR(player, hp: TEST_HP + 100); } } @@ -132,18 +121,15 @@ SINGLE_BATTLE_TEST("Restore HP: Energy Powder restores a battler's HP by 60(50)" s16 damage; GIVEN { ASSUME(gItems[ITEM_ENERGY_POWDER].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(200); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_ENERGY_POWDER, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - #if I_HEALTH_RECOVERY >= GEN_7 - EXPECT_EQ(damage, -60); - #else - EXPECT_EQ(damage, -50); - #endif + if (I_HEALTH_RECOVERY >= GEN_7) + HP_BAR(player, hp: TEST_HP + 60); + else + HP_BAR(player, hp: TEST_HP + 50); } } @@ -152,18 +138,15 @@ SINGLE_BATTLE_TEST("Restore HP: Energy Root restores a battler's HP by 120(200)" s16 damage; GIVEN { ASSUME(gItems[ITEM_ENERGY_ROOT].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(300); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_ENERGY_ROOT, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - #if I_HEALTH_RECOVERY >= GEN_7 - EXPECT_EQ(damage, -120); - #else - EXPECT_EQ(damage, -200); - #endif + if (I_HEALTH_RECOVERY >= GEN_7) + HP_BAR(player, hp: TEST_HP + 120); + else + HP_BAR(player, hp: TEST_HP + 200); } } @@ -172,14 +155,12 @@ SINGLE_BATTLE_TEST("Restore HP: Sweet Heart restores a battler's HP by 20") s16 damage; GIVEN { ASSUME(gItems[ITEM_SWEET_HEART].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_SWEET_HEART, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -20); + HP_BAR(player, hp: TEST_HP + 20); } } @@ -188,32 +169,31 @@ SINGLE_BATTLE_TEST("Restore HP: Oran Berry restores a battler's HP by 10") s16 damage; GIVEN { ASSUME(gItems[ITEM_ORAN_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_ORAN_BERRY, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { - EXPECT_EQ(damage, -10); + HP_BAR(player, hp: TEST_HP + 10); } } -SINGLE_BATTLE_TEST("Restore HP: Sitrus Berry restores a battler's HP") +SINGLE_BATTLE_TEST("Restore HP: Sitrus Berry restores a battler's HP by 25% of its max HP(30HP flat)") { s16 damage; GIVEN { ASSUME(gItems[ITEM_SITRUS_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_SITRUS_BERRY, partyIndex: 0); } } SCENE { - HP_BAR(player, captureDamage: &damage); - } FINALLY { if (I_SITRUS_BERRY_HEAL >= GEN_4) - EXPECT_EQ(damage, -25); + HP_BAR(player, hp: TEST_HP + MAX_HP * 0.25); else - EXPECT_EQ(damage, -30); + HP_BAR(player, hp: TEST_HP + 30); } } + +#undef TEST_HP +#undef MAX_HP \ No newline at end of file From 727f1ac37cb4de69e8d4bbf390a5628e7c719501 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sat, 22 Apr 2023 15:04:40 +0200 Subject: [PATCH 23/88] guard spec test --- test/item_effect_set_mist.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/item_effect_set_mist.c diff --git a/test/item_effect_set_mist.c b/test/item_effect_set_mist.c new file mode 100644 index 0000000000..e01775ec48 --- /dev/null +++ b/test/item_effect_set_mist.c @@ -0,0 +1,19 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Guard Spec. sets Mist effect on the battlers side") +{ + GIVEN { + ASSUME(gItems[ITEM_GUARD_SPEC].battleUsage == EFFECT_ITEM_SET_MIST); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_GUARD_SPEC, partyIndex: 0); } + TURN { MOVE(opponent, MOVE_GROWL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIST, player); + MESSAGE("Ally became shrouded in MIST!"); + MESSAGE("Foe Wobbuffet used Growl!"); + MESSAGE("Wobbuffet is protected by MIST!"); + } +} From 18ac81174d626bf858882dad8861283cd751290c Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sat, 22 Apr 2023 17:41:19 +0200 Subject: [PATCH 24/88] Dire Hit test currently known failing, bc of weird bug. see comment. --- test/item_effect_set_focus_energy.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/item_effect_set_focus_energy.c diff --git a/test/item_effect_set_focus_energy.c b/test/item_effect_set_focus_energy.c new file mode 100644 index 0000000000..ff7d82f497 --- /dev/null +++ b/test/item_effect_set_focus_energy.c @@ -0,0 +1,22 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages") +{ + KNOWN_FAILING; // Displays Message "Foe Wobbuffet used Dire Hit.." when facing off against Wobbuffet. Doesnt happen against other species? + ASSUME(B_CRIT_CHANCE >= GEN_7); + PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); + GIVEN { + ASSUME(gItems[ITEM_DIRE_HIT].battleUsage == EFFECT_ITEM_SET_FOCUS_ENERGY); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ABOMASNOW); + } WHEN { + TURN { USE_ITEM(player, ITEM_DIRE_HIT, partyIndex: 0); } + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); + MESSAGE("Wobbuffet used Dire Hit to get pumped!"); + MESSAGE("Wobbuffet used Scratch!"); + MESSAGE("A critical hit!"); + } +} From 1b6bb3e9ebd9380e1f5c3026a986caa6e8b6d963 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sat, 22 Apr 2023 17:53:37 +0200 Subject: [PATCH 25/88] make the test fail intentionally --- test/item_effect_set_focus_energy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/item_effect_set_focus_energy.c b/test/item_effect_set_focus_energy.c index ff7d82f497..72e90d610b 100644 --- a/test/item_effect_set_focus_energy.c +++ b/test/item_effect_set_focus_energy.c @@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stag GIVEN { ASSUME(gItems[ITEM_DIRE_HIT].battleUsage == EFFECT_ITEM_SET_FOCUS_ENERGY); PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_ABOMASNOW); + OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { USE_ITEM(player, ITEM_DIRE_HIT, partyIndex: 0); } TURN { MOVE(player, MOVE_SCRATCH); } From 0b66cf01ecb71fe83c98a2b70edc4eeb443a5864 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Mon, 24 Apr 2023 14:15:43 +0200 Subject: [PATCH 26/88] remove no longer needed variable --- test/item_effect_restore_hp.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index 67c5eb202f..4572a0d8be 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -6,7 +6,6 @@ SINGLE_BATTLE_TEST("Restore HP: Potion restores a battler's HP by 20") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -20,7 +19,6 @@ SINGLE_BATTLE_TEST("Restore HP: Potion restores a battler's HP by 20") SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 60") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -34,7 +32,6 @@ SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 60") SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 120") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_HYPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -48,7 +45,6 @@ SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 120") SINGLE_BATTLE_TEST("Restore HP: Max Potion restores a battler's HP fully") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_MAX_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -62,7 +58,6 @@ SINGLE_BATTLE_TEST("Restore HP: Max Potion restores a battler's HP fully") SINGLE_BATTLE_TEST("Restore HP: Fresh Water restores a battler's HP by 30") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_FRESH_WATER].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -76,7 +71,6 @@ SINGLE_BATTLE_TEST("Restore HP: Fresh Water restores a battler's HP by 30") SINGLE_BATTLE_TEST("Restore HP: Soda Pop restores a battler's HP by 50") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_SODA_POP].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -90,7 +84,6 @@ SINGLE_BATTLE_TEST("Restore HP: Soda Pop restores a battler's HP by 50") SINGLE_BATTLE_TEST("Restore HP: Lemonade restores a battler's HP by 70") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_LEMONADE].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -104,7 +97,6 @@ SINGLE_BATTLE_TEST("Restore HP: Lemonade restores a battler's HP by 70") SINGLE_BATTLE_TEST("Restore HP: Moomoo Milk restores a battler's HP by 100") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_MOOMOO_MILK].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -118,7 +110,6 @@ SINGLE_BATTLE_TEST("Restore HP: Moomoo Milk restores a battler's HP by 100") SINGLE_BATTLE_TEST("Restore HP: Energy Powder restores a battler's HP by 60(50)") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_ENERGY_POWDER].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -135,7 +126,6 @@ SINGLE_BATTLE_TEST("Restore HP: Energy Powder restores a battler's HP by 60(50)" SINGLE_BATTLE_TEST("Restore HP: Energy Root restores a battler's HP by 120(200)") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_ENERGY_ROOT].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -152,7 +142,6 @@ SINGLE_BATTLE_TEST("Restore HP: Energy Root restores a battler's HP by 120(200)" SINGLE_BATTLE_TEST("Restore HP: Sweet Heart restores a battler's HP by 20") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_SWEET_HEART].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -166,7 +155,6 @@ SINGLE_BATTLE_TEST("Restore HP: Sweet Heart restores a battler's HP by 20") SINGLE_BATTLE_TEST("Restore HP: Oran Berry restores a battler's HP by 10") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_ORAN_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } @@ -180,7 +168,6 @@ SINGLE_BATTLE_TEST("Restore HP: Oran Berry restores a battler's HP by 10") SINGLE_BATTLE_TEST("Restore HP: Sitrus Berry restores a battler's HP by 25% of its max HP(30HP flat)") { - s16 damage; GIVEN { ASSUME(gItems[ITEM_SITRUS_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } From c4497bc0a8ca077eccf305f9d16982e4cc8a80f2 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Mon, 24 Apr 2023 14:16:17 +0200 Subject: [PATCH 27/88] revive tests --- test/item_effect_revive.c | 96 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 test/item_effect_revive.c diff --git a/test/item_effect_revive.c b/test/item_effect_revive.c new file mode 100644 index 0000000000..9ced527f4e --- /dev/null +++ b/test/item_effect_revive.c @@ -0,0 +1,96 @@ +#include "global.h" +#include "test_battle.h" + +#define MAX_HP 200 + +SINGLE_BATTLE_TEST("Revive: Super Potion restores a fainted battler's HP to half") +{ + GIVEN { + ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { USE_ITEM(player, ITEM_SUPER_POTION, partyIndex: 0); } + TURN { SWITCH(player, 0); } + } SCENE { + MESSAGE("Super Potion restored Wynaut's health!"); + } FINALLY { + EXPECT_EQ(player->hp, 60); + } +} + +SINGLE_BATTLE_TEST("Revive: Revive restores a fainted battler's HP to half") +{ + GIVEN { + ASSUME(gItems[ITEM_REVIVE].battleUsage == EFFECT_ITEM_REVIVE); + PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { USE_ITEM(player, ITEM_REVIVE, partyIndex: 0); } + TURN { SWITCH(player, 0); } + } SCENE { + MESSAGE("Revive restored Wynaut's health!"); + } FINALLY { + EXPECT_EQ(player->hp, MAX_HP/2); + } +} + +SINGLE_BATTLE_TEST("Revive: Max Revive restores a fainted battler's HP fully") +{ + GIVEN { + ASSUME(gItems[ITEM_MAX_REVIVE].battleUsage == EFFECT_ITEM_REVIVE); + PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { USE_ITEM(player, ITEM_MAX_REVIVE, partyIndex: 0); } + TURN { SWITCH(player, 0); } + } SCENE { + MESSAGE("Max Revive restored Wynaut's health!"); + } FINALLY { + EXPECT_EQ(player->hp, MAX_HP); + } +} + +SINGLE_BATTLE_TEST("Revive: Revival Herb restores a fainted battler's HP fully") +{ + GIVEN { + ASSUME(gItems[ITEM_REVIVAL_HERB].battleUsage == EFFECT_ITEM_REVIVE); + PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { USE_ITEM(player, ITEM_REVIVAL_HERB, partyIndex: 0); } + TURN { SWITCH(player, 0); } + } SCENE { + MESSAGE("Revival Herb restored Wynaut's health!"); + } FINALLY { + EXPECT_EQ(player->hp, MAX_HP); + } +} + +SINGLE_BATTLE_TEST("Revive: Max Honey restores a fainted battler's HP fully") +{ + GIVEN { + ASSUME(gItems[ITEM_MAX_HONEY].battleUsage == EFFECT_ITEM_REVIVE); + PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { USE_ITEM(player, ITEM_MAX_HONEY, partyIndex: 0); } + TURN { SWITCH(player, 0); } + } SCENE { + MESSAGE("Max Honey restored Wynaut's health!"); + } FINALLY { + EXPECT_EQ(player->hp, MAX_HP); + } +} + +#undef MAX_HP From f74a1b07660ad5fd8c2e205074f1e265df98657d Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Mon, 24 Apr 2023 22:26:56 +0200 Subject: [PATCH 28/88] fix strings after update --- test/item_effect_cure_status.c | 61 +++++++------------------ test/item_effect_heal_and_cure_status.c | 5 +- test/item_effect_revive.c | 10 ++-- 3 files changed, 23 insertions(+), 53 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index f014a29b78..c593aa016c 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Cure Status: Paralyze Heal heals a battler from being paraly } WHEN { TURN { USE_ITEM(player, ITEM_PARALYZE_HEAL, partyIndex: 0); } } SCENE { - MESSAGE("Paralyze Heal cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being poisoned") } WHEN { TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } } SCENE { - MESSAGE("Antidote cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -36,7 +36,7 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being badly poiso } WHEN { TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } } SCENE { - MESSAGE("Antidote cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -49,7 +49,7 @@ SINGLE_BATTLE_TEST("Cure Status: Awakening heals a battler from being asleep") } WHEN { TURN { USE_ITEM(player, ITEM_AWAKENING, partyIndex: 0); } } SCENE { - MESSAGE("Awakening cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -62,7 +62,7 @@ SINGLE_BATTLE_TEST("Cure Status: Burn Heal heals a battler from being burned") } WHEN { TURN { USE_ITEM(player, ITEM_BURN_HEAL, partyIndex: 0); } } SCENE { - MESSAGE("Burn Heal cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -75,7 +75,7 @@ SINGLE_BATTLE_TEST("Cure Status: Ice Heal heals a battler from being paralyzed") } WHEN { TURN { USE_ITEM(player, ITEM_ICE_HEAL, partyIndex: 0); } } SCENE { - MESSAGE("Ice Heal cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -95,7 +95,7 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary stat } WHEN { TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); } } SCENE { - MESSAGE("Full Heal cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -115,7 +115,7 @@ SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any prima } WHEN { TURN { USE_ITEM(player, ITEM_PEWTER_CRUNCHIES, partyIndex: 0); } } SCENE { - MESSAGE("PewtrCrnches cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -135,7 +135,7 @@ SINGLE_BATTLE_TEST("Cure Status: Lava Cookies heals a battler from any primary s } WHEN { TURN { USE_ITEM(player, ITEM_LAVA_COOKIE, partyIndex: 0); } } SCENE { - MESSAGE("Lava Cookie cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -155,7 +155,7 @@ SINGLE_BATTLE_TEST("Cure Status: Rage Candy Bar heals a battler from any primary } WHEN { TURN { USE_ITEM(player, ITEM_RAGE_CANDY_BAR, partyIndex: 0); } } SCENE { - MESSAGE("RageCandyBar cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -175,7 +175,7 @@ SINGLE_BATTLE_TEST("Cure Status: Old Gateu heals a battler from any primary stat } WHEN { TURN { USE_ITEM(player, ITEM_OLD_GATEAU, partyIndex: 0); } } SCENE { - MESSAGE("Old Gateau cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -195,7 +195,7 @@ SINGLE_BATTLE_TEST("Cure Status: Casteliacone heals a battler from any primary s } WHEN { TURN { USE_ITEM(player, ITEM_CASTELIACONE, partyIndex: 0); } } SCENE { - MESSAGE("Casteliacone cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -215,7 +215,7 @@ SINGLE_BATTLE_TEST("Cure Status: Lumiose Galette heals a battler from any primar } WHEN { TURN { USE_ITEM(player, ITEM_LUMIOSE_GALETTE, partyIndex: 0); } } SCENE { - MESSAGE("LumioseGlete cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!");; } } @@ -235,7 +235,7 @@ SINGLE_BATTLE_TEST("Cure Status: Shalour Sable heals a battler from any primary } WHEN { TURN { USE_ITEM(player, ITEM_SHALOUR_SABLE, partyIndex: 0); } } SCENE { - MESSAGE("ShalourSable cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -255,7 +255,7 @@ SINGLE_BATTLE_TEST("Cure Status: Big Malasada heals a battler from any primary s } WHEN { TURN { USE_ITEM(player, ITEM_BIG_MALASADA, partyIndex: 0); } } SCENE { - MESSAGE("Big Malasada cured Wobbuffet's status!"); + MESSAGE("Wobbuffet had its status healed!"); } } @@ -279,35 +279,6 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal and local specialties heal a battler TURN { MOVE(opponent, MOVE_CONFUSE_RAY); } TURN { USE_ITEM(player, item, partyIndex: 0); } } SCENE { - switch (item) - { - case ITEM_FULL_HEAL: - MESSAGE("Full Heal cured Wobbuffet's status!"); - break; - case ITEM_PEWTER_CRUNCHIES: - MESSAGE("PewtrCrnches cured Wobbuffet's status!"); - break; - case ITEM_LAVA_COOKIE: - MESSAGE("Lava Cookie cured Wobbuffet's status!"); - break; - case ITEM_RAGE_CANDY_BAR: - MESSAGE("RageCandyBar cured Wobbuffet's status!"); - break; - case ITEM_OLD_GATEAU: - MESSAGE("Old Gateau cured Wobbuffet's status!"); - break; - case ITEM_CASTELIACONE: - MESSAGE("Casteliacone cured Wobbuffet's status!"); - break; - case ITEM_LUMIOSE_GALETTE: - MESSAGE("LumioseGlete cured Wobbuffet's status!"); - break; - case ITEM_SHALOUR_SABLE: - MESSAGE("ShalourSable cured Wobbuffet's status!"); - break; - case ITEM_BIG_MALASADA: - MESSAGE("Big Malasada cured Wobbuffet's status!"); - break; - } + MESSAGE("Wobbuffet had its status healed!"); } } diff --git a/test/item_effect_heal_and_cure_status.c b/test/item_effect_heal_and_cure_status.c index d9bf0cf7c8..95f97fbaa3 100644 --- a/test/item_effect_heal_and_cure_status.c +++ b/test/item_effect_heal_and_cure_status.c @@ -17,7 +17,7 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s } WHEN { TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } } SCENE { - MESSAGE("Full Restore restored Wobbuffet's health!"); + MESSAGE("Wobbuffet had its HP restored!"); } FINALLY { EXPECT_EQ(player->hp, player->maxHP); EXPECT_EQ(player->status1, STATUS1_NONE); @@ -28,7 +28,6 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") { // known failing bc even though it passes there is an underlying bug still that is only obvious in the replay. see #2938 // this also means that this test is likely not very good so I advice this to be redone - KNOWN_FAILING; GIVEN { ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300);}; @@ -38,7 +37,7 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } TURN{ MOVE(player, MOVE_TACKLE); } } SCENE { - MESSAGE("Full Restore restored Wobbuffet's health!"); + MESSAGE("Wobbuffet had its HP restored!"); NONE_OF { MESSAGE("Wobbuffet is confused!"); } } FINALLY { EXPECT_EQ(player->hp, player->maxHP); diff --git a/test/item_effect_revive.c b/test/item_effect_revive.c index 9ced527f4e..695361892e 100644 --- a/test/item_effect_revive.c +++ b/test/item_effect_revive.c @@ -15,7 +15,7 @@ SINGLE_BATTLE_TEST("Revive: Super Potion restores a fainted battler's HP to half TURN { USE_ITEM(player, ITEM_SUPER_POTION, partyIndex: 0); } TURN { SWITCH(player, 0); } } SCENE { - MESSAGE("Super Potion restored Wynaut's health!"); + MESSAGE("Wynaut had its HP restored!"); } FINALLY { EXPECT_EQ(player->hp, 60); } @@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Revive: Revive restores a fainted battler's HP to half") TURN { USE_ITEM(player, ITEM_REVIVE, partyIndex: 0); } TURN { SWITCH(player, 0); } } SCENE { - MESSAGE("Revive restored Wynaut's health!"); + MESSAGE("Wynaut had its HP restored!"); } FINALLY { EXPECT_EQ(player->hp, MAX_HP/2); } @@ -51,7 +51,7 @@ SINGLE_BATTLE_TEST("Revive: Max Revive restores a fainted battler's HP fully") TURN { USE_ITEM(player, ITEM_MAX_REVIVE, partyIndex: 0); } TURN { SWITCH(player, 0); } } SCENE { - MESSAGE("Max Revive restored Wynaut's health!"); + MESSAGE("Wynaut had its HP restored!"); } FINALLY { EXPECT_EQ(player->hp, MAX_HP); } @@ -69,7 +69,7 @@ SINGLE_BATTLE_TEST("Revive: Revival Herb restores a fainted battler's HP fully") TURN { USE_ITEM(player, ITEM_REVIVAL_HERB, partyIndex: 0); } TURN { SWITCH(player, 0); } } SCENE { - MESSAGE("Revival Herb restored Wynaut's health!"); + MESSAGE("Wynaut had its HP restored!"); } FINALLY { EXPECT_EQ(player->hp, MAX_HP); } @@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Revive: Max Honey restores a fainted battler's HP fully") TURN { USE_ITEM(player, ITEM_MAX_HONEY, partyIndex: 0); } TURN { SWITCH(player, 0); } } SCENE { - MESSAGE("Max Honey restored Wynaut's health!"); + MESSAGE("Wynaut had its HP restored!"); } FINALLY { EXPECT_EQ(player->hp, MAX_HP); } From 59666a1eb42d0699f27c102614234f4a8abd03eb Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Mon, 24 Apr 2023 22:36:58 +0200 Subject: [PATCH 29/88] heal powder test --- test/item_effect_cure_status.c | 21 +++++++++++++++++++++ test/item_effect_heal_and_cure_status.c | 2 -- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index c593aa016c..229f4e2b79 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -99,6 +99,26 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary stat } } +SINGLE_BATTLE_TEST("Cure Status: Heal Powder heals a battler from any primary status") +{ + u16 status; + PARAMETRIZE{ status = STATUS1_BURN; } + PARAMETRIZE{ status = STATUS1_FREEZE; } + PARAMETRIZE{ status = STATUS1_PARALYSIS; } + PARAMETRIZE{ status = STATUS1_POISON; } + PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } + PARAMETRIZE{ status = STATUS1_SLEEP; } + GIVEN { + ASSUME(gItems[ITEM_HEAL_POWDER].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_HEAL_POWDER, partyIndex: 0); } + } SCENE { + MESSAGE("Wobbuffet had its status healed!"); + } +} + SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any primary status") { u16 status; @@ -263,6 +283,7 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal and local specialties heal a battler { u16 item; PARAMETRIZE { item = ITEM_FULL_HEAL; } + PARAMETRIZE { item = ITEM_HEAL_POWDER; } PARAMETRIZE { item = ITEM_PEWTER_CRUNCHIES; } PARAMETRIZE { item = ITEM_LAVA_COOKIE; } PARAMETRIZE { item = ITEM_RAGE_CANDY_BAR; } diff --git a/test/item_effect_heal_and_cure_status.c b/test/item_effect_heal_and_cure_status.c index 95f97fbaa3..6e264d25ac 100644 --- a/test/item_effect_heal_and_cure_status.c +++ b/test/item_effect_heal_and_cure_status.c @@ -26,8 +26,6 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") { - // known failing bc even though it passes there is an underlying bug still that is only obvious in the replay. see #2938 - // this also means that this test is likely not very good so I advice this to be redone GIVEN { ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300);}; From 60e048b83e967db723f2b6893555e14a44a8b0cb Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 25 Apr 2023 13:44:16 +0200 Subject: [PATCH 30/88] more robust status cure tests --- test/item_effect_cure_status.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index 229f4e2b79..4d4cbda2ee 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -11,6 +11,8 @@ SINGLE_BATTLE_TEST("Cure Status: Paralyze Heal heals a battler from being paraly TURN { USE_ITEM(player, ITEM_PARALYZE_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -24,6 +26,8 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being poisoned") TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -37,6 +41,8 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being badly poiso TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -50,6 +56,8 @@ SINGLE_BATTLE_TEST("Cure Status: Awakening heals a battler from being asleep") TURN { USE_ITEM(player, ITEM_AWAKENING, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -63,6 +71,8 @@ SINGLE_BATTLE_TEST("Cure Status: Burn Heal heals a battler from being burned") TURN { USE_ITEM(player, ITEM_BURN_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -76,6 +86,8 @@ SINGLE_BATTLE_TEST("Cure Status: Ice Heal heals a battler from being paralyzed") TURN { USE_ITEM(player, ITEM_ICE_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -96,6 +108,8 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary stat TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -116,6 +130,8 @@ SINGLE_BATTLE_TEST("Cure Status: Heal Powder heals a battler from any primary st TURN { USE_ITEM(player, ITEM_HEAL_POWDER, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -136,6 +152,8 @@ SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any prima TURN { USE_ITEM(player, ITEM_PEWTER_CRUNCHIES, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -156,6 +174,8 @@ SINGLE_BATTLE_TEST("Cure Status: Lava Cookies heals a battler from any primary s TURN { USE_ITEM(player, ITEM_LAVA_COOKIE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -176,6 +196,8 @@ SINGLE_BATTLE_TEST("Cure Status: Rage Candy Bar heals a battler from any primary TURN { USE_ITEM(player, ITEM_RAGE_CANDY_BAR, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -196,6 +218,8 @@ SINGLE_BATTLE_TEST("Cure Status: Old Gateu heals a battler from any primary stat TURN { USE_ITEM(player, ITEM_OLD_GATEAU, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -216,6 +240,8 @@ SINGLE_BATTLE_TEST("Cure Status: Casteliacone heals a battler from any primary s TURN { USE_ITEM(player, ITEM_CASTELIACONE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -236,6 +262,8 @@ SINGLE_BATTLE_TEST("Cure Status: Lumiose Galette heals a battler from any primar TURN { USE_ITEM(player, ITEM_LUMIOSE_GALETTE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!");; + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -256,6 +284,8 @@ SINGLE_BATTLE_TEST("Cure Status: Shalour Sable heals a battler from any primary TURN { USE_ITEM(player, ITEM_SHALOUR_SABLE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -276,6 +306,8 @@ SINGLE_BATTLE_TEST("Cure Status: Big Malasada heals a battler from any primary s TURN { USE_ITEM(player, ITEM_BIG_MALASADA, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -301,5 +333,7 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal and local specialties heal a battler TURN { USE_ITEM(player, item, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); + } FINALLY { + EXPECT_EQ(player->status2, STATUS1_NONE); // because we dont have STATUS2_NONE } } From 7fad0a2dbce29dfa50e40e6c2194c3462df7626d Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 25 Apr 2023 19:45:35 +0200 Subject: [PATCH 31/88] add MovesWithPP macro to allow for setting pps in tests --- test/test_battle.h | 5 +++++ test/test_runner_battle.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/test/test_battle.h b/test/test_battle.h index 967bb661dd..0f413a2e47 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -713,6 +713,9 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext); /* Given */ +#define MOVE_ARGS(...) {__VA_ARGS__} +#define PP_ARGS(...) {__VA_ARGS__} + #define GIVEN for (; gBattleTestRunnerState->runGiven; gBattleTestRunnerState->runGiven = FALSE) #define RNGSeed(seed) RNGSeed_(__LINE__, seed) @@ -733,6 +736,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext); #define Speed(speed) Speed_(__LINE__, speed) #define Item(item) Item_(__LINE__, item) #define Moves(move1, ...) Moves_(__LINE__, (const u16 [MAX_MON_MOVES]) { move1, __VA_ARGS__ }) +#define MovesWithPP(moves, pp) MovesWithPP_(__LINE__, (const u16 [MAX_MON_MOVES]) MOVE_ARGS moves, (const u8 [MAX_MON_MOVES]) PP_ARGS pp) #define Friendship(friendship) Friendship_(__LINE__, friendship) #define Status1(status1) Status1_(__LINE__, status1) @@ -753,6 +757,7 @@ void SpDefense_(u32 sourceLine, u32 spDefense); void Speed_(u32 sourceLine, u32 speed); void Item_(u32 sourceLine, u32 item); void Moves_(u32 sourceLine, const u16 moves[MAX_MON_MOVES]); +void MovesWithPP_(u32 sourceLine, const u16 moves[MAX_MON_MOVES], const u8 pp[MAX_MON_MOVES]); void Friendship_(u32 sourceLine, u32 friendship); void Status1_(u32 sourceLine, u32 status1); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 2a44cc018b..33fcd5a0f3 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1161,6 +1161,21 @@ void Moves_(u32 sourceLine, const u16 moves[MAX_MON_MOVES]) DATA.explicitMoves[DATA.currentSide] |= 1 << DATA.currentPartyIndex; } +void MovesWithPP_(u32 sourceLine, const u16 moves[MAX_MON_MOVES], const u8 pp[MAX_MON_MOVES]) +{ + s32 i; + INVALID_IF(!DATA.currentMon, "Moves outside of PLAYER/OPPONENT"); + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (moves[i] == MOVE_NONE) + break; + INVALID_IF(moves[i] >= MOVES_COUNT, "Illegal move: %d", moves[i]); + SetMonData(DATA.currentMon, MON_DATA_MOVE1 + i, &moves[i]); + SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &pp[i]); + } + DATA.explicitMoves[DATA.currentSide] |= 1 << DATA.currentPartyIndex; +} + void Friendship_(u32 sourceLine, u32 friendship) { INVALID_IF(!DATA.currentMon, "Friendship outside of PLAYER/OPPONENT"); From 08ec873ef27da02404c35c9ae911779043edaf9a Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 25 Apr 2023 20:35:36 +0200 Subject: [PATCH 32/88] update MoveWithPP macro to use struct --- test/test_battle.h | 10 ++++++---- test/test_runner_battle.c | 10 +++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/test_battle.h b/test/test_battle.h index 0f413a2e47..f2d8bab786 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -713,8 +713,10 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext); /* Given */ -#define MOVE_ARGS(...) {__VA_ARGS__} -#define PP_ARGS(...) {__VA_ARGS__} +struct moveWithPP { + u16 moveId; + u8 pp; +}; #define GIVEN for (; gBattleTestRunnerState->runGiven; gBattleTestRunnerState->runGiven = FALSE) @@ -736,7 +738,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext); #define Speed(speed) Speed_(__LINE__, speed) #define Item(item) Item_(__LINE__, item) #define Moves(move1, ...) Moves_(__LINE__, (const u16 [MAX_MON_MOVES]) { move1, __VA_ARGS__ }) -#define MovesWithPP(moves, pp) MovesWithPP_(__LINE__, (const u16 [MAX_MON_MOVES]) MOVE_ARGS moves, (const u8 [MAX_MON_MOVES]) PP_ARGS pp) +#define MovesWithPP(movewithpp1, ...) MovesWithPP_(__LINE__, (struct moveWithPP[MAX_MON_MOVES]) {movewithpp1, __VA_ARGS__}) #define Friendship(friendship) Friendship_(__LINE__, friendship) #define Status1(status1) Status1_(__LINE__, status1) @@ -757,7 +759,7 @@ void SpDefense_(u32 sourceLine, u32 spDefense); void Speed_(u32 sourceLine, u32 speed); void Item_(u32 sourceLine, u32 item); void Moves_(u32 sourceLine, const u16 moves[MAX_MON_MOVES]); -void MovesWithPP_(u32 sourceLine, const u16 moves[MAX_MON_MOVES], const u8 pp[MAX_MON_MOVES]); +void MovesWithPP_(u32 sourceLine, struct moveWithPP moveWithPP[MAX_MON_MOVES]); void Friendship_(u32 sourceLine, u32 friendship); void Status1_(u32 sourceLine, u32 status1); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 33fcd5a0f3..75e459e49b 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1161,17 +1161,17 @@ void Moves_(u32 sourceLine, const u16 moves[MAX_MON_MOVES]) DATA.explicitMoves[DATA.currentSide] |= 1 << DATA.currentPartyIndex; } -void MovesWithPP_(u32 sourceLine, const u16 moves[MAX_MON_MOVES], const u8 pp[MAX_MON_MOVES]) +void MovesWithPP_(u32 sourceLine, struct moveWithPP moveWithPP[MAX_MON_MOVES]) { s32 i; INVALID_IF(!DATA.currentMon, "Moves outside of PLAYER/OPPONENT"); for (i = 0; i < MAX_MON_MOVES; i++) { - if (moves[i] == MOVE_NONE) + if (moveWithPP[i].moveId == MOVE_NONE) break; - INVALID_IF(moves[i] >= MOVES_COUNT, "Illegal move: %d", moves[i]); - SetMonData(DATA.currentMon, MON_DATA_MOVE1 + i, &moves[i]); - SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &pp[i]); + INVALID_IF(moveWithPP[i].moveId >= MOVES_COUNT, "Illegal move: %d", &moveWithPP[i].moveId); + SetMonData(DATA.currentMon, MON_DATA_MOVE1 + i, &moveWithPP[i].moveId); + SetMonData(DATA.currentMon, MON_DATA_PP1 + i, &moveWithPP[i].pp); } DATA.explicitMoves[DATA.currentSide] |= 1 << DATA.currentPartyIndex; } From d3e9a689fdf1a42519f0cf77777bde189758232f Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 25 Apr 2023 21:26:19 +0200 Subject: [PATCH 33/88] restore pp item tests --- test/item_effect_restore_pp.c | 61 +++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/test/item_effect_restore_pp.c b/test/item_effect_restore_pp.c index 234d4ae4e5..f21593566f 100644 --- a/test/item_effect_restore_pp.c +++ b/test/item_effect_restore_pp.c @@ -1,19 +1,66 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Ether restores the PP of one of a battler's moves") +SINGLE_BATTLE_TEST("Restore PP: Ether restores the PP of one of a battler's moves by 10 ") { GIVEN { ASSUME(gItems[ITEM_ETHER].battleUsage == EFFECT_ITEM_RESTORE_PP); ASSUME(gItems[ITEM_ETHER].type == ITEM_USE_PARTY_MENU_MOVES); - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, MOVE_CONFUSION); } + PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 20}); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } - TURN { MOVE(player, MOVE_CONFUSION); } TURN { USE_ITEM(player, ITEM_ETHER, partyIndex: 0, move: MOVE_TACKLE); } - } FINALLY { - EXPECT_EQ(player->pp[0], 35); - EXPECT_EQ(player->pp[1], 24); + } THEN { + EXPECT_EQ(player->pp[0], 10); + EXPECT_EQ(player->pp[1], 20); + } +} + +SINGLE_BATTLE_TEST("Restore PP: Max Ether restores the PP of one of a battler's moves fully") +{ + GIVEN { + ASSUME(gItems[ITEM_MAX_ETHER].battleUsage == EFFECT_ITEM_RESTORE_PP); + ASSUME(gItems[ITEM_MAX_ETHER].type == ITEM_USE_PARTY_MENU_MOVES); + PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 20}); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_MAX_ETHER, partyIndex: 0, move: MOVE_TACKLE); } + } THEN { + EXPECT_EQ(player->pp[0], 35); + EXPECT_EQ(player->pp[1], 20); + } +} + +SINGLE_BATTLE_TEST("Restore PP: Elixir restores the PP of all of a battler's moves by 10") +{ + GIVEN { + ASSUME(gItems[ITEM_ELIXIR].battleUsage == EFFECT_ITEM_RESTORE_PP); + ASSUME(gItems[ITEM_ELIXIR].type == ITEM_USE_PARTY_MENU); + PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 0}, {MOVE_SCRATCH, 0}, {MOVE_GROWL, 0}); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_ELIXIR, partyIndex: 0); } + } THEN { + EXPECT_EQ(player->pp[0], 10); + EXPECT_EQ(player->pp[1], 10); + EXPECT_EQ(player->pp[2], 10); + EXPECT_EQ(player->pp[3], 10); + } +} + +SINGLE_BATTLE_TEST("Restore PP: Max Elixir restores the PP of all of a battler's moves fully") +{ + GIVEN { + ASSUME(gItems[ITEM_MAX_ELIXIR].battleUsage == EFFECT_ITEM_RESTORE_PP); + ASSUME(gItems[ITEM_MAX_ELIXIR].type == ITEM_USE_PARTY_MENU); + PLAYER(SPECIES_WOBBUFFET) { MovesWithPP({MOVE_TACKLE, 0}, {MOVE_CONFUSION, 0}, {MOVE_SCRATCH, 0}, {MOVE_GROWL, 0}); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_MAX_ELIXIR, partyIndex: 0); } + } THEN { + EXPECT_EQ(player->pp[0], 35); + EXPECT_EQ(player->pp[1], 25); + EXPECT_EQ(player->pp[2], 35); + EXPECT_EQ(player->pp[3], 40); } } From 9f2589e1595cd8bf9f40a734bfc13b195b03f967 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 25 Apr 2023 23:06:07 +0200 Subject: [PATCH 34/88] better usage of THEN over FINALLY --- test/item_effect_cure_status.c | 36 ++++++++++++------------- test/item_effect_heal_and_cure_status.c | 4 +-- test/item_effect_revive.c | 26 +++--------------- 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index 4d4cbda2ee..949f60f0e4 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -11,7 +11,7 @@ SINGLE_BATTLE_TEST("Cure Status: Paralyze Heal heals a battler from being paraly TURN { USE_ITEM(player, ITEM_PARALYZE_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -26,7 +26,7 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being poisoned") TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -41,7 +41,7 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being badly poiso TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Cure Status: Awakening heals a battler from being asleep") TURN { USE_ITEM(player, ITEM_AWAKENING, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -71,7 +71,7 @@ SINGLE_BATTLE_TEST("Cure Status: Burn Heal heals a battler from being burned") TURN { USE_ITEM(player, ITEM_BURN_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -86,7 +86,7 @@ SINGLE_BATTLE_TEST("Cure Status: Ice Heal heals a battler from being paralyzed") TURN { USE_ITEM(player, ITEM_ICE_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -108,7 +108,7 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary stat TURN { USE_ITEM(player, ITEM_FULL_HEAL, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -130,7 +130,7 @@ SINGLE_BATTLE_TEST("Cure Status: Heal Powder heals a battler from any primary st TURN { USE_ITEM(player, ITEM_HEAL_POWDER, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -152,7 +152,7 @@ SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any prima TURN { USE_ITEM(player, ITEM_PEWTER_CRUNCHIES, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -174,7 +174,7 @@ SINGLE_BATTLE_TEST("Cure Status: Lava Cookies heals a battler from any primary s TURN { USE_ITEM(player, ITEM_LAVA_COOKIE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -196,7 +196,7 @@ SINGLE_BATTLE_TEST("Cure Status: Rage Candy Bar heals a battler from any primary TURN { USE_ITEM(player, ITEM_RAGE_CANDY_BAR, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -218,7 +218,7 @@ SINGLE_BATTLE_TEST("Cure Status: Old Gateu heals a battler from any primary stat TURN { USE_ITEM(player, ITEM_OLD_GATEAU, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -240,7 +240,7 @@ SINGLE_BATTLE_TEST("Cure Status: Casteliacone heals a battler from any primary s TURN { USE_ITEM(player, ITEM_CASTELIACONE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -262,7 +262,7 @@ SINGLE_BATTLE_TEST("Cure Status: Lumiose Galette heals a battler from any primar TURN { USE_ITEM(player, ITEM_LUMIOSE_GALETTE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!");; - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -284,7 +284,7 @@ SINGLE_BATTLE_TEST("Cure Status: Shalour Sable heals a battler from any primary TURN { USE_ITEM(player, ITEM_SHALOUR_SABLE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } @@ -306,12 +306,12 @@ SINGLE_BATTLE_TEST("Cure Status: Big Malasada heals a battler from any primary s TURN { USE_ITEM(player, ITEM_BIG_MALASADA, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status1, STATUS1_NONE); } } -SINGLE_BATTLE_TEST("Cure Status: Full Heal and local specialties heal a battler from being confused") +SINGLE_BATTLE_TEST("Cure Status: Full Heal, Heal Powder and Local Specialties heal a battler from being confused") { u16 item; PARAMETRIZE { item = ITEM_FULL_HEAL; } @@ -333,7 +333,7 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal and local specialties heal a battler TURN { USE_ITEM(player, item, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its status healed!"); - } FINALLY { + } THEN { EXPECT_EQ(player->status2, STATUS1_NONE); // because we dont have STATUS2_NONE } } diff --git a/test/item_effect_heal_and_cure_status.c b/test/item_effect_heal_and_cure_status.c index 6e264d25ac..17ac5be969 100644 --- a/test/item_effect_heal_and_cure_status.c +++ b/test/item_effect_heal_and_cure_status.c @@ -18,7 +18,7 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s TURN{ USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } } SCENE { MESSAGE("Wobbuffet had its HP restored!"); - } FINALLY { + } THEN { EXPECT_EQ(player->hp, player->maxHP); EXPECT_EQ(player->status1, STATUS1_NONE); } @@ -37,7 +37,7 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") } SCENE { MESSAGE("Wobbuffet had its HP restored!"); NONE_OF { MESSAGE("Wobbuffet is confused!"); } - } FINALLY { + } THEN { EXPECT_EQ(player->hp, player->maxHP); } } diff --git a/test/item_effect_revive.c b/test/item_effect_revive.c index 695361892e..3c6995176e 100644 --- a/test/item_effect_revive.c +++ b/test/item_effect_revive.c @@ -3,24 +3,6 @@ #define MAX_HP 200 -SINGLE_BATTLE_TEST("Revive: Super Potion restores a fainted battler's HP to half") -{ - GIVEN { - ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); - PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(MAX_HP); } - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } - TURN { USE_ITEM(player, ITEM_SUPER_POTION, partyIndex: 0); } - TURN { SWITCH(player, 0); } - } SCENE { - MESSAGE("Wynaut had its HP restored!"); - } FINALLY { - EXPECT_EQ(player->hp, 60); - } -} - SINGLE_BATTLE_TEST("Revive: Revive restores a fainted battler's HP to half") { GIVEN { @@ -34,7 +16,7 @@ SINGLE_BATTLE_TEST("Revive: Revive restores a fainted battler's HP to half") TURN { SWITCH(player, 0); } } SCENE { MESSAGE("Wynaut had its HP restored!"); - } FINALLY { + } THEN { EXPECT_EQ(player->hp, MAX_HP/2); } } @@ -52,7 +34,7 @@ SINGLE_BATTLE_TEST("Revive: Max Revive restores a fainted battler's HP fully") TURN { SWITCH(player, 0); } } SCENE { MESSAGE("Wynaut had its HP restored!"); - } FINALLY { + } THEN { EXPECT_EQ(player->hp, MAX_HP); } } @@ -70,7 +52,7 @@ SINGLE_BATTLE_TEST("Revive: Revival Herb restores a fainted battler's HP fully") TURN { SWITCH(player, 0); } } SCENE { MESSAGE("Wynaut had its HP restored!"); - } FINALLY { + } THEN { EXPECT_EQ(player->hp, MAX_HP); } } @@ -88,7 +70,7 @@ SINGLE_BATTLE_TEST("Revive: Max Honey restores a fainted battler's HP fully") TURN { SWITCH(player, 0); } } SCENE { MESSAGE("Wynaut had its HP restored!"); - } FINALLY { + } THEN { EXPECT_EQ(player->hp, MAX_HP); } } From a2809acfd1d6818acb6d39c5d8f1520746201c20 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Wed, 26 Apr 2023 01:02:47 +0200 Subject: [PATCH 35/88] fix gChosenMovePos not being assigned correctly for battle items thanks griffin <3 Co-Authored-By: Martin Griffin <838573+mrgriffin@users.noreply.github.com> --- include/recorded_battle.h | 1 + src/battle_controller_recorded_opponent.c | 1 + src/battle_controller_recorded_player.c | 1 + src/battle_script_commands.c | 10 +++++++--- test/test_runner_battle.c | 8 ++------ 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/recorded_battle.h b/include/recorded_battle.h index ea66096d7f..02bbe8fc3f 100644 --- a/include/recorded_battle.h +++ b/include/recorded_battle.h @@ -46,6 +46,7 @@ enum RECORDED_BATTLE_PALACE_ACTION, RECORDED_ITEM_ID, RECORDED_ITEM_TARGET, + RECORDED_ITEM_MOVE, }; extern u32 gRecordedBattleRngSeed; diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index bb41b42d65..8255abcab0 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -1442,6 +1442,7 @@ static void RecordedOpponentHandleChooseItem(void) u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); + gChosenMovePos = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedOpponentBufferExecCompleted(); } diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index efbd60f18b..8350e8594e 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -1466,6 +1466,7 @@ static void RecordedPlayerHandleChooseItem(void) u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); + gChosenMovePos = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedPlayerBufferExecCompleted(); } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 07669635e3..31ef4cea75 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16563,8 +16563,7 @@ void BS_ItemIncreaseStat(void) { void BS_ItemRestorePP(void) { NATIVE_ARGS(); const u8 *effect = GetItemEffect(gLastUsedItem); - u32 i, pp, maxPP, moveId; - u32 loopEnd = MAX_MON_MOVES; + u32 i, pp, maxPP, moveId, loopEnd; u32 battlerId = MAX_BATTLERS_COUNT; struct Pokemon *mon = (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) ? &gPlayerParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]] : &gEnemyParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]]; @@ -16574,6 +16573,11 @@ void BS_ItemRestorePP(void) { i = gChosenMovePos; loopEnd = gChosenMovePos + 1; } + else + { + i = 0; + loopEnd = MAX_MON_MOVES; + } // Check if the recipient is an active battler. if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker]) @@ -16583,7 +16587,7 @@ void BS_ItemRestorePP(void) { battlerId = BATTLE_PARTNER(gBattlerAttacker); // Heal PP! - for (i = 0; i < loopEnd; i++) + for (; i < loopEnd; i++) { pp = GetMonData(mon, MON_DATA_PP1 + i, NULL); moveId = GetMonData(mon, MON_DATA_MOVE1 + i, NULL); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 75e459e49b..f102912620 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1554,12 +1554,8 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c PushBattlerAction(sourceLine, battlerId, RECORDED_ACTION_TYPE, B_ACTION_USE_ITEM); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, (ctx.itemId >> 8) & 0xFF); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, ctx.itemId & 0xFF); - if (ctx.explicitPartyIndex) - PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, ctx.partyIndex); - else if (ctx.explicitMove) - PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, i); - else - PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, 0); + PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_TARGET, ctx.partyIndex); + PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_MOVE, i); DATA.actionBattlers |= 1 << battlerId; } From a5d39155c641da90df709db174961618ebd1b812 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:15:35 +0200 Subject: [PATCH 36/88] add Max Mushroom test --- test/item_effect_increase_stat.c | 121 +++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/test/item_effect_increase_stat.c b/test/item_effect_increase_stat.c index 7fcbe72794..bb5989207a 100644 --- a/test/item_effect_increase_stat.c +++ b/test/item_effect_increase_stat.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage) +SINGLE_BATTLE_TEST("Increase Stat: X Attack sharply raises battler's Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage) } } -SINGLE_BATTLE_TEST("X Defense sharply raises battler's Defense stat", s16 damage) +SINGLE_BATTLE_TEST("Increase Stat: X Defense sharply raises battler's Defense stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -47,7 +47,7 @@ SINGLE_BATTLE_TEST("X Defense sharply raises battler's Defense stat", s16 damage } } -SINGLE_BATTLE_TEST("X Sp. Atk sharply raises battler's Sp. Attack stat", s16 damage) +SINGLE_BATTLE_TEST("Increase Stat: X Sp. Atk sharply raises battler's Sp. Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -70,7 +70,7 @@ SINGLE_BATTLE_TEST("X Sp. Atk sharply raises battler's Sp. Attack stat", s16 dam } } -SINGLE_BATTLE_TEST("X Sp. Def sharply raises battler's Sp. Defense stat", s16 damage) +SINGLE_BATTLE_TEST("Increase Stat: X Sp. Def sharply raises battler's Sp. Defense stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -93,7 +93,7 @@ SINGLE_BATTLE_TEST("X Sp. Def sharply raises battler's Sp. Defense stat", s16 da } } -SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) +SINGLE_BATTLE_TEST("Increase Stat: X Speed sharply raises battler's Speed stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -106,7 +106,7 @@ SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }; } else - { + { PLAYER(SPECIES_WOBBUFFET) { Speed(4); }; OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }; } @@ -118,7 +118,7 @@ SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) { MESSAGE("Wobbuffet used Tackle!"); MESSAGE("Foe Wobbuffet used Tackle!"); - } else + } else { MESSAGE("Foe Wobbuffet used Tackle!"); MESSAGE("Wobbuffet used Tackle!"); @@ -126,7 +126,7 @@ SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) } } -SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat", s16 damage) +SINGLE_BATTLE_TEST("Increase Stat: X Accuracy sharply raises battler's Accuracy stat", s16 damage) { ASSUME(gBattleMoves[MOVE_SING].accuracy == 55); @@ -146,3 +146,108 @@ SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat", s16 dama MESSAGE("Foe Wobbuffet fell asleep!"); } } + +SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Attack stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + MESSAGE("Wobbuffet used Tackle!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Defense stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + MESSAGE("Foe Wobbuffet used Tackle!"); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Sp. Attack stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } + TURN { MOVE(player, MOVE_DISARMING_VOICE); } + } SCENE { + MESSAGE("Wobbuffet used DisrmngVoice!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms battler's Sp. Defense stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } + TURN { MOVE(opponent, MOVE_DISARMING_VOICE); } + } SCENE { + MESSAGE("Foe Wobbuffet used DisrmngVoice!"); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.66), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Speed stat", s16 damage) +{ + u16 useItem; + PARAMETRIZE { useItem = FALSE; } + PARAMETRIZE { useItem = TRUE; } + GIVEN { + ASSUME(gItems[ITEM_MAX_MUSHROOMS].battleUsage == EFFECT_ITEM_INCREASE_ALL_STATS); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); }; + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }; + } WHEN { + if (useItem) TURN { USE_ITEM(player, ITEM_MAX_MUSHROOMS); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + if (useItem) + { + MESSAGE("Wobbuffet used Tackle!"); + MESSAGE("Foe Wobbuffet used Tackle!"); + } else + { + MESSAGE("Foe Wobbuffet used Tackle!"); + MESSAGE("Wobbuffet used Tackle!"); + } + } +} From e83e08f21c2d05c8a844fce17a5da55abb96ccee Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 26 Apr 2023 12:32:43 -0400 Subject: [PATCH 37/88] Add time ranges to UpdateAmbientCry --- src/overworld.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/overworld.c b/src/overworld.c index 70b102fd28..3d46fc5005 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1260,23 +1260,34 @@ static void PlayAmbientCry(void) PlayCry_NormalNoDucking(sAmbientCrySpecies, pan, volume, CRY_PRIORITY_AMBIENT); } +// States for UpdateAmbientCry +enum { + AMB_CRY_INIT, + AMB_CRY_FIRST, + AMB_CRY_RESET, + AMB_CRY_WAIT, + AMB_CRY_IDLE, +}; + void UpdateAmbientCry(s16 *state, u16 *delayCounter) { u8 i, monsCount, divBy; switch (*state) { - case 0: + case AMB_CRY_INIT: + // This state will be revisited whenever ResetFieldTasksArgs is called (which happens on map transition) if (sAmbientCrySpecies == SPECIES_NONE) - *state = 4; + *state = AMB_CRY_IDLE; else - *state = 1; + *state = AMB_CRY_FIRST; break; - case 1: + case AMB_CRY_FIRST: + // It takes between 1200-3599 frames (~20-60 seconds) to play the first ambient cry after entering a map *delayCounter = (Random() % 2400) + 1200; - *state = 3; + *state = AMB_CRY_WAIT; break; - case 2: + case AMB_CRY_RESET: divBy = 1; monsCount = CalculatePlayerPartyCount(); for (i = 0; i < monsCount; i++) @@ -1288,18 +1299,20 @@ void UpdateAmbientCry(s16 *state, u16 *delayCounter) break; } } + // Ambient cries after the first one take between 1200-2399 frames (~20-40 seconds) + // If the player has a pokemon with the ability Swarm in their party, the time is halved to 600-1199 frames (~10-20 seconds) *delayCounter = ((Random() % 1200) + 1200) / divBy; - *state = 3; + *state = AMB_CRY_WAIT; break; - case 3: - (*delayCounter)--; - if (*delayCounter == 0) + case AMB_CRY_WAIT: + if (--(*delayCounter) == 0) { PlayAmbientCry(); - *state = 2; + *state = AMB_CRY_RESET; } break; - case 4: + case AMB_CRY_IDLE: + // No land/water pokemon on this map break; } } From 36e6981ac0df338f85cb49a3c1d56f4410dbe007 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 26 Apr 2023 16:12:00 -0400 Subject: [PATCH 38/88] Replace some & usage with % --- src/event_object_movement.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 3025e0849a..d4b89fbae1 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2575,7 +2575,7 @@ bool8 MovementType_WanderAround_Step2(struct ObjectEvent *objectEvent, struct Sp { if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; - SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]); sprite->sTypeFuncId = 3; return TRUE; } @@ -2855,7 +2855,7 @@ bool8 MovementType_LookAround_Step2(struct ObjectEvent *objectEvent, struct Spri { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -2907,7 +2907,7 @@ bool8 MovementType_WanderUpAndDown_Step2(struct ObjectEvent *objectEvent, struct if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; - SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]); sprite->sTypeFuncId = 3; return TRUE; } @@ -2975,7 +2975,7 @@ bool8 MovementType_WanderLeftAndRight_Step2(struct ObjectEvent *objectEvent, str if (!ObjectEventExecSingleMovementAction(objectEvent, sprite)) return FALSE; - SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]); sprite->sTypeFuncId = 3; return TRUE; } @@ -3195,7 +3195,7 @@ bool8 MovementType_FaceDownAndUp_Step2(struct ObjectEvent *objectEvent, struct S { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3245,7 +3245,7 @@ bool8 MovementType_FaceLeftAndRight_Step2(struct ObjectEvent *objectEvent, struc { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysMedium[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysMedium[Random() % ARRAY_COUNT(sMovementDelaysMedium)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3295,7 +3295,7 @@ bool8 MovementType_FaceUpAndLeft_Step2(struct ObjectEvent *objectEvent, struct S { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3345,7 +3345,7 @@ bool8 MovementType_FaceUpAndRight_Step2(struct ObjectEvent *objectEvent, struct { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3395,7 +3395,7 @@ bool8 MovementType_FaceDownAndLeft_Step2(struct ObjectEvent *objectEvent, struct { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3445,7 +3445,7 @@ bool8 MovementType_FaceDownAndRight_Step2(struct ObjectEvent *objectEvent, struc { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3495,7 +3495,7 @@ bool8 MovementType_FaceDownUpAndLeft_Step2(struct ObjectEvent *objectEvent, stru { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3545,7 +3545,7 @@ bool8 MovementType_FaceDownUpAndRight_Step2(struct ObjectEvent *objectEvent, str { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3595,7 +3595,7 @@ bool8 MovementType_FaceUpLeftAndRight_Step2(struct ObjectEvent *objectEvent, str { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } @@ -3645,7 +3645,7 @@ bool8 MovementType_FaceDownLeftAndRight_Step2(struct ObjectEvent *objectEvent, s { if (ObjectEventExecSingleMovementAction(objectEvent, sprite)) { - SetMovementDelay(sprite, sMovementDelaysShort[Random() & 3]); + SetMovementDelay(sprite, sMovementDelaysShort[Random() % ARRAY_COUNT(sMovementDelaysShort)]); objectEvent->singleMovementActive = FALSE; sprite->sTypeFuncId = 3; } From c2dad5ac44960f6d7b762c5ebb8199b66c1090df Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 27 Apr 2023 13:59:09 -0400 Subject: [PATCH 39/88] Redefine TV_SHOWS_COUNT --- include/constants/global.h | 1 - include/constants/tv.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/constants/global.h b/include/constants/global.h index 8e40e63786..04f1d548c4 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -46,7 +46,6 @@ #define OBJECT_EVENTS_COUNT 16 #define MAIL_COUNT (10 + PARTY_SIZE) #define SECRET_BASES_COUNT 20 -#define TV_SHOWS_COUNT 25 #define POKE_NEWS_COUNT 16 #define PC_ITEMS_COUNT 50 #define BAG_ITEMS_COUNT 30 diff --git a/include/constants/tv.h b/include/constants/tv.h index 87748a31de..13ec88ecdc 100644 --- a/include/constants/tv.h +++ b/include/constants/tv.h @@ -74,6 +74,7 @@ // for TV shows from TVGROUP_NORMAL. The remainder are for TV // shows from TVGROUP_RECORD_MIX. #define NUM_NORMAL_TVSHOW_SLOTS 5 +#define TV_SHOWS_COUNT (NUM_NORMAL_TVSHOW_SLOTS + 20) #define PLAYERS_HOUSE_TV_NONE 0 #define PLAYERS_HOUSE_TV_LATI 1 From b53c913558c5c454922a29c12cd864822b5da3dc Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sat, 29 Apr 2023 18:16:36 +0200 Subject: [PATCH 40/88] Manually port daycare expansion to modern standards --- include/config/pokemon.h | 13 +++-- src/daycare.c | 108 ++++++++++++++++++++++++++------------- 2 files changed, 82 insertions(+), 39 deletions(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 1cde573c96..6247217465 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -8,10 +8,15 @@ #define P_UPDATED_EGG_GROUPS GEN_LATEST // Since Gen 8, certain Pokémon have gained new egg groups. // Breeding settings -#define P_NIDORAN_M_DITTO_BREED GEN_LATEST // Since Gen 5, when Nidoran♂ breeds with Ditto it can produce Nidoran♀ offspring. Before, it would only yield male offspring. This change also applies to Volbeat. -#define P_INCENSE_BREEDING GEN_LATEST // Since Gen 9, cross-generation Baby Pokémon don't require Incense being held by the parents to be obtained via breeding. -#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5. -#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen7 onwards, the father can pass it down as well, as long as it's of the same species as the mother. +#define P_NIDORAN_M_DITTO_BREED GEN_LATEST // Since Gen 5, when Nidoran♂ breeds with Ditto it can produce Nidoran♀ offspring. Before, it would only yield male offspring. This change also applies to Volbeat. +#define P_INCENSE_BREEDING GEN_LATEST // Since Gen 9, cross-generation Baby Pokémon don't require Incense being held by the parents to be obtained via breeding. +#define P_EGG_HATCH_LEVEL GEN_LATEST // Since Gen 4, Pokémon will hatch from eggs at level 1 instead of 5. +#define P_BALL_INHERITING GEN_LATEST // Since Gen 6, Eggs from the Daycare will inherit the Poké Ball from their mother. From Gen 7 onwards, the father can pass it down as well, as long as it's of the same species as the mother. +#define P_TM_INHERITANCE GEN_LATEST // Since Gen 6, the father no longer passes down TMs to the baby. +#define P_MOTHER_EGG_MOVE_INHERITANCE GEN_LATEST // Since Gen 6, the mother can also pass down Egg Moves. +#define P_NATURE_INHERITANCE GEN_LATEST // In Gen 3, Everstone grants Ditto and mothers a 50% chance to pass on Nature. Since Gen 4, anyone can pass on nature. Since Gen 5, the chance is 100%. +#define P_ABILITY_INHERITANCE GEN_LATEST // TODO: In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted. +#define P_EGG_MOVE_TRANSFER GEN_LATEST // TODO: Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. // Species-specific settings #define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball. diff --git a/src/daycare.c b/src/daycare.c index 803c0cacf9..3feabe9ba9 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -21,11 +21,14 @@ #include "overworld.h" #include "item.h" #include "constants/items.h" +#include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/region_map_sections.h" extern const struct Evolution gEvolutionTable[][EVOS_PER_MON]; +#define IS_DITTO(species) (gBaseStats[species].eggGroup1 == EGG_GROUP_DITTO || gBaseStats[species].eggGroup2 == EGG_GROUP_DITTO) + static void ClearDaycareMonMail(struct DaycareMail *mail); static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare *daycare); static u8 GetDaycareCompatibilityScore(struct DayCare *daycare); @@ -425,43 +428,29 @@ static u16 GetEggSpecies(u16 species) static s32 GetParentToInheritNature(struct DayCare *daycare) { - u32 species[DAYCARE_MON_COUNT]; - s32 i; - s32 dittoCount; - s32 parent = -1; + u32 i; + u8 numWithEverstone = 0; + s32 slot = -1; - // search for female gender for (i = 0; i < DAYCARE_MON_COUNT; i++) { - if (GetBoxMonGender(&daycare->mons[i].mon) == MON_FEMALE) - parent = i; + if (ItemId_GetHoldEffect(GetBoxMonData(&daycare->mons[i].mon, MON_DATA_HELD_ITEM)) == HOLD_EFFECT_PREVENT_EVOLVE + #if P_NATURE_INHERITANCE == GEN_3 + && (GetBoxMonGender(&daycare->mons[i].mon) == MON_FEMALE || IS_DITTO(GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES))) + #endif + ) { + slot = i; + numWithEverstone++; + } } - // search for ditto - for (dittoCount = 0, i = 0; i < DAYCARE_MON_COUNT; i++) - { - species[i] = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES); - if (species[i] == SPECIES_DITTO) - dittoCount++, parent = i; - } - - // coin flip on ...two Dittos - if (dittoCount == DAYCARE_MON_COUNT) - { - if (Random() >= USHRT_MAX / 2) - parent = 0; - else - parent = 1; - } - - // Don't inherit nature if not holding Everstone - if (GetBoxMonData(&daycare->mons[parent].mon, MON_DATA_HELD_ITEM) != ITEM_EVERSTONE - || Random() >= USHRT_MAX / 2) - { - return -1; - } - - return parent; + if (numWithEverstone >= DAYCARE_MON_COUNT) + return Random() & 1; +#if P_NATURE_INHERITANCE > GEN_4 + return slot; +#else + return Random() & 1 ? slot : -1; +#endif } static void _TriggerPendingDaycareEgg(struct DayCare *daycare) @@ -541,7 +530,7 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare) { u16 motherItem = GetBoxMonData(&daycare->mons[0].mon, MON_DATA_HELD_ITEM); u16 fatherItem = GetBoxMonData(&daycare->mons[1].mon, MON_DATA_HELD_ITEM); - u8 i; + u8 i, start; u8 selectedIvs[5]; u8 availableIVs[NUM_STATS]; u8 whichParents[5]; @@ -557,8 +546,33 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare) availableIVs[i] = i; } + start = 0; + if (ItemId_GetHoldEffect(motherItem) == HOLD_EFFECT_POWER_ITEM && + ItemId_GetHoldEffect(fatherItem) == HOLD_EFFECT_POWER_ITEM) + { + whichParents[0] = Random() % DAYCARE_MON_COUNT; + selectedIvs[0] = ItemId_GetSecondaryId( + GetBoxMonData(&daycare->mons[whichParents[0]].mon, MON_DATA_HELD_ITEM)); + RemoveIVIndexFromList(availableIVs, selectedIvs[0]); + start++; + } + else if (ItemId_GetHoldEffect(motherItem) == HOLD_EFFECT_POWER_ITEM) + { + whichParents[0] = 0; + selectedIvs[0] = ItemId_GetSecondaryId(motherItem); + RemoveIVIndexFromList(availableIVs, selectedIvs[0]); + start++; + } + else if (ItemId_GetHoldEffect(fatherItem) == HOLD_EFFECT_POWER_ITEM) + { + whichParents[0] = 1; + selectedIvs[0] = ItemId_GetSecondaryId(fatherItem); + RemoveIVIndexFromList(availableIVs, selectedIvs[0]); + start++; + } + // Select which IVs that will be inherited. - for (i = 0; i < howManyIVs; i++) + for (i = start; i < howManyIVs; i++) { // Randomly pick an IV from the available list and stop from being chosen again. // BUG: Instead of removing the IV that was just picked, this @@ -577,7 +591,7 @@ static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare) } // Determine which parent each of the selected IVs should inherit from. - for (i = 0; i < howManyIVs; i++) + for (i = start; i < howManyIVs; i++) { whichParents[i] = Random() % DAYCARE_MON_COUNT; } @@ -703,6 +717,28 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru numEggMoves = GetEggMoves(egg, sHatchedEggEggMoves); +#if P_MOTHER_EGG_MOVE_INHERITANCE >= GEN_6 + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (sHatchedEggMotherMoves[i] != MOVE_NONE) + { + for (j = 0; j < numEggMoves; j++) + { + if (sHatchedEggMotherMoves[i] == sHatchedEggEggMoves[j]) + { + if (GiveMoveToMon(egg, sHatchedEggMotherMoves[i]) == MON_HAS_MAX_MOVES) + DeleteFirstMoveAndGiveMoveToMon(egg, sHatchedEggMotherMoves[i]); + break; + } + } + } + else + { + break; + } + } +#endif + for (i = 0; i < MAX_MON_MOVES; i++) { if (sHatchedEggFatherMoves[i] != MOVE_NONE) @@ -722,6 +758,7 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru break; } } +#if P_TM_INHERITANCE < GEN_6 for (i = 0; i < MAX_MON_MOVES; i++) { if (sHatchedEggFatherMoves[i] != MOVE_NONE) @@ -737,6 +774,7 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru } } } +#endif for (i = 0; i < MAX_MON_MOVES; i++) { if (sHatchedEggFatherMoves[i] == MOVE_NONE) From 99cbd2af37004fef58e8838f130b293b09f0ed2d Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Sat, 29 Apr 2023 12:30:39 +0200 Subject: [PATCH 41/88] add snow weather --- asm/macros/battle_script.inc | 4 ++ data/battle_anim_scripts.s | 19 ++++++++- data/battle_scripts_1.s | 30 ++++++++++++++- include/battle_ai_util.h | 1 + include/battle_scripts.h | 4 +- include/config/battle.h | 1 + include/constants/battle.h | 6 ++- include/constants/battle_ai.h | 1 + include/constants/battle_anim.h | 2 + include/constants/battle_move_effects.h | 3 +- include/constants/battle_string_ids.h | 14 ++++++- src/battle_ai_main.c | 34 ++++++++++++++++- src/battle_ai_util.c | 30 +++++++++++++-- src/battle_anim_effects_3.c | 2 + src/battle_gfx_sfx_util.c | 1 + src/battle_main.c | 4 +- src/battle_message.c | 19 ++++++++- src/battle_script_commands.c | 29 +++++++++++--- src/battle_tv.c | 3 +- src/battle_util.c | 51 +++++++++++++++++++++---- src/data/battle_moves.h | 2 +- 21 files changed, 229 insertions(+), 31 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 78fb914063..bfc81fbb6c 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1346,6 +1346,10 @@ callnative BS_ItemRestorePP .endm + .macro setsnow + callnative BS_SetSnow + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index adcb88c881..f1637fff9d 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -947,6 +947,7 @@ gBattleAnims_General:: .4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP .4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE .4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON + .4byte General_Snow @ B_ANIM_SNOW_CONTINUES .align 2 gBattleAnims_Special:: @@ -14448,7 +14449,19 @@ Move_ELECTRO_DRIFT:: Move_SHED_TAIL:: Move_CHILLY_RECEPTION:: Move_TIDY_UP:: -Move_SNOWSCAPE:: + +@ Also used by Snow weather. Currently identical with Move_HAIL +Move_SNOWSCAPE: + loadspritegfx ANIM_TAG_HAIL + loadspritegfx ANIM_TAG_ICE_CRYSTALS + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 6, RGB_BLACK + waitforvisualfinish + createvisualtask AnimTask_Hail, 5 + loopsewithpan SE_M_HAIL, 0, 8, 10 + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 6, 0, RGB_BLACK + end + Move_POUNCE:: Move_TRAILBLAZE:: Move_CHILLING_WATER:: @@ -23924,6 +23937,7 @@ Move_WEATHER_BALL: jumpreteq ANIM_WEATHER_RAIN, WeatherBallWater jumpreteq ANIM_WEATHER_SANDSTORM, WeatherBallSandstorm jumpreteq ANIM_WEATHER_HAIL, WeatherBallIce + jumpreteq ANIM_WEATHER_SNOW, WeatherBallIce WeatherBallNormal: loadspritegfx ANIM_TAG_IMPACT createsprite gWeatherBallNormalDownSpriteTemplate, ANIM_TARGET, 2, -30, -100, 25, 1, 0, 0 @@ -24679,6 +24693,9 @@ General_Sandstorm: General_Hail: goto Move_HAIL +General_Snow: + goto Move_SNOWSCAPE + General_LeechSeedDrain: createvisualtask AnimTask_GetBattlersFromArg, 5 delay 0 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index ec73fd6b25..0b76d8648d 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -424,6 +424,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE .4byte BattleScript_EffectRevivalBlessing @ EFFECT_REVIVAL_BLESSING + .4byte BattleScript_EffectSnow @ EFFECT_SNOW BattleScript_EffectRevivalBlessing:: attackcanceler @@ -6858,6 +6859,14 @@ BattleScript_DamagingWeatherContinuesEnd:: bicword gHitMarker, HITMARKER_SKIP_DMG_TRACK | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE | HITMARKER_GRUDGE end2 +BattleScript_SnowContinuesOrEnds:: + printfromtable gSnowContinuesStringIds + waitmessage B_WAIT_TIME_LONG + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SNOW_STOPPED, BattleScript_SnowContinuesOrEndsEnd + playanimation BS_ATTACKER, B_ANIM_SNOW_CONTINUES +BattleScript_SnowContinuesOrEndsEnd:: + end2 + BattleScript_SandStormHailEnds:: printfromtable gSandStormHailEndStringIds waitmessage B_WAIT_TIME_LONG @@ -8840,7 +8849,7 @@ BattleScript_MimicryActivates_End3:: waitmessage B_WAIT_TIME_SHORT end3 -BattleScript_SnowWarningActivates:: +BattleScript_SnowWarningActivatesHail:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp printstring STRINGID_SNOWWARNINGHAIL @@ -8849,6 +8858,15 @@ BattleScript_SnowWarningActivates:: call BattleScript_WeatherFormChanges end3 +BattleScript_SnowWarningActivatesSnow:: + pause B_WAIT_TIME_SHORT + call BattleScript_AbilityPopUp + printstring STRINGID_SNOWWARNINGSNOW + waitstate + playanimation BS_BATTLER_0, B_ANIM_SNOW_CONTINUES + call BattleScript_WeatherFormChanges + end3 + BattleScript_ActivateTerrainEffects: savetarget setbyte gBattlerTarget, 0 @@ -10422,3 +10440,13 @@ BattleScript_BerserkGeneRet_OwnTempoPrevents: BattleScript_BerserkGeneRet_End: removeitem BS_SCRIPTING end3 + +BattleScript_EffectSnow:: + attackcanceler + attackstring + ppreduce + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_SUN_PRIMAL, BattleScript_ExtremelyHarshSunlightWasNotLessened + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_NoReliefFromHeavyRain + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOn + setsnow + goto BattleScript_MoveWeatherChange diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index d1319f0ca7..0d134ff1ce 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -112,6 +112,7 @@ bool32 IsEncoreEncouragedEffect(u16 moveEffect); void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score); bool32 ShouldSetSandstorm(u8 battler, u16 ability, u16 holdEffect); bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect); +bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect); bool32 ShouldSetRain(u8 battlerAtk, u16 ability, u16 holdEffect); bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect); bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 876e2d343a..e0b756e937 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -43,6 +43,7 @@ extern const u8 BattleScript_ActionSwitch[]; extern const u8 BattleScript_Pausex20[]; extern const u8 BattleScript_LevelUp[]; extern const u8 BattleScript_RainContinuesOrEnds[]; +extern const u8 BattleScript_SnowContinuesOrEnds[]; extern const u8 BattleScript_DamagingWeatherContinues[]; extern const u8 BattleScript_SandStormHailEnds[]; extern const u8 BattleScript_SunlightContinues[]; @@ -269,7 +270,8 @@ extern const u8 BattleScript_CursedBodyActivates[]; extern const u8 BattleScript_MummyActivates[]; extern const u8 BattleScript_WeakArmorActivates[]; extern const u8 BattleScript_FellStingerRaisesStat[]; -extern const u8 BattleScript_SnowWarningActivates[]; +extern const u8 BattleScript_SnowWarningActivatesHail[]; +extern const u8 BattleScript_SnowWarningActivatesSnow[]; extern const u8 BattleScript_HarvestActivates[]; extern const u8 BattleScript_ImposterActivates[]; extern const u8 BattleScript_SelectingNotAllowedMoveAssaultVest[]; diff --git a/include/config/battle.h b/include/config/battle.h index b9d3926b05..e3975e7199 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -118,6 +118,7 @@ #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. +#define B_SNOW_WARNING GEN_9 // In Gen9+, Snowwarning will summon snow instead of hail. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. diff --git a/include/constants/battle.h b/include/constants/battle.h index f0130e58f2..7a4ede37a5 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -286,8 +286,11 @@ #define B_WEATHER_HAIL_PERMANENT (1 << 10) #define B_WEATHER_HAIL (B_WEATHER_HAIL_TEMPORARY | B_WEATHER_HAIL_PERMANENT) #define B_WEATHER_STRONG_WINDS (1 << 11) -#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS) +#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS | B_WEATHER_SNOW) #define B_WEATHER_PRIMAL_ANY (B_WEATHER_RAIN_PRIMAL | B_WEATHER_SUN_PRIMAL | B_WEATHER_STRONG_WINDS) +#define B_WEATHER_SNOW_TEMPORARY (1 << 12) +#define B_WEATHER_SNOW_PERMANENT (1 << 13) +#define B_WEATHER_SNOW (B_WEATHER_SNOW_TEMPORARY | B_WEATHER_SNOW_PERMANENT) // Battle Weather as enum #define ENUM_WEATHER_NONE 0 @@ -298,6 +301,7 @@ #define ENUM_WEATHER_SUN_PRIMAL 5 #define ENUM_WEATHER_RAIN_PRIMAL 6 #define ENUM_WEATHER_STRONG_WINDS 7 +#define ENUM_WEATHER_SNOW 8 // Move Effects #define MOVE_EFFECT_SLEEP 1 diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index 1316054cd1..81dbdad163 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -30,6 +30,7 @@ #define AI_WEATHER_RAIN 2 #define AI_WEATHER_SANDSTORM 3 #define AI_WEATHER_HAIL 4 +#define AI_WEATHER_SNOW 5 // get_how_powerful_move_is #define MOVE_POWER_OTHER 0 diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 46e229a319..4b772022e5 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -544,6 +544,7 @@ #define B_ANIM_SHELL_TRAP_SETUP 34 #define B_ANIM_ZMOVE_ACTIVATE 35 // Using Z Moves #define B_ANIM_AFFECTION_HANGED_ON 36 +#define B_ANIM_SNOW_CONTINUES 37 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 @@ -591,6 +592,7 @@ #define ANIM_WEATHER_RAIN 2 #define ANIM_WEATHER_SANDSTORM 3 #define ANIM_WEATHER_HAIL 4 +#define ANIM_WEATHER_SNOW 5 // mon pal blend #define ANIM_PAL_BG 0x1 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index d1562dec93..e67348bfc4 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -405,7 +405,8 @@ #define EFFECT_DIRE_CLAW 399 #define EFFECT_BARB_BARRAGE 400 #define EFFECT_REVIVAL_BLESSING 401 +#define EFFECT_SNOW 402 -#define NUM_BATTLE_MOVE_EFFECTS 402 +#define NUM_BATTLE_MOVE_EFFECTS 403 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index f4fd589db1..f9d55a8a9d 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -652,8 +652,15 @@ #define STRINGID_ITEMCUREDSPECIESSTATUS 650 #define STRINGID_ITEMRESTOREDSPECIESPP 651 #define STRINGID_THUNDERCAGETRAPPED 652 +#define STRINGID_STARTEDSNOW 653 +#define STRINGID_SNOWCONTINUES 654 +#define STRINGID_SNOWSTOPPED 655 +#define STRINGID_STARTEDSNOW 653 +#define STRINGID_SNOWCONTINUES 654 +#define STRINGID_SNOWSTOPPED 655 +#define STRINGID_SNOWWARNINGSNOW 656 -#define BATTLESTRINGS_COUNT 653 +#define BATTLESTRINGS_COUNT 657 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -719,12 +726,17 @@ #define B_MSG_STARTED_SANDSTORM 3 #define B_MSG_STARTED_SUNLIGHT 4 #define B_MSG_STARTED_HAIL 5 +#define B_MSG_STARTED_SNOW 6 // gRainContinuesStringIds #define B_MSG_RAIN_CONTINUES 0 #define B_MSG_DOWNPOUR_CONTINUES 1 #define B_MSG_RAIN_STOPPED 2 +// gSnowContinuesStringIds +#define B_MSG_SNOW_CONTINUES 0 +#define B_MSG_SNOW_STOPPED 1 + // gSandStormHailContinuesStringIds / gSandStormHailDmgStringIds/ gSandStormHailEndStringIds #define B_MSG_SANDSTORM 0 #define B_MSG_HAIL 1 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 6c60705d5a..3933c455d0 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1372,7 +1372,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_AURORA_VEIL: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) - || !(gBattleWeather & B_WEATHER_HAIL)) + || !(gBattleWeather & B_WEATHER_HAIL && gBattleWeather & B_WEATHER_SNOW)) score -= 10; break; case EFFECT_OHKO: @@ -1578,7 +1578,12 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 8; break; case EFFECT_HAIL: - if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) + if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) // should hail be discouraged if snow is up? + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + score -= 8; + break; + case EFFECT_SNOW: + if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) // should snow be discouraged if hail is up? || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; @@ -2800,6 +2805,13 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) RETURN_SCORE_PLUS(2); // our partner benefits from hail } break; + case EFFECT_SNOW: + if (IsBattlerAlive(battlerAtkPartner) + && ShouldSetSnow(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect)) + { + RETURN_SCORE_PLUS(2); // our partner benefits from snow + } + break; } // global move effect check @@ -3949,6 +3961,22 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; } break; + case EFFECT_SNOW: // any other reasons? + if (ShouldSetSnow(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + { + if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL)) + && ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL)) + score += 3; + + score++; + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) + score++; + if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) + || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) + || HasMoveEffect(battlerDef, EFFECT_MOONLIGHT)) + score += 2; + } + break; case EFFECT_RAIN_DANCE: if (ShouldSetRain(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { @@ -4932,6 +4960,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: case EFFECT_HAIL: + case EFFECT_SNOW: case EFFECT_GEOMANCY: case EFFECT_VICTORY_DANCE: case EFFECT_HIT_SET_ENTRY_HAZARD: @@ -5158,6 +5187,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: case EFFECT_HAIL: + case EFFECT_SNOW: case EFFECT_RAIN_DANCE: score -= 2; break; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index f23127a0c3..230bc255f1 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -331,6 +331,7 @@ static const u16 sEncouragedEncoreEffects[] = EFFECT_SPIT_UP, EFFECT_SWALLOW, EFFECT_HAIL, + EFFECT_SNOW, EFFECT_TORMENT, EFFECT_WILL_O_WISP, EFFECT_FOLLOW_ME, @@ -1504,7 +1505,7 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) // increased accuracy but don't always hit if ((AI_WeatherHasEffect() && (((gBattleWeather & B_WEATHER_RAIN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)) - || (((gBattleWeather & B_WEATHER_HAIL) && move == MOVE_BLIZZARD)))) + || (((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD)))) || (gBattleMoves[move].effect == EFFECT_VITAL_THROW) #if B_MINIMIZE_DMG_ACC >= GEN_6 || ((gStatuses3[battlerDef] & STATUS3_MINIMIZED) && (gBattleMoves[move].flags & FLAG_DMG_MINIMIZE)) @@ -1579,7 +1580,7 @@ bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect) { if (!AI_WeatherHasEffect()) return FALSE; - else if (gBattleWeather & B_WEATHER_HAIL) + else if (gBattleWeather & B_WEATHER_HAIL) // dont set hail if snow is up? return FALSE; if (ability == ABILITY_SNOW_CLOAK @@ -1649,6 +1650,26 @@ bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect) return FALSE; } +bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect) // any other reasons snow should be set? +{ + if (!AI_WeatherHasEffect()) + return FALSE; + else if (gBattleWeather & B_WEATHER_SNOW) // dont set snow if hail is up? + return FALSE; + + if (ability == ABILITY_SNOW_CLOAK + || ability == ABILITY_ICE_BODY + || ability == ABILITY_FORECAST + || ability == ABILITY_SLUSH_RUSH + || IS_BATTLER_OF_TYPE(battler, TYPE_ICE) + || HasMove(battler, MOVE_BLIZZARD) + || HasMoveEffect(battler, EFFECT_AURORA_VEIL) + || HasMoveEffect(battler, EFFECT_WEATHER_BALL)) + { + return TRUE; + } + return FALSE; +} void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score) { @@ -3110,7 +3131,7 @@ bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect) { case EFFECT_AURORA_VEIL: // Use only in Hail and only if AI doesn't already have Reflect, Light Screen or Aurora Veil itself active. - if (gBattleWeather & B_WEATHER_HAIL + if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && !(gSideStatuses[atkSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL))) return TRUE; break; @@ -3215,7 +3236,8 @@ bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove) && (gBattleMoves[partnerMove].effect == EFFECT_SUNNY_DAY || gBattleMoves[partnerMove].effect == EFFECT_RAIN_DANCE || gBattleMoves[partnerMove].effect == EFFECT_SANDSTORM - || gBattleMoves[partnerMove].effect == EFFECT_HAIL)) + || gBattleMoves[partnerMove].effect == EFFECT_HAIL + || gBattleMoves[partnerMove].effect == EFFECT_SNOW)) return TRUE; return FALSE; diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c index ff5f044464..e18dfc9027 100755 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -5601,6 +5601,8 @@ void AnimTask_GetWeather(u8 taskId) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SANDSTORM; else if (gWeatherMoveAnim & B_WEATHER_HAIL) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_HAIL; + else if (gWeatherMoveAnim & B_WEATHER_SNOW) + gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SNOW; DestroyAnimVisualTask(taskId); } diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index bc541f0a01..2cc6b45bf8 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -492,6 +492,7 @@ static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId) case B_ANIM_SUN_CONTINUES: case B_ANIM_SANDSTORM_CONTINUES: case B_ANIM_HAIL_CONTINUES: + case B_ANIM_SNOW_CONTINUES: case B_ANIM_SNATCH_MOVE: return TRUE; default: diff --git a/src/battle_main.c b/src/battle_main.c index 2faeb84f2c..43b9c4a77a 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4665,7 +4665,7 @@ u32 GetBattlerTotalSpeedStat(u8 battlerId) speed *= 2; else if (ability == ABILITY_SAND_RUSH && gBattleWeather & B_WEATHER_SANDSTORM) speed *= 2; - else if (ability == ABILITY_SLUSH_RUSH && gBattleWeather & B_WEATHER_HAIL) + else if (ability == ABILITY_SLUSH_RUSH && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) speed *= 2; } @@ -5603,7 +5603,7 @@ void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk) gBattleStruct->dynamicMoveType = TYPE_ROCK | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) gBattleStruct->dynamicMoveType = TYPE_FIRE | F_DYNAMIC_TYPE_2; - else if (gBattleWeather & B_WEATHER_HAIL) + else if (gBattleWeather & (B_WEATHER_HAIL |B_WEATHER_SNOW)) gBattleStruct->dynamicMoveType = TYPE_ICE | F_DYNAMIC_TYPE_2; else gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2; diff --git a/src/battle_message.c b/src/battle_message.c index 21f7098f81..b2fb17c34b 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -358,8 +358,11 @@ static const u8 sText_SunlightGotBright[] = _("The sunlight got bright!"); static const u8 sText_SunlightStrong[] = _("The sunlight is strong."); static const u8 sText_SunlightFaded[] = _("The sunlight faded."); static const u8 sText_StartedHail[] = _("It started to hail!"); +static const u8 sText_StartedSnow[] = _("It started to snow!"); static const u8 sText_HailContinues[] = _("Hail continues to fall."); +static const u8 sText_SnowContinues[] = _("Snow continues to fall."); static const u8 sText_HailStopped[] = _("The hail stopped."); +static const u8 sText_SnowStopped[] = _("The snow stopped."); static const u8 sText_FailedToSpitUp[] = _("But it failed to spit up\na thing!"); static const u8 sText_FailedToSwallow[] = _("But it failed to swallow\na thing!"); static const u8 sText_WindBecameHeatWave[] = _("The wind turned into a\nHEAT WAVE!"); @@ -608,6 +611,7 @@ static const u8 sText_AnticipationActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFI static const u8 sText_ForewarnActivates[] = _("{B_SCR_ACTIVE_ABILITY} alerted {B_SCR_ACTIVE_NAME_WITH_PREFIX}\nto the {B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1}!"); static const u8 sText_IceBodyHpGain[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY}\nhealed it a little bit!"); static const u8 sText_SnowWarningHail[] = _("It started to hail!"); +static const u8 sText_SnowWarningSnow[] = _("It started to snow!"); static const u8 sText_FriskActivates[] = _("{B_ATK_NAME_WITH_PREFIX} frisked {B_DEF_NAME_WITH_PREFIX} and\nfound its {B_LAST_ITEM}!"); static const u8 sText_UnnerveEnters[] = _("The opposing team is too nervous\nto eat Berries!"); static const u8 sText_HarvestBerry[] = _("{B_ATK_NAME_WITH_PREFIX} harvested\nits {B_LAST_ITEM}!"); @@ -1134,6 +1138,9 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_STARTEDHAIL - BATTLESTRINGS_TABLE_START] = sText_StartedHail, [STRINGID_HAILCONTINUES - BATTLESTRINGS_TABLE_START] = sText_HailContinues, [STRINGID_HAILSTOPPED - BATTLESTRINGS_TABLE_START] = sText_HailStopped, + [STRINGID_STARTEDSNOW - BATTLESTRINGS_TABLE_START] = sText_StartedSnow, + [STRINGID_SNOWCONTINUES -BATTLESTRINGS_TABLE_START] = sText_SnowContinues, + [STRINGID_SNOWSTOPPED - BATTLESTRINGS_TABLE_START] = sText_SnowStopped, [STRINGID_FAILEDTOSPITUP - BATTLESTRINGS_TABLE_START] = sText_FailedToSpitUp, [STRINGID_FAILEDTOSWALLOW - BATTLESTRINGS_TABLE_START] = sText_FailedToSwallow, [STRINGID_WINDBECAMEHEATWAVE - BATTLESTRINGS_TABLE_START] = sText_WindBecameHeatWave, @@ -1354,6 +1361,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_FOREWARNACTIVATES - BATTLESTRINGS_TABLE_START] = sText_ForewarnActivates, [STRINGID_ICEBODYHPGAIN - BATTLESTRINGS_TABLE_START] = sText_IceBodyHpGain, [STRINGID_SNOWWARNINGHAIL - BATTLESTRINGS_TABLE_START] = sText_SnowWarningHail, + [STRINGID_SNOWWARNINGSNOW - BATTLESTRINGS_TABLE_START] = sText_SnowWarningSnow, [STRINGID_FRISKACTIVATES - BATTLESTRINGS_TABLE_START] = sText_FriskActivates, [STRINGID_UNNERVEENTERS - BATTLESTRINGS_TABLE_START] = sText_UnnerveEnters, [STRINGID_HARVESTBERRY - BATTLESTRINGS_TABLE_START] = sText_HarvestBerry, @@ -1543,12 +1551,13 @@ const u16 gMoveWeatherChangeStringIds[] = [B_MSG_STARTED_SANDSTORM] = STRINGID_SANDSTORMBREWED, [B_MSG_STARTED_SUNLIGHT] = STRINGID_SUNLIGHTGOTBRIGHT, [B_MSG_STARTED_HAIL] = STRINGID_STARTEDHAIL, + [B_MSG_STARTED_SNOW] = STRINGID_STARTEDSNOW, }; const u16 gSandStormHailContinuesStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMRAGES, - [B_MSG_HAIL] = STRINGID_HAILCONTINUES + [B_MSG_HAIL] = STRINGID_HAILCONTINUES, }; const u16 gSandStormHailDmgStringIds[] = @@ -1560,7 +1569,7 @@ const u16 gSandStormHailDmgStringIds[] = const u16 gSandStormHailEndStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMSUBSIDED, - [B_MSG_HAIL] = STRINGID_HAILSTOPPED + [B_MSG_HAIL] = STRINGID_HAILSTOPPED, }; const u16 gRainContinuesStringIds[] = @@ -1570,6 +1579,12 @@ const u16 gRainContinuesStringIds[] = [B_MSG_RAIN_STOPPED] = STRINGID_RAINSTOPPED }; +const u16 gSnowContinuesStringIds[] = +{ + [B_MSG_SNOW_CONTINUES] = STRINGID_SNOWCONTINUES, + [B_MSG_SNOW_STOPPED] = STRINGID_SNOWSTOPPED, +}; + const u16 gProtectLikeUsedStringIds[] = { [B_MSG_PROTECTED_ITSELF] = STRINGID_PKMNPROTECTEDITSELF2, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index a561307483..f9edb21708 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1809,7 +1809,7 @@ static bool32 AccuracyCalcHelper(u16 move) return TRUE; } #if B_BLIZZARD_HAIL >= GEN_4 - else if ((gBattleWeather & B_WEATHER_HAIL) && move == MOVE_BLIZZARD) + else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD) { // thunder/hurricane ignore acc checks in rain unless target is holding utility umbrella JumpIfMoveFailed(7, move); @@ -1889,7 +1889,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM) calc = (calc * 80) / 100; // 1.2 sand veil loss - else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL) + else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) calc = (calc * 80) / 100; // 1.2 snow cloak loss else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) calc = (calc * 50) / 100; // 1.5 tangled feet loss @@ -5185,7 +5185,8 @@ static void Cmd_playanimation(void) else if (animId == B_ANIM_RAIN_CONTINUES || animId == B_ANIM_SUN_CONTINUES || animId == B_ANIM_SANDSTORM_CONTINUES - || animId == B_ANIM_HAIL_CONTINUES) + || animId == B_ANIM_HAIL_CONTINUES + || animId == B_ANIM_SNOW_CONTINUES) { BtlController_EmitBattleAnimation(BUFFER_A, animId, *argPtr); MarkBattlerForControllerExec(gActiveBattler); @@ -5234,7 +5235,8 @@ static void Cmd_playanimation_var(void) else if (*animIdPtr == B_ANIM_RAIN_CONTINUES || *animIdPtr == B_ANIM_SUN_CONTINUES || *animIdPtr == B_ANIM_SANDSTORM_CONTINUES - || *animIdPtr == B_ANIM_HAIL_CONTINUES) + || *animIdPtr == B_ANIM_HAIL_CONTINUES + || *animIdPtr == B_ANIM_SNOW_CONTINUES) { BtlController_EmitBattleAnimation(BUFFER_A, *animIdPtr, *argPtr); MarkBattlerForControllerExec(gActiveBattler); @@ -10300,7 +10302,7 @@ static void Cmd_various(void) { VARIOUS_ARGS(); if (gSideStatuses[GET_BATTLER_SIDE(gActiveBattler)] & SIDE_STATUS_AURORA_VEIL - || !(WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL)) + || !(WEATHER_HAS_EFFECT && gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) { gMoveResultFlags |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = 0; @@ -16620,3 +16622,20 @@ void BS_ItemRestorePP(void) { PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(mon, MON_DATA_SPECIES)); gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_SetSnow(void) +{ + NATIVE_ARGS(); + + if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SNOW, FALSE)) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; + + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW; + } + gBattlescriptCurrInstr = cmd->nextInstr; +} \ No newline at end of file diff --git a/src/battle_tv.c b/src/battle_tv.c index 10c2144ccd..0ed75c1768 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -251,6 +251,7 @@ static const u16 sPoints_MoveEffect[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SWALLOW] = 3, // [EFFECT_UNUSED_A3] = 1, [EFFECT_HAIL] = 4, + [EFFECT_SNOW] = 4, [EFFECT_TORMENT] = 7, [EFFECT_FLATTER] = 7, [EFFECT_WILL_O_WISP] = 5, @@ -1769,6 +1770,6 @@ static void AddPointsBasedOnWeather(u16 weatherFlags, u16 moveId, u8 moveSlot) AddMovePoints(PTS_SUN, moveId, moveSlot, 0); else if (weatherFlags & B_WEATHER_SANDSTORM) AddMovePoints(PTS_SANDSTORM, moveId, moveSlot, 0); - else if (weatherFlags & B_WEATHER_HAIL) + else if (weatherFlags & (B_WEATHER_HAIL | B_WEATHER_SNOW)) AddMovePoints(PTS_HAIL, moveId, moveSlot, 0); } diff --git a/src/battle_util.c b/src/battle_util.c index b89b9976ee..594d412110 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2032,6 +2032,7 @@ enum ENDTURN_SANDSTORM, ENDTURN_SUN, ENDTURN_HAIL, + ENDTURN_SNOW, ENDTURN_GRAVITY, ENDTURN_WATER_SPORT, ENDTURN_MUD_SPORT, @@ -2394,6 +2395,27 @@ u8 DoFieldEndTurnEffects(void) } gBattleStruct->turnCountersTracker++; break; + case ENDTURN_SNOW: + if (gBattleWeather & B_WEATHER_SNOW) + { + if (!(gBattleWeather & B_WEATHER_SNOW_PERMANENT)) + { + if (--gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather &= ~B_WEATHER_SNOW_TEMPORARY; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW_STOPPED; + } + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW_CONTINUES; + } + + BattleScriptExecute(BattleScript_SnowContinuesOrEnds); + effect++; + } + gBattleStruct->turnCountersTracker++; + break; case ENDTURN_TRICK_ROOM: if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && --gFieldTimers.trickRoomTimer == 0) { @@ -3995,7 +4017,7 @@ u8 TryWeatherFormChange(u8 battler) } } #endif - else if (holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || (!(gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_HAIL)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))) + else if (holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || (!(gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_SNOW)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); ret = CASTFORM_NORMAL + 1; @@ -4010,7 +4032,7 @@ u8 TryWeatherFormChange(u8 battler) SET_BATTLER_TYPE(battler, TYPE_WATER); ret = CASTFORM_WATER + 1; } - else if (gBattleWeather & B_WEATHER_HAIL && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) + else if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW) && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) { SET_BATTLER_TYPE(battler, TYPE_ICE); ret = CASTFORM_ICE + 1; @@ -4046,6 +4068,7 @@ static const u16 sWeatherFlagsInfo[][3] = [ENUM_WEATHER_SANDSTORM] = {B_WEATHER_SANDSTORM_TEMPORARY, B_WEATHER_SANDSTORM_PERMANENT, HOLD_EFFECT_SMOOTH_ROCK}, [ENUM_WEATHER_HAIL] = {B_WEATHER_HAIL_TEMPORARY, B_WEATHER_HAIL_PERMANENT, HOLD_EFFECT_ICY_ROCK}, [ENUM_WEATHER_STRONG_WINDS] = {B_WEATHER_STRONG_WINDS, B_WEATHER_STRONG_WINDS, HOLD_EFFECT_NONE}, + [ENUM_WEATHER_SNOW] = {B_WEATHER_SNOW_TEMPORARY, B_WEATHER_SNOW_PERMANENT, HOLD_EFFECT_ICY_ROCK}, }; static void ShouldChangeFormInWeather(u8 battler) @@ -4066,7 +4089,6 @@ static void ShouldChangeFormInWeather(u8 battler) bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility) { u16 battlerAbility = GetBattlerAbility(battler); - if (gBattleWeather & B_WEATHER_PRIMAL_ANY && battlerAbility != ABILITY_DESOLATE_LAND && battlerAbility != ABILITY_PRIMORDIAL_SEA @@ -4092,7 +4114,6 @@ bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility) ShouldChangeFormInWeather(battler); return TRUE; } - return FALSE; } @@ -4642,11 +4663,19 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITY_SNOW_WARNING: - if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE)) + #if B_SNOW_WARNING >= GEN_9 + if (TryChangeBattleWeather(battler, ENUM_WEATHER_SNOW, TRUE)) { - BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivates); + BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow); effect++; } + #else + if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE)) + { + BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail); + effect++; + } + #endif else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; @@ -6132,7 +6161,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITY_ICE_FACE: - if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL) + if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL | B_WEATHER_SNOW) && gBattleMons[battler].species == SPECIES_EISCUE_NOICE_FACE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)]) @@ -9087,7 +9116,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_SOLAR_BEAM: - if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN))) + if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN | B_WEATHER_SNOW))) MulModifier(&modifier, UQ_4_12(0.5)); break; case EFFECT_STOMPING_TANTRUM: @@ -9495,6 +9524,12 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, // sandstorm sp.def boost for rock types if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); + // snow def boost for ice types + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. // Having the 5th badge boosts physical defense while having the 7th badge boosts special defense. diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 4a89222a96..5cb428d9a3 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13330,7 +13330,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_SNOWSCAPE] = { - .effect = EFFECT_HAIL, + .effect = EFFECT_SNOW, .power = 0, .type = TYPE_ICE, .accuracy = 0, From 62ec4989f24644da1296b46017f7975780b89716 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Sun, 30 Apr 2023 02:35:09 +0200 Subject: [PATCH 42/88] spelling --- include/config/battle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index e3975e7199..0605ad43e5 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -118,7 +118,7 @@ #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. -#define B_SNOW_WARNING GEN_9 // In Gen9+, Snowwarning will summon snow instead of hail. +#define B_SNOW_WARNING GEN_9 // In Gen9+, Snow Warning will summon snow instead of hail. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. From acf4529d8b48587a7033e7cf71251ec8eb97ff06 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 30 Apr 2023 11:25:48 +0200 Subject: [PATCH 43/88] Allow Egg Move transfer for non-eggs --- include/config/pokemon.h | 2 +- include/pokemon.h | 1 + src/daycare.c | 50 ++++++++++++++++++++++++++++++++++++++++ src/pokemon.c | 3 +-- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 6247217465..d6e239515e 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -16,7 +16,7 @@ #define P_MOTHER_EGG_MOVE_INHERITANCE GEN_LATEST // Since Gen 6, the mother can also pass down Egg Moves. #define P_NATURE_INHERITANCE GEN_LATEST // In Gen 3, Everstone grants Ditto and mothers a 50% chance to pass on Nature. Since Gen 4, anyone can pass on nature. Since Gen 5, the chance is 100%. #define P_ABILITY_INHERITANCE GEN_LATEST // TODO: In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted. -#define P_EGG_MOVE_TRANSFER GEN_LATEST // TODO: Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. +#define P_EGG_MOVE_TRANSFER GEN_LATEST // Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. In Gen 9, if a Pokémon holds a Mirror Herb, it will receive Egg Moves from the other regardless of species. // Species-specific settings #define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball. diff --git a/include/pokemon.h b/include/pokemon.h index 581677cae7..2394861060 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -442,6 +442,7 @@ void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest); u8 GetLevelFromMonExp(struct Pokemon *mon); u8 GetLevelFromBoxMonExp(struct BoxPokemon *boxMon); u16 GiveMoveToMon(struct Pokemon *mon, u16 move); +u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move); u16 GiveMoveToBattleMon(struct BattlePokemon *mon, u16 move); void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot); void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot); diff --git a/src/daycare.c b/src/daycare.c index 3feabe9ba9..a058ce9052 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -34,6 +34,7 @@ static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare * static u8 GetDaycareCompatibilityScore(struct DayCare *daycare); static void DaycarePrintMonInfo(u8 windowId, u32 daycareSlotId, u8 y); static u8 ModifyBreedingScoreForOvalCharm(u8 score); +static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves); // RAM buffers used to assist with BuildEggMoveset() EWRAM_DATA static u16 sHatchedEggLevelUpMoves[EGG_LVL_UP_MOVES_ARRAY_COUNT] = {0}; @@ -164,6 +165,51 @@ static s8 Daycare_FindEmptySpot(struct DayCare *daycare) return -1; } +static void TransferEggMoves(void) +{ + u32 i, j, k, l; + u16 numEggMoves; + struct Pokemon mon; + + for (i = 0; i < DAYCARE_MON_COUNT; i++) + { + if (!GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SANITY_HAS_SPECIES)) + continue; + + // Get list of egg moves for mon + BoxMonToMon(&gSaveBlock1Ptr->daycare.mons[i].mon, &mon); + for (j = 0; j < EGG_MOVES_ARRAY_COUNT; j++) + sHatchedEggEggMoves[j] = MOVE_NONE; + numEggMoves = GetEggMoves(&mon, sHatchedEggEggMoves); + for (j = 0; j < numEggMoves; j++) + { + // Go through other Daycare mons + for (k = 0; k < DAYCARE_MON_COUNT; k++) + { + if (k == i || !GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SANITY_HAS_SPECIES)) + continue; + + // Check if you can inherit from them + if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SPECIES) != GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SPECIES) + #if P_EGG_MOVE_TRANSFER >= GEN_9 + && GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_HELD_ITEM) != ITEM_MIRROR_HERB + #endif + ) + continue; + + for (l = 0; l < MAX_MON_MOVES; l++) + { + if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_MOVE1 + l) != sHatchedEggEggMoves[j]) + continue; + + if (GiveMoveToBoxMon(&gSaveBlock1Ptr->daycare.mons[i].mon, sHatchedEggEggMoves[j]) == MON_HAS_MAX_MOVES) + break; + } + } + } + } +} + static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycareMon) { if (MonHasMail(mon)) @@ -186,6 +232,10 @@ static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycar ZeroMonData(mon); CompactPartySlots(); CalculatePlayerPartyCount(); + +#if P_EGG_MOVE_TRANSFER >= GEN_8 + TransferEggMoves(); +#endif } static void StorePokemonInEmptyDaycareSlot(struct Pokemon *mon, struct DayCare *daycare) diff --git a/src/pokemon.c b/src/pokemon.c index e82cb98c30..5b83e97488 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -63,7 +63,6 @@ static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 perso static void EncryptBoxMon(struct BoxPokemon *boxMon); static void DecryptBoxMon(struct BoxPokemon *boxMon); static void Task_PlayMapChosenOrBattleBGM(u8 taskId); -static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move); static bool8 ShouldSkipFriendshipChange(void); static void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv); void TrySpecialOverworldEvo(); @@ -4194,7 +4193,7 @@ u16 GiveMoveToMon(struct Pokemon *mon, u16 move) return GiveMoveToBoxMon(&mon->box, move); } -static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move) +u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move) { s32 i; for (i = 0; i < MAX_MON_MOVES; i++) From 21fa91b80b76b8e4b67b8ea0df45c94bc7d078de Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 30 Apr 2023 11:45:24 +0200 Subject: [PATCH 44/88] Add ClearHatchedEggMoves per SBird's suggestion Co-Authored-By: Philipp AUER --- src/daycare.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/daycare.c b/src/daycare.c index a058ce9052..2997578d5f 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -165,6 +165,14 @@ static s8 Daycare_FindEmptySpot(struct DayCare *daycare) return -1; } +static void ClearHatchedEggMoves(void) +{ + u16 i; + + for (i = 0; i < EGG_MOVES_ARRAY_COUNT; i++) + sHatchedEggEggMoves[i] = MOVE_NONE; +} + static void TransferEggMoves(void) { u32 i, j, k, l; @@ -176,10 +184,8 @@ static void TransferEggMoves(void) if (!GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SANITY_HAS_SPECIES)) continue; - // Get list of egg moves for mon BoxMonToMon(&gSaveBlock1Ptr->daycare.mons[i].mon, &mon); - for (j = 0; j < EGG_MOVES_ARRAY_COUNT; j++) - sHatchedEggEggMoves[j] = MOVE_NONE; + ClearHatchedEggMoves(); numEggMoves = GetEggMoves(&mon, sHatchedEggEggMoves); for (j = 0; j < numEggMoves; j++) { @@ -753,8 +759,7 @@ static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, stru sHatchedEggFatherMoves[i] = MOVE_NONE; sHatchedEggFinalMoves[i] = MOVE_NONE; } - for (i = 0; i < EGG_MOVES_ARRAY_COUNT; i++) - sHatchedEggEggMoves[i] = MOVE_NONE; + ClearHatchedEggMoves(); for (i = 0; i < EGG_LVL_UP_MOVES_ARRAY_COUNT; i++) sHatchedEggLevelUpMoves[i] = MOVE_NONE; From 8f75844ece7546df6b06db53302685f458c1f8b9 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 30 Apr 2023 12:58:18 +0200 Subject: [PATCH 45/88] Add Ability inheritance --- include/config/pokemon.h | 2 +- src/daycare.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index d6e239515e..a65eddb73c 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -15,7 +15,7 @@ #define P_TM_INHERITANCE GEN_LATEST // Since Gen 6, the father no longer passes down TMs to the baby. #define P_MOTHER_EGG_MOVE_INHERITANCE GEN_LATEST // Since Gen 6, the mother can also pass down Egg Moves. #define P_NATURE_INHERITANCE GEN_LATEST // In Gen 3, Everstone grants Ditto and mothers a 50% chance to pass on Nature. Since Gen 4, anyone can pass on nature. Since Gen 5, the chance is 100%. -#define P_ABILITY_INHERITANCE GEN_LATEST // TODO: In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted. +#define P_ABILITY_INHERITANCE GEN_LATEST // In B2W2, a female Pokémon has an 80% chance of passing down their ability if bred with a male. Since Gen 6, the chance is 80% for normal ability and 60% for Hidden Ability, and anyone can pass down their abilities if bred with Ditto. NOTE: BW's effect: 60% chance to pass down HA and random for normal ability has been omitted. #define P_EGG_MOVE_TRANSFER GEN_LATEST // Starting in Gen 8, if two Pokémon of the same species are together in the Daycare, one knows an Egg Move, and the other has an empty slot, the other Pokémon will receive the Egg Move in the empty slot. In Gen 9, if a Pokémon holds a Mirror Herb, it will receive Egg Moves from the other regardless of species. // Species-specific settings diff --git a/src/daycare.c b/src/daycare.c index 2997578d5f..08b9ec23fc 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -712,6 +712,35 @@ static void InheritPokeball(struct Pokemon *egg, struct BoxPokemon *father, stru SetMonData(egg, MON_DATA_POKEBALL, &inheritBall); } +static void InheritAbility(struct Pokemon *egg, struct BoxPokemon *father, struct BoxPokemon *mother) +{ + u8 fatherAbility = GetBoxMonData(father, MON_DATA_ABILITY_NUM); + u8 motherAbility = GetBoxMonData(mother, MON_DATA_ABILITY_NUM); + u8 motherSpecies = GetBoxMonData(mother, MON_DATA_SPECIES); + u8 inheritAbility = motherAbility; + + if (motherSpecies == SPECIES_DITTO) + #if P_ABILITY_INHERITANCE < GEN_6 + return; + #else + inheritAbility = fatherAbility; + #endif + + if (inheritAbility < 2 && (Random() % 10 < 8)) + { + SetMonData(egg, MON_DATA_ABILITY_NUM, &inheritAbility); + } +#if P_ABILITY_INHERITANCE < GEN_6 + else if (Random() % 10 < 8) +#else + else if (Random() % 10 < 6) +#endif + { + // Hidden Abilities have a different chance of being passed down + SetMonData(egg, MON_DATA_ABILITY_NUM, &inheritAbility); + } +} + // Counts the number of egg moves a pokemon learns and stores the moves in // the given array. static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves) @@ -1000,6 +1029,9 @@ static void _GiveEggFromDaycare(struct DayCare *daycare) InheritIVs(&egg, daycare); InheritPokeball(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); BuildEggMoveset(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); +#if P_ABILITY_INHERITANCE >= GEN_8 + InheritAbility(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); +#endif GiveMoveIfItem(&egg, daycare); From dc368727ea458463b74aef280a21069e1e442838 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Sun, 30 Apr 2023 15:11:35 +0200 Subject: [PATCH 46/88] remove stupidity for some reason I added the 50% def increase in snow check twice? this removes that --- src/battle_util.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 594d412110..45208c0baf 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9528,9 +9528,6 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) - MulModifier(&modifier, UQ_4_12(1.5)); - // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. // Having the 5th badge boosts physical defense while having the 7th badge boosts special defense. if (ShouldGetStatBadgeBoost(FLAG_BADGE05_GET, battlerDef) && IS_MOVE_PHYSICAL(move)) From 47cd903fdecc3d2636a30479fa698de720a5650d Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sun, 30 Apr 2023 18:02:41 +0200 Subject: [PATCH 47/88] update dire hit failing comment --- test/item_effect_set_focus_energy.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/item_effect_set_focus_energy.c b/test/item_effect_set_focus_energy.c index 72e90d610b..19093e739a 100644 --- a/test/item_effect_set_focus_energy.c +++ b/test/item_effect_set_focus_energy.c @@ -3,7 +3,10 @@ SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages") { - KNOWN_FAILING; // Displays Message "Foe Wobbuffet used Dire Hit.." when facing off against Wobbuffet. Doesnt happen against other species? + /* KNOWN_FAILING due to message mismatch for "Wobbuffet used Dire Hit to get pumped!" + * As of the time of writing, this test passes if the above mentioned message is removed + * For more details refer to description of PR #2940 */ + KNOWN_FAILING; ASSUME(B_CRIT_CHANCE >= GEN_7); PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); GIVEN { From f1d60a4a04b13882c91ef2b5a0496119675e246b Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sun, 30 Apr 2023 22:52:37 +0200 Subject: [PATCH 48/88] Fixed wrong generation in check --- src/daycare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daycare.c b/src/daycare.c index 08b9ec23fc..31a7bade05 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -1029,7 +1029,7 @@ static void _GiveEggFromDaycare(struct DayCare *daycare) InheritIVs(&egg, daycare); InheritPokeball(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); BuildEggMoveset(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); -#if P_ABILITY_INHERITANCE >= GEN_8 +#if P_ABILITY_INHERITANCE >= GEN_6 InheritAbility(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); #endif From 772c50408cf93957f545855c8ca9d8a660f64f84 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sun, 30 Apr 2023 23:22:51 +0200 Subject: [PATCH 49/88] remove identifier strings no longer needed since testing is done. --- test/item_effect_cure_status.c | 34 ++++++++++++++++---------------- test/item_effect_increase_stat.c | 22 ++++++++++----------- test/item_effect_restore_hp.c | 26 ++++++++++++------------ test/item_effect_restore_pp.c | 8 ++++---- test/item_effect_revive.c | 8 ++++---- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/test/item_effect_cure_status.c b/test/item_effect_cure_status.c index 949f60f0e4..1f7c9d7a23 100644 --- a/test/item_effect_cure_status.c +++ b/test/item_effect_cure_status.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Cure Status: Paralyze Heal heals a battler from being paralyzed") +SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed") { GIVEN { ASSUME(gItems[ITEM_PARALYZE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -16,7 +16,7 @@ SINGLE_BATTLE_TEST("Cure Status: Paralyze Heal heals a battler from being paraly } } -SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being poisoned") +SINGLE_BATTLE_TEST("Antidote heals a battler from being poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -31,7 +31,7 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being poisoned") } } -SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being badly poisoned") +SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned") { GIVEN { ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -46,7 +46,7 @@ SINGLE_BATTLE_TEST("Cure Status: Antidote heals a battler from being badly poiso } } -SINGLE_BATTLE_TEST("Cure Status: Awakening heals a battler from being asleep") +SINGLE_BATTLE_TEST("Awakening heals a battler from being asleep") { GIVEN { ASSUME(gItems[ITEM_AWAKENING].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -61,7 +61,7 @@ SINGLE_BATTLE_TEST("Cure Status: Awakening heals a battler from being asleep") } } -SINGLE_BATTLE_TEST("Cure Status: Burn Heal heals a battler from being burned") +SINGLE_BATTLE_TEST("Burn Heal heals a battler from being burned") { GIVEN { ASSUME(gItems[ITEM_BURN_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -76,7 +76,7 @@ SINGLE_BATTLE_TEST("Cure Status: Burn Heal heals a battler from being burned") } } -SINGLE_BATTLE_TEST("Cure Status: Ice Heal heals a battler from being paralyzed") +SINGLE_BATTLE_TEST("Ice Heal heals a battler from being paralyzed") { GIVEN { ASSUME(gItems[ITEM_ICE_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); @@ -91,7 +91,7 @@ SINGLE_BATTLE_TEST("Cure Status: Ice Heal heals a battler from being paralyzed") } } -SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary status") +SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -113,7 +113,7 @@ SINGLE_BATTLE_TEST("Cure Status: Full Heal heals a battler from any primary stat } } -SINGLE_BATTLE_TEST("Cure Status: Heal Powder heals a battler from any primary status") +SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -135,7 +135,7 @@ SINGLE_BATTLE_TEST("Cure Status: Heal Powder heals a battler from any primary st } } -SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any primary status") +SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -157,7 +157,7 @@ SINGLE_BATTLE_TEST("Cure Status: Pewter Crunchies heals a battler from any prima } } -SINGLE_BATTLE_TEST("Cure Status: Lava Cookies heals a battler from any primary status") +SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -179,7 +179,7 @@ SINGLE_BATTLE_TEST("Cure Status: Lava Cookies heals a battler from any primary s } } -SINGLE_BATTLE_TEST("Cure Status: Rage Candy Bar heals a battler from any primary status") +SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -201,7 +201,7 @@ SINGLE_BATTLE_TEST("Cure Status: Rage Candy Bar heals a battler from any primary } } -SINGLE_BATTLE_TEST("Cure Status: Old Gateu heals a battler from any primary status") +SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -223,7 +223,7 @@ SINGLE_BATTLE_TEST("Cure Status: Old Gateu heals a battler from any primary stat } } -SINGLE_BATTLE_TEST("Cure Status: Casteliacone heals a battler from any primary status") +SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -245,7 +245,7 @@ SINGLE_BATTLE_TEST("Cure Status: Casteliacone heals a battler from any primary s } } -SINGLE_BATTLE_TEST("Cure Status: Lumiose Galette heals a battler from any primary status") +SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -267,7 +267,7 @@ SINGLE_BATTLE_TEST("Cure Status: Lumiose Galette heals a battler from any primar } } -SINGLE_BATTLE_TEST("Cure Status: Shalour Sable heals a battler from any primary status") +SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -289,7 +289,7 @@ SINGLE_BATTLE_TEST("Cure Status: Shalour Sable heals a battler from any primary } } -SINGLE_BATTLE_TEST("Cure Status: Big Malasada heals a battler from any primary status") +SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status") { u16 status; PARAMETRIZE{ status = STATUS1_BURN; } @@ -311,7 +311,7 @@ SINGLE_BATTLE_TEST("Cure Status: Big Malasada heals a battler from any primary s } } -SINGLE_BATTLE_TEST("Cure Status: Full Heal, Heal Powder and Local Specialties heal a battler from being confused") +SINGLE_BATTLE_TEST("Full Heal, Heal Powder and Local Specialties heal a battler from being confused") { u16 item; PARAMETRIZE { item = ITEM_FULL_HEAL; } diff --git a/test/item_effect_increase_stat.c b/test/item_effect_increase_stat.c index bb5989207a..bbaf20e2fa 100644 --- a/test/item_effect_increase_stat.c +++ b/test/item_effect_increase_stat.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Increase Stat: X Attack sharply raises battler's Attack stat", s16 damage) +SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("Increase Stat: X Attack sharply raises battler's Attack stat } } -SINGLE_BATTLE_TEST("Increase Stat: X Defense sharply raises battler's Defense stat", s16 damage) +SINGLE_BATTLE_TEST("X Defense sharply raises battler's Defense stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -47,7 +47,7 @@ SINGLE_BATTLE_TEST("Increase Stat: X Defense sharply raises battler's Defense st } } -SINGLE_BATTLE_TEST("Increase Stat: X Sp. Atk sharply raises battler's Sp. Attack stat", s16 damage) +SINGLE_BATTLE_TEST("X Sp. Atk sharply raises battler's Sp. Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -70,7 +70,7 @@ SINGLE_BATTLE_TEST("Increase Stat: X Sp. Atk sharply raises battler's Sp. Attack } } -SINGLE_BATTLE_TEST("Increase Stat: X Sp. Def sharply raises battler's Sp. Defense stat", s16 damage) +SINGLE_BATTLE_TEST("X Sp. Def sharply raises battler's Sp. Defense stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -93,7 +93,7 @@ SINGLE_BATTLE_TEST("Increase Stat: X Sp. Def sharply raises battler's Sp. Defens } } -SINGLE_BATTLE_TEST("Increase Stat: X Speed sharply raises battler's Speed stat", s16 damage) +SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -126,7 +126,7 @@ SINGLE_BATTLE_TEST("Increase Stat: X Speed sharply raises battler's Speed stat", } } -SINGLE_BATTLE_TEST("Increase Stat: X Accuracy sharply raises battler's Accuracy stat", s16 damage) +SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat", s16 damage) { ASSUME(gBattleMoves[MOVE_SING].accuracy == 55); @@ -147,7 +147,7 @@ SINGLE_BATTLE_TEST("Increase Stat: X Accuracy sharply raises battler's Accuracy } } -SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Attack stat", s16 damage) +SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -167,7 +167,7 @@ SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Attack stat", } } -SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Defense stat", s16 damage) +SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Defense stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -187,7 +187,7 @@ SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Defense stat", } } -SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Sp. Attack stat", s16 damage) +SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Sp. Attack stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -207,7 +207,7 @@ SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Sp. Attack sta } } -SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms battler's Sp. Defense stat", s16 damage) +SINGLE_BATTLE_TEST("Max Mushrooms battler's Sp. Defense stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } @@ -227,7 +227,7 @@ SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms battler's Sp. Defense stat", s1 } } -SINGLE_BATTLE_TEST("Increase Stat: Max Mushrooms raises battler's Speed stat", s16 damage) +SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Speed stat", s16 damage) { u16 useItem; PARAMETRIZE { useItem = FALSE; } diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index 4572a0d8be..4662e9f25c 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -4,7 +4,7 @@ #define TEST_HP 1 #define MAX_HP 400 -SINGLE_BATTLE_TEST("Restore HP: Potion restores a battler's HP by 20") +SINGLE_BATTLE_TEST("Potion restores a battler's HP by 20") { GIVEN { ASSUME(gItems[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -17,7 +17,7 @@ SINGLE_BATTLE_TEST("Restore HP: Potion restores a battler's HP by 20") } } -SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 60") +SINGLE_BATTLE_TEST("Super Potion restores a battler's HP by 60") { GIVEN { ASSUME(gItems[ITEM_SUPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -30,7 +30,7 @@ SINGLE_BATTLE_TEST("Restore HP: Super Potion restores a battler's HP by 60") } } -SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 120") +SINGLE_BATTLE_TEST("Hyper Potion restores a battler's HP by 120") { GIVEN { ASSUME(gItems[ITEM_HYPER_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -43,7 +43,7 @@ SINGLE_BATTLE_TEST("Restore HP: Hyper Potion restores a battler's HP by 120") } } -SINGLE_BATTLE_TEST("Restore HP: Max Potion restores a battler's HP fully") +SINGLE_BATTLE_TEST("Max Potion restores a battler's HP fully") { GIVEN { ASSUME(gItems[ITEM_MAX_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Restore HP: Max Potion restores a battler's HP fully") } } -SINGLE_BATTLE_TEST("Restore HP: Fresh Water restores a battler's HP by 30") +SINGLE_BATTLE_TEST("Fresh Water restores a battler's HP by 30") { GIVEN { ASSUME(gItems[ITEM_FRESH_WATER].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -69,7 +69,7 @@ SINGLE_BATTLE_TEST("Restore HP: Fresh Water restores a battler's HP by 30") } } -SINGLE_BATTLE_TEST("Restore HP: Soda Pop restores a battler's HP by 50") +SINGLE_BATTLE_TEST("Soda Pop restores a battler's HP by 50") { GIVEN { ASSUME(gItems[ITEM_SODA_POP].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -82,7 +82,7 @@ SINGLE_BATTLE_TEST("Restore HP: Soda Pop restores a battler's HP by 50") } } -SINGLE_BATTLE_TEST("Restore HP: Lemonade restores a battler's HP by 70") +SINGLE_BATTLE_TEST("Lemonade restores a battler's HP by 70") { GIVEN { ASSUME(gItems[ITEM_LEMONADE].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -95,7 +95,7 @@ SINGLE_BATTLE_TEST("Restore HP: Lemonade restores a battler's HP by 70") } } -SINGLE_BATTLE_TEST("Restore HP: Moomoo Milk restores a battler's HP by 100") +SINGLE_BATTLE_TEST("Moomoo Milk restores a battler's HP by 100") { GIVEN { ASSUME(gItems[ITEM_MOOMOO_MILK].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -108,7 +108,7 @@ SINGLE_BATTLE_TEST("Restore HP: Moomoo Milk restores a battler's HP by 100") } } -SINGLE_BATTLE_TEST("Restore HP: Energy Powder restores a battler's HP by 60(50)") +SINGLE_BATTLE_TEST("Energy Powder restores a battler's HP by 60(50)") { GIVEN { ASSUME(gItems[ITEM_ENERGY_POWDER].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -124,7 +124,7 @@ SINGLE_BATTLE_TEST("Restore HP: Energy Powder restores a battler's HP by 60(50)" } } -SINGLE_BATTLE_TEST("Restore HP: Energy Root restores a battler's HP by 120(200)") +SINGLE_BATTLE_TEST("Energy Root restores a battler's HP by 120(200)") { GIVEN { ASSUME(gItems[ITEM_ENERGY_ROOT].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -140,7 +140,7 @@ SINGLE_BATTLE_TEST("Restore HP: Energy Root restores a battler's HP by 120(200)" } } -SINGLE_BATTLE_TEST("Restore HP: Sweet Heart restores a battler's HP by 20") +SINGLE_BATTLE_TEST("Sweet Heart restores a battler's HP by 20") { GIVEN { ASSUME(gItems[ITEM_SWEET_HEART].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -153,7 +153,7 @@ SINGLE_BATTLE_TEST("Restore HP: Sweet Heart restores a battler's HP by 20") } } -SINGLE_BATTLE_TEST("Restore HP: Oran Berry restores a battler's HP by 10") +SINGLE_BATTLE_TEST("Oran Berry restores a battler's HP by 10") { GIVEN { ASSUME(gItems[ITEM_ORAN_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); @@ -166,7 +166,7 @@ SINGLE_BATTLE_TEST("Restore HP: Oran Berry restores a battler's HP by 10") } } -SINGLE_BATTLE_TEST("Restore HP: Sitrus Berry restores a battler's HP by 25% of its max HP(30HP flat)") +SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP by 25% of its max HP(30HP flat)") { GIVEN { ASSUME(gItems[ITEM_SITRUS_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP); diff --git a/test/item_effect_restore_pp.c b/test/item_effect_restore_pp.c index f21593566f..7194efbbb5 100644 --- a/test/item_effect_restore_pp.c +++ b/test/item_effect_restore_pp.c @@ -1,7 +1,7 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Restore PP: Ether restores the PP of one of a battler's moves by 10 ") +SINGLE_BATTLE_TEST("Ether restores the PP of one of a battler's moves by 10 ") { GIVEN { ASSUME(gItems[ITEM_ETHER].battleUsage == EFFECT_ITEM_RESTORE_PP); @@ -16,7 +16,7 @@ SINGLE_BATTLE_TEST("Restore PP: Ether restores the PP of one of a battler's move } } -SINGLE_BATTLE_TEST("Restore PP: Max Ether restores the PP of one of a battler's moves fully") +SINGLE_BATTLE_TEST("Max Ether restores the PP of one of a battler's moves fully") { GIVEN { ASSUME(gItems[ITEM_MAX_ETHER].battleUsage == EFFECT_ITEM_RESTORE_PP); @@ -31,7 +31,7 @@ SINGLE_BATTLE_TEST("Restore PP: Max Ether restores the PP of one of a battler's } } -SINGLE_BATTLE_TEST("Restore PP: Elixir restores the PP of all of a battler's moves by 10") +SINGLE_BATTLE_TEST("Elixir restores the PP of all of a battler's moves by 10") { GIVEN { ASSUME(gItems[ITEM_ELIXIR].battleUsage == EFFECT_ITEM_RESTORE_PP); @@ -48,7 +48,7 @@ SINGLE_BATTLE_TEST("Restore PP: Elixir restores the PP of all of a battler's mov } } -SINGLE_BATTLE_TEST("Restore PP: Max Elixir restores the PP of all of a battler's moves fully") +SINGLE_BATTLE_TEST("Max Elixir restores the PP of all of a battler's moves fully") { GIVEN { ASSUME(gItems[ITEM_MAX_ELIXIR].battleUsage == EFFECT_ITEM_RESTORE_PP); diff --git a/test/item_effect_revive.c b/test/item_effect_revive.c index 3c6995176e..77423fff1b 100644 --- a/test/item_effect_revive.c +++ b/test/item_effect_revive.c @@ -3,7 +3,7 @@ #define MAX_HP 200 -SINGLE_BATTLE_TEST("Revive: Revive restores a fainted battler's HP to half") +SINGLE_BATTLE_TEST("Revive restores a fainted battler's HP to half") { GIVEN { ASSUME(gItems[ITEM_REVIVE].battleUsage == EFFECT_ITEM_REVIVE); @@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("Revive: Revive restores a fainted battler's HP to half") } } -SINGLE_BATTLE_TEST("Revive: Max Revive restores a fainted battler's HP fully") +SINGLE_BATTLE_TEST("Max Revive restores a fainted battler's HP fully") { GIVEN { ASSUME(gItems[ITEM_MAX_REVIVE].battleUsage == EFFECT_ITEM_REVIVE); @@ -39,7 +39,7 @@ SINGLE_BATTLE_TEST("Revive: Max Revive restores a fainted battler's HP fully") } } -SINGLE_BATTLE_TEST("Revive: Revival Herb restores a fainted battler's HP fully") +SINGLE_BATTLE_TEST("Revival Herb restores a fainted battler's HP fully") { GIVEN { ASSUME(gItems[ITEM_REVIVAL_HERB].battleUsage == EFFECT_ITEM_REVIVE); @@ -57,7 +57,7 @@ SINGLE_BATTLE_TEST("Revive: Revival Herb restores a fainted battler's HP fully") } } -SINGLE_BATTLE_TEST("Revive: Max Honey restores a fainted battler's HP fully") +SINGLE_BATTLE_TEST("Max Honey restores a fainted battler's HP fully") { GIVEN { ASSUME(gItems[ITEM_MAX_HONEY].battleUsage == EFFECT_ITEM_REVIVE); From 8e5347b450f4b655cc58a783de70e14b0fd115e1 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 21 Apr 2023 03:20:36 -0400 Subject: [PATCH 50/88] Fix mapjson misreporting errors when processing map_groups.json --- tools/mapjson/mapjson.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tools/mapjson/mapjson.cpp b/tools/mapjson/mapjson.cpp index cfe95c485a..e53ac7924d 100644 --- a/tools/mapjson/mapjson.cpp +++ b/tools/mapjson/mapjson.cpp @@ -75,6 +75,9 @@ string json_to_string(const Json &data, const string &field = "", bool silent = case Json::Type::BOOL: output = value.bool_value() ? "TRUE" : "FALSE"; break; + case Json::Type::NUL: + output = ""; + break; default:{ if (!silent) { string s = !field.empty() ? ("Value for '" + field + "'") : "JSON field"; @@ -459,23 +462,24 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) { for (auto &group : groups_data["group_order"].array_items()) { string groupName = json_to_string(group); text << "// " << groupName << "\n"; - vector map_ids; + vector map_ids; size_t max_length = 0; for (auto &map_name : groups_data[groupName].array_items()) { - string header_filepath = file_dir + json_to_string(map_name) + dir_separator + "map.json"; + string map_filepath = file_dir + json_to_string(map_name) + dir_separator + "map.json"; string err_str; - Json map_data = Json::parse(read_text_file(header_filepath), err_str); - map_ids.push_back(map_data["id"]); - string id = json_to_string(map_data, "id"); + Json map_data = Json::parse(read_text_file(map_filepath), err_str); + if (map_data == Json()) + FATAL_ERROR("%s: %s\n", map_filepath.c_str(), err_str.c_str()); + string id = json_to_string(map_data, "id", true); + map_ids.push_back(id); if (id.length() > max_length) max_length = id.length(); } int map_id_num = 0; - for (Json map_id : map_ids) { - string id = json_to_string(map_id); - text << "#define " << id << string((max_length - id.length() + 1), ' ') + for (string map_id : map_ids) { + text << "#define " << map_id << string((max_length - map_id.length() + 1), ' ') << "(" << map_id_num++ << " | (" << group_num << " << 8))\n"; } text << "\n"; From 15ce9eb71cde27b9142bb20c067fef19333ca852 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Mon, 1 May 2023 18:53:06 +0200 Subject: [PATCH 51/88] fix dire hit message mismatch --- src/battle_message.c | 2 +- test/item_effect_set_focus_energy.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/battle_message.c b/src/battle_message.c index 21f7098f81..cf42176a1b 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -260,7 +260,7 @@ static const u8 sText_XFoundOneY[] = _("{B_ATK_NAME_WITH_PREFIX} found\none {B_L static const u8 sText_SoothingAroma[] = _("A soothing aroma wafted\nthrough the area!"); static const u8 sText_ItemsCantBeUsedNow[] = _("Items can't be used now.{PAUSE 64}"); static const u8 sText_ForXCommaYZ[] = _("For {B_SCR_ACTIVE_NAME_WITH_PREFIX},\n{B_LAST_ITEM} {B_BUFF1}"); -static const u8 sText_PkmnUsedXToGetPumped[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used\n{B_LAST_ITEM} to get pumped!"); +static const u8 sText_PkmnUsedXToGetPumped[] = _("{B_ACTIVE_NAME_WITH_PREFIX} used\n{B_LAST_ITEM} to get pumped!"); static const u8 sText_PkmnLostFocus[] = _("{B_ATK_NAME_WITH_PREFIX} lost its\nfocus and couldn't move!"); static const u8 sText_PkmnWasDraggedOut[] = _("{B_DEF_NAME_WITH_PREFIX} was\ndragged out!\p"); static const u8 sText_TheWallShattered[] = _("The wall shattered!"); diff --git a/test/item_effect_set_focus_energy.c b/test/item_effect_set_focus_energy.c index 19093e739a..95648465e6 100644 --- a/test/item_effect_set_focus_energy.c +++ b/test/item_effect_set_focus_energy.c @@ -3,10 +3,6 @@ SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages") { - /* KNOWN_FAILING due to message mismatch for "Wobbuffet used Dire Hit to get pumped!" - * As of the time of writing, this test passes if the above mentioned message is removed - * For more details refer to description of PR #2940 */ - KNOWN_FAILING; ASSUME(B_CRIT_CHANCE >= GEN_7); PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); GIVEN { From bdd0195fe01963e81dfe09c5b8e2368a1d12f0be Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 1 May 2023 17:00:37 -0400 Subject: [PATCH 52/88] add mirror herb tests --- test/hold_effect_mirror_herb.c | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 test/hold_effect_mirror_herb.c diff --git a/test/hold_effect_mirror_herb.c b/test/hold_effect_mirror_herb.c new file mode 100644 index 0000000000..68ea5614b4 --- /dev/null +++ b/test/hold_effect_mirror_herb.c @@ -0,0 +1,51 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB; +}; + +SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage) +{ + u32 item; + PARAMETRIZE{ item = ITEM_NONE; } + PARAMETRIZE{ item = ITEM_MIRROR_HERB; } + GIVEN { + ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_DRAGON_DANCE); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + if (item == ITEM_NONE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + } + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_SPEED], opponent->statStages[STAT_SPEED]); + } +} + +SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks", s16 damage) +{ + GIVEN { + ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); + ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP); + PLAYER(SPECIES_SKWOVET) { Item(ITEM_LIECHI_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); } + } WHEN { + TURN { MOVE(player, MOVE_STUFF_CHEEKS); } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_DEF], opponent->statStages[STAT_DEF]); + } +} From 3652e8261a3fa8575c0f54a194d562f5c11f434c Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Mon, 1 May 2023 15:09:20 -0600 Subject: [PATCH 53/88] Apply suggestions from code review Co-authored-by: Salem <65783283+CallmeEchoo@users.noreply.github.com> --- test/hold_effect_mirror_herb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/hold_effect_mirror_herb.c b/test/hold_effect_mirror_herb.c index 68ea5614b4..b1c3c8132b 100644 --- a/test/hold_effect_mirror_herb.c +++ b/test/hold_effect_mirror_herb.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB; + ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); }; SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage) @@ -12,7 +12,6 @@ SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 PARAMETRIZE{ item = ITEM_NONE; } PARAMETRIZE{ item = ITEM_MIRROR_HERB; } GIVEN { - ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); PLAYER(SPECIES_WOBBUFFET) { Speed(4); } OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(item); } } WHEN { @@ -38,7 +37,6 @@ SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks", s16 damage) { GIVEN { - ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP); PLAYER(SPECIES_SKWOVET) { Item(ITEM_LIECHI_BERRY); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); } From 63c2e8a7d98b50921bdc1398dfb41bf04326de4e Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 1 May 2023 18:02:21 -0400 Subject: [PATCH 54/88] small mirror herb test synta fix --- test/hold_effect_mirror_herb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hold_effect_mirror_herb.c b/test/hold_effect_mirror_herb.c index b1c3c8132b..fc0a6de957 100644 --- a/test/hold_effect_mirror_herb.c +++ b/test/hold_effect_mirror_herb.c @@ -4,7 +4,7 @@ ASSUMPTIONS { ASSUME(gItems[ITEM_MIRROR_HERB].holdEffect == HOLD_EFFECT_MIRROR_HERB); -}; +} SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage) { From 52cc3bb45ea0c272e8311834114855144b2ec9db Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 2 May 2023 16:16:58 +0200 Subject: [PATCH 55/88] add some snow related tests --- include/config/battle.h | 2 +- test/ability_snow_warning.c | 20 +++++ test/weather_snow.c | 159 ++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 test/ability_snow_warning.c create mode 100644 test/weather_snow.c diff --git a/include/config/battle.h b/include/config/battle.h index 0605ad43e5..4be810ed2a 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -118,7 +118,7 @@ #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. -#define B_SNOW_WARNING GEN_9 // In Gen9+, Snow Warning will summon snow instead of hail. +#define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. diff --git a/test/ability_snow_warning.c b/test/ability_snow_warning.c new file mode 100644 index 0000000000..6c0753ad39 --- /dev/null +++ b/test/ability_snow_warning.c @@ -0,0 +1,20 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Snow Warning summons hail(snow)") +{ + GIVEN { + PLAYER(SPECIES_ABOMASNOW) { Ability(ABILITY_SNOW_WARNING); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN {} + } SCENE { + #if B_SNOW_WARNING < GEN_9 + MESSAGE("It started to hail!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HAIL_CONTINUES); + #elif B_SNOW_WARNING >= GEN_9 + MESSAGE("It started to snow!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SNOW_CONTINUES); + #endif + } +} diff --git a/test/weather_snow.c b/test/weather_snow.c new file mode 100644 index 0000000000..07afa48d57 --- /dev/null +++ b/test/weather_snow.c @@ -0,0 +1,159 @@ +#include "global.h" +#include "test_battle.h" + +#define TEST_HP 1 +#define MAX_HP 400 + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_SNOWSCAPE].effect == EFFECT_SNOW); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[1] == TYPE_ICE); +} + +SINGLE_BATTLE_TEST("Snow increases the defense of Ice types by 50 %", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE{ move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GLALIE); + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Snow turns Weather Ball to an Ice-type move and doubles its power", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_WEATHER_BALL].effect == EFFECT_WEATHER_BALL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_DRAGONAIR); + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(player, MOVE_WEATHER_BALL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.0), results[1].damage); // double base power + type effectiveness. + } +} + +SINGLE_BATTLE_TEST("Snow allows Blizzard to bypass accuracy checks") +{ + PASSES_RANDOMLY(100, 100); + GIVEN { + ASSUME(gBattleMoves[MOVE_BLIZZARD].accuracy == 70); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_BLIZZARD); } + } SCENE { + NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } + } +} + +SINGLE_BATTLE_TEST("Snow halves the power of Solar Beam", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Snow halves the power of Solar Blade", s16 damage) +{ + u16 move; + KNOWN_FAILING; // fails bc the bp of solar blade gets rounded up which leads to slightly incorrect calcs down the line + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Snow causes Moonlight to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_MOONLIGHT].effect == EFFECT_MOONLIGHT); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MOONLIGHT); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +SINGLE_BATTLE_TEST("Snow causes Moonlight to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_MOONLIGHT].effect == EFFECT_MOONLIGHT); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MOONLIGHT); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +SINGLE_BATTLE_TEST("Snow causes Synthesis to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_SYNTHESIS].effect == EFFECT_SYNTHESIS); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_SYNTHESIS); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +SINGLE_BATTLE_TEST("Snow causes Morning Sun to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_MORNING_SUN].effect == EFFECT_MORNING_SUN); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MORNING_SUN); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +#undef MAX_HP +#undef TEST_HP \ No newline at end of file From d6f15104a38cc5186b60591c0c747dedf69e4504 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 2 May 2023 16:23:31 +0200 Subject: [PATCH 56/88] remove duplicate stringIDs --- include/constants/battle_string_ids.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index f9d55a8a9d..a43ea91ec3 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -655,9 +655,6 @@ #define STRINGID_STARTEDSNOW 653 #define STRINGID_SNOWCONTINUES 654 #define STRINGID_SNOWSTOPPED 655 -#define STRINGID_STARTEDSNOW 653 -#define STRINGID_SNOWCONTINUES 654 -#define STRINGID_SNOWSTOPPED 655 #define STRINGID_SNOWWARNINGSNOW 656 #define BATTLESTRINGS_COUNT 657 From 76721b16f89ca49893471880a68b193836450d09 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Sat, 29 Apr 2023 12:30:39 +0200 Subject: [PATCH 57/88] add snow weather --- asm/macros/battle_script.inc | 4 ++ data/battle_anim_scripts.s | 19 ++++++++- data/battle_scripts_1.s | 30 ++++++++++++++- include/battle_ai_util.h | 1 + include/battle_scripts.h | 4 +- include/config/battle.h | 1 + include/constants/battle.h | 6 ++- include/constants/battle_ai.h | 1 + include/constants/battle_anim.h | 2 + include/constants/battle_move_effects.h | 3 +- include/constants/battle_string_ids.h | 11 +++++- src/battle_ai_main.c | 34 ++++++++++++++++- src/battle_ai_util.c | 30 +++++++++++++-- src/battle_anim_effects_3.c | 2 + src/battle_gfx_sfx_util.c | 1 + src/battle_main.c | 4 +- src/battle_message.c | 19 ++++++++- src/battle_script_commands.c | 29 +++++++++++--- src/battle_tv.c | 3 +- src/battle_util.c | 51 +++++++++++++++++++++---- src/data/battle_moves.h | 2 +- 21 files changed, 226 insertions(+), 31 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 78fb914063..bfc81fbb6c 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1346,6 +1346,10 @@ callnative BS_ItemRestorePP .endm + .macro setsnow + callnative BS_SetSnow + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index dcfdac88ed..01311c35d1 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -947,6 +947,7 @@ gBattleAnims_General:: .4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP .4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE .4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON + .4byte General_Snow @ B_ANIM_SNOW_CONTINUES .align 2 gBattleAnims_Special:: @@ -14448,7 +14449,19 @@ Move_ELECTRO_DRIFT:: Move_SHED_TAIL:: Move_CHILLY_RECEPTION:: Move_TIDY_UP:: -Move_SNOWSCAPE:: + +@ Also used by Snow weather. Currently identical with Move_HAIL +Move_SNOWSCAPE: + loadspritegfx ANIM_TAG_HAIL + loadspritegfx ANIM_TAG_ICE_CRYSTALS + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 6, RGB_BLACK + waitforvisualfinish + createvisualtask AnimTask_Hail, 5 + loopsewithpan SE_M_HAIL, 0, 8, 10 + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 6, 0, RGB_BLACK + end + Move_POUNCE:: Move_TRAILBLAZE:: Move_CHILLING_WATER:: @@ -23924,6 +23937,7 @@ Move_WEATHER_BALL: jumpreteq ANIM_WEATHER_RAIN, WeatherBallWater jumpreteq ANIM_WEATHER_SANDSTORM, WeatherBallSandstorm jumpreteq ANIM_WEATHER_HAIL, WeatherBallIce + jumpreteq ANIM_WEATHER_SNOW, WeatherBallIce WeatherBallNormal: loadspritegfx ANIM_TAG_IMPACT createsprite gWeatherBallNormalDownSpriteTemplate, ANIM_TARGET, 2, -30, -100, 25, 1, 0, 0 @@ -24679,6 +24693,9 @@ General_Sandstorm: General_Hail: goto Move_HAIL +General_Snow: + goto Move_SNOWSCAPE + General_LeechSeedDrain: createvisualtask AnimTask_GetBattlersFromArg, 5 delay 0 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 4f41d154ef..d949e6cf72 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -429,6 +429,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE .4byte BattleScript_EffectRevivalBlessing @ EFFECT_REVIVAL_BLESSING .4byte BattleScript_EffectFrostbiteHit @ EFFECT_FROSTBITE_HIT + .4byte BattleScript_EffectSnow @ EFFECT_SNOW BattleScript_EffectRevivalBlessing:: attackcanceler @@ -6873,6 +6874,14 @@ BattleScript_DamagingWeatherContinuesEnd:: bicword gHitMarker, HITMARKER_SKIP_DMG_TRACK | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE | HITMARKER_GRUDGE end2 +BattleScript_SnowContinuesOrEnds:: + printfromtable gSnowContinuesStringIds + waitmessage B_WAIT_TIME_LONG + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SNOW_STOPPED, BattleScript_SnowContinuesOrEndsEnd + playanimation BS_ATTACKER, B_ANIM_SNOW_CONTINUES +BattleScript_SnowContinuesOrEndsEnd:: + end2 + BattleScript_SandStormHailEnds:: printfromtable gSandStormHailEndStringIds waitmessage B_WAIT_TIME_LONG @@ -8856,7 +8865,7 @@ BattleScript_MimicryActivates_End3:: waitmessage B_WAIT_TIME_SHORT end3 -BattleScript_SnowWarningActivates:: +BattleScript_SnowWarningActivatesHail:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp printstring STRINGID_SNOWWARNINGHAIL @@ -8865,6 +8874,15 @@ BattleScript_SnowWarningActivates:: call BattleScript_ActivateWeatherAbilities end3 +BattleScript_SnowWarningActivatesSnow:: + pause B_WAIT_TIME_SHORT + call BattleScript_AbilityPopUp + printstring STRINGID_SNOWWARNINGSNOW + waitstate + playanimation BS_BATTLER_0, B_ANIM_SNOW_CONTINUES + call BattleScript_WeatherFormChanges + end3 + BattleScript_ActivateTerrainEffects: savetarget setbyte gBattlerTarget, 0 @@ -10450,3 +10468,13 @@ BattleScript_BerserkGeneRet_OwnTempoPrevents: BattleScript_BerserkGeneRet_End: removeitem BS_SCRIPTING end3 + +BattleScript_EffectSnow:: + attackcanceler + attackstring + ppreduce + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_SUN_PRIMAL, BattleScript_ExtremelyHarshSunlightWasNotLessened + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_NoReliefFromHeavyRain + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOn + setsnow + goto BattleScript_MoveWeatherChange diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 1a0fbc2645..37c9e9c19c 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -112,6 +112,7 @@ bool32 IsEncoreEncouragedEffect(u16 moveEffect); void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score); bool32 ShouldSetSandstorm(u8 battler, u16 ability, u16 holdEffect); bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect); +bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect); bool32 ShouldSetRain(u8 battlerAtk, u16 ability, u16 holdEffect); bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect); bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 8cd8606a54..68b3a0c965 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -43,6 +43,7 @@ extern const u8 BattleScript_ActionSwitch[]; extern const u8 BattleScript_Pausex20[]; extern const u8 BattleScript_LevelUp[]; extern const u8 BattleScript_RainContinuesOrEnds[]; +extern const u8 BattleScript_SnowContinuesOrEnds[]; extern const u8 BattleScript_DamagingWeatherContinues[]; extern const u8 BattleScript_SandStormHailEnds[]; extern const u8 BattleScript_SunlightContinues[]; @@ -272,7 +273,8 @@ extern const u8 BattleScript_CursedBodyActivates[]; extern const u8 BattleScript_MummyActivates[]; extern const u8 BattleScript_WeakArmorActivates[]; extern const u8 BattleScript_FellStingerRaisesStat[]; -extern const u8 BattleScript_SnowWarningActivates[]; +extern const u8 BattleScript_SnowWarningActivatesHail[]; +extern const u8 BattleScript_SnowWarningActivatesSnow[]; extern const u8 BattleScript_HarvestActivates[]; extern const u8 BattleScript_ImposterActivates[]; extern const u8 BattleScript_SelectingNotAllowedMoveAssaultVest[]; diff --git a/include/config/battle.h b/include/config/battle.h index da58c8bbf8..1ed337ca35 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -120,6 +120,7 @@ #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. +#define B_SNOW_WARNING GEN_9 // In Gen9+, Snowwarning will summon snow instead of hail. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. diff --git a/include/constants/battle.h b/include/constants/battle.h index 950d98484f..814d1bfa57 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -288,8 +288,11 @@ #define B_WEATHER_HAIL_PERMANENT (1 << 10) #define B_WEATHER_HAIL (B_WEATHER_HAIL_TEMPORARY | B_WEATHER_HAIL_PERMANENT) #define B_WEATHER_STRONG_WINDS (1 << 11) -#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS) +#define B_WEATHER_ANY (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_STRONG_WINDS | B_WEATHER_SNOW) #define B_WEATHER_PRIMAL_ANY (B_WEATHER_RAIN_PRIMAL | B_WEATHER_SUN_PRIMAL | B_WEATHER_STRONG_WINDS) +#define B_WEATHER_SNOW_TEMPORARY (1 << 12) +#define B_WEATHER_SNOW_PERMANENT (1 << 13) +#define B_WEATHER_SNOW (B_WEATHER_SNOW_TEMPORARY | B_WEATHER_SNOW_PERMANENT) // Battle Weather as enum #define ENUM_WEATHER_NONE 0 @@ -300,6 +303,7 @@ #define ENUM_WEATHER_SUN_PRIMAL 5 #define ENUM_WEATHER_RAIN_PRIMAL 6 #define ENUM_WEATHER_STRONG_WINDS 7 +#define ENUM_WEATHER_SNOW 8 // Move Effects #define MOVE_EFFECT_SLEEP 1 diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index 1316054cd1..81dbdad163 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -30,6 +30,7 @@ #define AI_WEATHER_RAIN 2 #define AI_WEATHER_SANDSTORM 3 #define AI_WEATHER_HAIL 4 +#define AI_WEATHER_SNOW 5 // get_how_powerful_move_is #define MOVE_POWER_OTHER 0 diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 46e229a319..4b772022e5 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -544,6 +544,7 @@ #define B_ANIM_SHELL_TRAP_SETUP 34 #define B_ANIM_ZMOVE_ACTIVATE 35 // Using Z Moves #define B_ANIM_AFFECTION_HANGED_ON 36 +#define B_ANIM_SNOW_CONTINUES 37 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 @@ -591,6 +592,7 @@ #define ANIM_WEATHER_RAIN 2 #define ANIM_WEATHER_SANDSTORM 3 #define ANIM_WEATHER_HAIL 4 +#define ANIM_WEATHER_SNOW 5 // mon pal blend #define ANIM_PAL_BG 0x1 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 5df12b4496..bcc077055e 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -406,7 +406,8 @@ #define EFFECT_BARB_BARRAGE 400 #define EFFECT_REVIVAL_BLESSING 401 #define EFFECT_FROSTBITE_HIT 402 +#define EFFECT_SNOW 403 -#define NUM_BATTLE_MOVE_EFFECTS 403 +#define NUM_BATTLE_MOVE_EFFECTS 404 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 48abc03b9e..4946f74e77 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -659,8 +659,12 @@ #define STRINGID_PKMNFROSTBITEHEALED 657 #define STRINGID_PKMNFROSTBITEHEALED2 658 #define STRINGID_PKMNFROSTBITEHEALEDBY 659 +#define STRINGID_STARTEDSNOW 660 +#define STRINGID_SNOWCONTINUES 661 +#define STRINGID_SNOWSTOPPED 662 +#define STRINGID_SNOWWARNINGSNOW 663 -#define BATTLESTRINGS_COUNT 660 +#define BATTLESTRINGS_COUNT 664 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -726,12 +730,17 @@ #define B_MSG_STARTED_SANDSTORM 3 #define B_MSG_STARTED_SUNLIGHT 4 #define B_MSG_STARTED_HAIL 5 +#define B_MSG_STARTED_SNOW 6 // gRainContinuesStringIds #define B_MSG_RAIN_CONTINUES 0 #define B_MSG_DOWNPOUR_CONTINUES 1 #define B_MSG_RAIN_STOPPED 2 +// gSnowContinuesStringIds +#define B_MSG_SNOW_CONTINUES 0 +#define B_MSG_SNOW_STOPPED 1 + // gSandStormHailContinuesStringIds / gSandStormHailDmgStringIds/ gSandStormHailEndStringIds #define B_MSG_SANDSTORM 0 #define B_MSG_HAIL 1 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 7dc33dd3b2..de64913fac 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1372,7 +1372,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_AURORA_VEIL: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) - || !(gBattleWeather & B_WEATHER_HAIL)) + || !(gBattleWeather & B_WEATHER_HAIL && gBattleWeather & B_WEATHER_SNOW)) score -= 10; break; case EFFECT_OHKO: @@ -1578,7 +1578,12 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 8; break; case EFFECT_HAIL: - if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) + if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) // should hail be discouraged if snow is up? + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + score -= 8; + break; + case EFFECT_SNOW: + if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) // should snow be discouraged if hail is up? || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; @@ -2803,6 +2808,13 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) RETURN_SCORE_PLUS(2); // our partner benefits from hail } break; + case EFFECT_SNOW: + if (IsBattlerAlive(battlerAtkPartner) + && ShouldSetSnow(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect)) + { + RETURN_SCORE_PLUS(2); // our partner benefits from snow + } + break; } // global move effect check @@ -3965,6 +3977,22 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; } break; + case EFFECT_SNOW: // any other reasons? + if (ShouldSetSnow(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + { + if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL)) + && ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL)) + score += 3; + + score++; + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) + score++; + if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) + || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) + || HasMoveEffect(battlerDef, EFFECT_MOONLIGHT)) + score += 2; + } + break; case EFFECT_RAIN_DANCE: if (ShouldSetRain(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { @@ -4948,6 +4976,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: case EFFECT_HAIL: + case EFFECT_SNOW: case EFFECT_GEOMANCY: case EFFECT_VICTORY_DANCE: case EFFECT_HIT_SET_ENTRY_HAZARD: @@ -5174,6 +5203,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: case EFFECT_HAIL: + case EFFECT_SNOW: case EFFECT_RAIN_DANCE: score -= 2; break; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 3adbd77ae2..010c5a01c9 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -331,6 +331,7 @@ static const u16 sEncouragedEncoreEffects[] = EFFECT_SPIT_UP, EFFECT_SWALLOW, EFFECT_HAIL, + EFFECT_SNOW, EFFECT_TORMENT, EFFECT_WILL_O_WISP, EFFECT_FOLLOW_ME, @@ -1504,7 +1505,7 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) // increased accuracy but don't always hit if ((AI_WeatherHasEffect() && (((gBattleWeather & B_WEATHER_RAIN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)) - || (((gBattleWeather & B_WEATHER_HAIL) && move == MOVE_BLIZZARD)))) + || (((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD)))) || (gBattleMoves[move].effect == EFFECT_VITAL_THROW) #if B_MINIMIZE_DMG_ACC >= GEN_6 || ((gStatuses3[battlerDef] & STATUS3_MINIMIZED) && (gBattleMoves[move].flags & FLAG_DMG_MINIMIZE)) @@ -1579,7 +1580,7 @@ bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect) { if (!AI_WeatherHasEffect()) return FALSE; - else if (gBattleWeather & B_WEATHER_HAIL) + else if (gBattleWeather & B_WEATHER_HAIL) // dont set hail if snow is up? return FALSE; if (ability == ABILITY_SNOW_CLOAK @@ -1649,6 +1650,26 @@ bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect) return FALSE; } +bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect) // any other reasons snow should be set? +{ + if (!AI_WeatherHasEffect()) + return FALSE; + else if (gBattleWeather & B_WEATHER_SNOW) // dont set snow if hail is up? + return FALSE; + + if (ability == ABILITY_SNOW_CLOAK + || ability == ABILITY_ICE_BODY + || ability == ABILITY_FORECAST + || ability == ABILITY_SLUSH_RUSH + || IS_BATTLER_OF_TYPE(battler, TYPE_ICE) + || HasMove(battler, MOVE_BLIZZARD) + || HasMoveEffect(battler, EFFECT_AURORA_VEIL) + || HasMoveEffect(battler, EFFECT_WEATHER_BALL)) + { + return TRUE; + } + return FALSE; +} void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score) { @@ -3137,7 +3158,7 @@ bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect) { case EFFECT_AURORA_VEIL: // Use only in Hail and only if AI doesn't already have Reflect, Light Screen or Aurora Veil itself active. - if (gBattleWeather & B_WEATHER_HAIL + if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && !(gSideStatuses[atkSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL))) return TRUE; break; @@ -3242,7 +3263,8 @@ bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove) && (gBattleMoves[partnerMove].effect == EFFECT_SUNNY_DAY || gBattleMoves[partnerMove].effect == EFFECT_RAIN_DANCE || gBattleMoves[partnerMove].effect == EFFECT_SANDSTORM - || gBattleMoves[partnerMove].effect == EFFECT_HAIL)) + || gBattleMoves[partnerMove].effect == EFFECT_HAIL + || gBattleMoves[partnerMove].effect == EFFECT_SNOW)) return TRUE; return FALSE; diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c index 7d449c767e..4e510c59c7 100755 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -5603,6 +5603,8 @@ void AnimTask_GetWeather(u8 taskId) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SANDSTORM; else if (gWeatherMoveAnim & B_WEATHER_HAIL) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_HAIL; + else if (gWeatherMoveAnim & B_WEATHER_SNOW) + gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SNOW; DestroyAnimVisualTask(taskId); } diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index c06a7795a5..b1187b59e7 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -492,6 +492,7 @@ static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId) case B_ANIM_SUN_CONTINUES: case B_ANIM_SANDSTORM_CONTINUES: case B_ANIM_HAIL_CONTINUES: + case B_ANIM_SNOW_CONTINUES: case B_ANIM_SNATCH_MOVE: return TRUE; default: diff --git a/src/battle_main.c b/src/battle_main.c index 91d5c3aa50..7270e0f369 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4642,7 +4642,7 @@ u32 GetBattlerTotalSpeedStat(u8 battlerId) speed *= 2; else if (ability == ABILITY_SAND_RUSH && gBattleWeather & B_WEATHER_SANDSTORM) speed *= 2; - else if (ability == ABILITY_SLUSH_RUSH && gBattleWeather & B_WEATHER_HAIL) + else if (ability == ABILITY_SLUSH_RUSH && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) speed *= 2; } @@ -5590,7 +5590,7 @@ void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk) gBattleStruct->dynamicMoveType = TYPE_ROCK | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) gBattleStruct->dynamicMoveType = TYPE_FIRE | F_DYNAMIC_TYPE_2; - else if (gBattleWeather & B_WEATHER_HAIL) + else if (gBattleWeather & (B_WEATHER_HAIL |B_WEATHER_SNOW)) gBattleStruct->dynamicMoveType = TYPE_ICE | F_DYNAMIC_TYPE_2; else gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2; diff --git a/src/battle_message.c b/src/battle_message.c index 29fdbd28c2..66bc4a7737 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -363,8 +363,11 @@ static const u8 sText_SunlightGotBright[] = _("The sunlight got bright!"); static const u8 sText_SunlightStrong[] = _("The sunlight is strong."); static const u8 sText_SunlightFaded[] = _("The sunlight faded."); static const u8 sText_StartedHail[] = _("It started to hail!"); +static const u8 sText_StartedSnow[] = _("It started to snow!"); static const u8 sText_HailContinues[] = _("Hail continues to fall."); +static const u8 sText_SnowContinues[] = _("Snow continues to fall."); static const u8 sText_HailStopped[] = _("The hail stopped."); +static const u8 sText_SnowStopped[] = _("The snow stopped."); static const u8 sText_FailedToSpitUp[] = _("But it failed to spit up\na thing!"); static const u8 sText_FailedToSwallow[] = _("But it failed to swallow\na thing!"); static const u8 sText_WindBecameHeatWave[] = _("The wind turned into a\nHEAT WAVE!"); @@ -614,6 +617,7 @@ static const u8 sText_AnticipationActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFI static const u8 sText_ForewarnActivates[] = _("{B_SCR_ACTIVE_ABILITY} alerted {B_SCR_ACTIVE_NAME_WITH_PREFIX}\nto the {B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1}!"); static const u8 sText_IceBodyHpGain[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY}\nhealed it a little bit!"); static const u8 sText_SnowWarningHail[] = _("It started to hail!"); +static const u8 sText_SnowWarningSnow[] = _("It started to snow!"); static const u8 sText_FriskActivates[] = _("{B_ATK_NAME_WITH_PREFIX} frisked {B_DEF_NAME_WITH_PREFIX} and\nfound its {B_LAST_ITEM}!"); static const u8 sText_UnnerveEnters[] = _("The opposing team is too nervous\nto eat Berries!"); static const u8 sText_HarvestBerry[] = _("{B_ATK_NAME_WITH_PREFIX} harvested\nits {B_LAST_ITEM}!"); @@ -1141,6 +1145,9 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_STARTEDHAIL - BATTLESTRINGS_TABLE_START] = sText_StartedHail, [STRINGID_HAILCONTINUES - BATTLESTRINGS_TABLE_START] = sText_HailContinues, [STRINGID_HAILSTOPPED - BATTLESTRINGS_TABLE_START] = sText_HailStopped, + [STRINGID_STARTEDSNOW - BATTLESTRINGS_TABLE_START] = sText_StartedSnow, + [STRINGID_SNOWCONTINUES -BATTLESTRINGS_TABLE_START] = sText_SnowContinues, + [STRINGID_SNOWSTOPPED - BATTLESTRINGS_TABLE_START] = sText_SnowStopped, [STRINGID_FAILEDTOSPITUP - BATTLESTRINGS_TABLE_START] = sText_FailedToSpitUp, [STRINGID_FAILEDTOSWALLOW - BATTLESTRINGS_TABLE_START] = sText_FailedToSwallow, [STRINGID_WINDBECAMEHEATWAVE - BATTLESTRINGS_TABLE_START] = sText_WindBecameHeatWave, @@ -1361,6 +1368,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_FOREWARNACTIVATES - BATTLESTRINGS_TABLE_START] = sText_ForewarnActivates, [STRINGID_ICEBODYHPGAIN - BATTLESTRINGS_TABLE_START] = sText_IceBodyHpGain, [STRINGID_SNOWWARNINGHAIL - BATTLESTRINGS_TABLE_START] = sText_SnowWarningHail, + [STRINGID_SNOWWARNINGSNOW - BATTLESTRINGS_TABLE_START] = sText_SnowWarningSnow, [STRINGID_FRISKACTIVATES - BATTLESTRINGS_TABLE_START] = sText_FriskActivates, [STRINGID_UNNERVEENTERS - BATTLESTRINGS_TABLE_START] = sText_UnnerveEnters, [STRINGID_HARVESTBERRY - BATTLESTRINGS_TABLE_START] = sText_HarvestBerry, @@ -1557,12 +1565,13 @@ const u16 gMoveWeatherChangeStringIds[] = [B_MSG_STARTED_SANDSTORM] = STRINGID_SANDSTORMBREWED, [B_MSG_STARTED_SUNLIGHT] = STRINGID_SUNLIGHTGOTBRIGHT, [B_MSG_STARTED_HAIL] = STRINGID_STARTEDHAIL, + [B_MSG_STARTED_SNOW] = STRINGID_STARTEDSNOW, }; const u16 gSandStormHailContinuesStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMRAGES, - [B_MSG_HAIL] = STRINGID_HAILCONTINUES + [B_MSG_HAIL] = STRINGID_HAILCONTINUES, }; const u16 gSandStormHailDmgStringIds[] = @@ -1574,7 +1583,7 @@ const u16 gSandStormHailDmgStringIds[] = const u16 gSandStormHailEndStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMSUBSIDED, - [B_MSG_HAIL] = STRINGID_HAILSTOPPED + [B_MSG_HAIL] = STRINGID_HAILSTOPPED, }; const u16 gRainContinuesStringIds[] = @@ -1584,6 +1593,12 @@ const u16 gRainContinuesStringIds[] = [B_MSG_RAIN_STOPPED] = STRINGID_RAINSTOPPED }; +const u16 gSnowContinuesStringIds[] = +{ + [B_MSG_SNOW_CONTINUES] = STRINGID_SNOWCONTINUES, + [B_MSG_SNOW_STOPPED] = STRINGID_SNOWSTOPPED, +}; + const u16 gProtectLikeUsedStringIds[] = { [B_MSG_PROTECTED_ITSELF] = STRINGID_PKMNPROTECTEDITSELF2, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1784f9682b..3bc77e8485 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1812,7 +1812,7 @@ static bool32 AccuracyCalcHelper(u16 move) return TRUE; } #if B_BLIZZARD_HAIL >= GEN_4 - else if ((gBattleWeather & B_WEATHER_HAIL) && move == MOVE_BLIZZARD) + else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD) { // thunder/hurricane ignore acc checks in rain unless target is holding utility umbrella JumpIfMoveFailed(7, move); @@ -1892,7 +1892,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM) calc = (calc * 80) / 100; // 1.2 sand veil loss - else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL) + else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) calc = (calc * 80) / 100; // 1.2 snow cloak loss else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) calc = (calc * 50) / 100; // 1.5 tangled feet loss @@ -5190,7 +5190,8 @@ static void Cmd_playanimation(void) else if (animId == B_ANIM_RAIN_CONTINUES || animId == B_ANIM_SUN_CONTINUES || animId == B_ANIM_SANDSTORM_CONTINUES - || animId == B_ANIM_HAIL_CONTINUES) + || animId == B_ANIM_HAIL_CONTINUES + || animId == B_ANIM_SNOW_CONTINUES) { BtlController_EmitBattleAnimation(BUFFER_A, animId, *argPtr); MarkBattlerForControllerExec(gActiveBattler); @@ -5239,7 +5240,8 @@ static void Cmd_playanimation_var(void) else if (*animIdPtr == B_ANIM_RAIN_CONTINUES || *animIdPtr == B_ANIM_SUN_CONTINUES || *animIdPtr == B_ANIM_SANDSTORM_CONTINUES - || *animIdPtr == B_ANIM_HAIL_CONTINUES) + || *animIdPtr == B_ANIM_HAIL_CONTINUES + || *animIdPtr == B_ANIM_SNOW_CONTINUES) { BtlController_EmitBattleAnimation(BUFFER_A, *animIdPtr, *argPtr); MarkBattlerForControllerExec(gActiveBattler); @@ -10250,7 +10252,7 @@ static void Cmd_various(void) { VARIOUS_ARGS(); if (gSideStatuses[GET_BATTLER_SIDE(gActiveBattler)] & SIDE_STATUS_AURORA_VEIL - || !(WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL)) + || !(WEATHER_HAS_EFFECT && gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) { gMoveResultFlags |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = 0; @@ -16559,3 +16561,20 @@ void BS_ItemRestorePP(void) { PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(mon, MON_DATA_SPECIES)); gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_SetSnow(void) +{ + NATIVE_ARGS(); + + if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SNOW, FALSE)) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; + + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW; + } + gBattlescriptCurrInstr = cmd->nextInstr; +} \ No newline at end of file diff --git a/src/battle_tv.c b/src/battle_tv.c index f5c9f43c4e..74f8c7008c 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -251,6 +251,7 @@ static const u16 sPoints_MoveEffect[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SWALLOW] = 3, // [EFFECT_UNUSED_A3] = 1, [EFFECT_HAIL] = 4, + [EFFECT_SNOW] = 4, [EFFECT_TORMENT] = 7, [EFFECT_FLATTER] = 7, [EFFECT_WILL_O_WISP] = 5, @@ -1770,6 +1771,6 @@ static void AddPointsBasedOnWeather(u16 weatherFlags, u16 moveId, u8 moveSlot) AddMovePoints(PTS_SUN, moveId, moveSlot, 0); else if (weatherFlags & B_WEATHER_SANDSTORM) AddMovePoints(PTS_SANDSTORM, moveId, moveSlot, 0); - else if (weatherFlags & B_WEATHER_HAIL) + else if (weatherFlags & (B_WEATHER_HAIL | B_WEATHER_SNOW)) AddMovePoints(PTS_HAIL, moveId, moveSlot, 0); } diff --git a/src/battle_util.c b/src/battle_util.c index 1c5a5d5d94..563ab9818a 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2032,6 +2032,7 @@ enum ENDTURN_SANDSTORM, ENDTURN_SUN, ENDTURN_HAIL, + ENDTURN_SNOW, ENDTURN_GRAVITY, ENDTURN_WATER_SPORT, ENDTURN_MUD_SPORT, @@ -2394,6 +2395,27 @@ u8 DoFieldEndTurnEffects(void) } gBattleStruct->turnCountersTracker++; break; + case ENDTURN_SNOW: + if (gBattleWeather & B_WEATHER_SNOW) + { + if (!(gBattleWeather & B_WEATHER_SNOW_PERMANENT)) + { + if (--gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather &= ~B_WEATHER_SNOW_TEMPORARY; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW_STOPPED; + } + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW_CONTINUES; + } + + BattleScriptExecute(BattleScript_SnowContinuesOrEnds); + effect++; + } + gBattleStruct->turnCountersTracker++; + break; case ENDTURN_TRICK_ROOM: if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && --gFieldTimers.trickRoomTimer == 0) { @@ -4032,7 +4054,7 @@ u8 TryWeatherFormChange(u8 battler) } } #endif - else if (holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || (!(gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_HAIL)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))) + else if (holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || (!(gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_HAIL | B_WEATHER_SNOW)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); ret = CASTFORM_NORMAL + 1; @@ -4047,7 +4069,7 @@ u8 TryWeatherFormChange(u8 battler) SET_BATTLER_TYPE(battler, TYPE_WATER); ret = CASTFORM_WATER + 1; } - else if (gBattleWeather & B_WEATHER_HAIL && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) + else if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW) && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) { SET_BATTLER_TYPE(battler, TYPE_ICE); ret = CASTFORM_ICE + 1; @@ -4083,6 +4105,7 @@ static const u16 sWeatherFlagsInfo[][3] = [ENUM_WEATHER_SANDSTORM] = {B_WEATHER_SANDSTORM_TEMPORARY, B_WEATHER_SANDSTORM_PERMANENT, HOLD_EFFECT_SMOOTH_ROCK}, [ENUM_WEATHER_HAIL] = {B_WEATHER_HAIL_TEMPORARY, B_WEATHER_HAIL_PERMANENT, HOLD_EFFECT_ICY_ROCK}, [ENUM_WEATHER_STRONG_WINDS] = {B_WEATHER_STRONG_WINDS, B_WEATHER_STRONG_WINDS, HOLD_EFFECT_NONE}, + [ENUM_WEATHER_SNOW] = {B_WEATHER_SNOW_TEMPORARY, B_WEATHER_SNOW_PERMANENT, HOLD_EFFECT_ICY_ROCK}, }; static void ShouldChangeFormInWeather(u8 battler) @@ -4103,7 +4126,6 @@ static void ShouldChangeFormInWeather(u8 battler) bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility) { u16 battlerAbility = GetBattlerAbility(battler); - if (gBattleWeather & B_WEATHER_PRIMAL_ANY && battlerAbility != ABILITY_DESOLATE_LAND && battlerAbility != ABILITY_PRIMORDIAL_SEA @@ -4129,7 +4151,6 @@ bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility) ShouldChangeFormInWeather(battler); return TRUE; } - return FALSE; } @@ -4634,11 +4655,19 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITY_SNOW_WARNING: - if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE)) + #if B_SNOW_WARNING >= GEN_9 + if (TryChangeBattleWeather(battler, ENUM_WEATHER_SNOW, TRUE)) { - BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivates); + BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow); effect++; } + #else + if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE)) + { + BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail); + effect++; + } + #endif else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; @@ -6111,7 +6140,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITY_ICE_FACE: - if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL) + if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL | B_WEATHER_SNOW) && gBattleMons[battler].species == SPECIES_EISCUE_NOICE_FACE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)]) @@ -9099,7 +9128,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_SOLAR_BEAM: - if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN))) + if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN | B_WEATHER_SNOW))) MulModifier(&modifier, UQ_4_12(0.5)); break; case EFFECT_STOMPING_TANTRUM: @@ -9504,6 +9533,12 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, // sandstorm sp.def boost for rock types if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); + // snow def boost for ice types + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); + + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) + MulModifier(&modifier, UQ_4_12(1.5)); // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. // Having the 5th badge boosts physical defense while having the 7th badge boosts special defense. diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 9473bae71c..6d88acb8cd 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13356,7 +13356,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_SNOWSCAPE] = { - .effect = EFFECT_HAIL, + .effect = EFFECT_SNOW, .power = 0, .type = TYPE_ICE, .accuracy = 0, From 0bf92943d441091fe7f0459bc8fd66ee632f1345 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Sun, 30 Apr 2023 02:35:09 +0200 Subject: [PATCH 58/88] spelling --- include/config/battle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index 1ed337ca35..4daeb59b4b 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -120,7 +120,7 @@ #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. -#define B_SNOW_WARNING GEN_9 // In Gen9+, Snowwarning will summon snow instead of hail. +#define B_SNOW_WARNING GEN_9 // In Gen9+, Snow Warning will summon snow instead of hail. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. From c6fbde6d5c03452f284afa6526016ee1d6bdc148 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Sun, 30 Apr 2023 15:11:35 +0200 Subject: [PATCH 59/88] remove stupidity for some reason I added the 50% def increase in snow check twice? this removes that --- src/battle_util.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 563ab9818a..1ab2f32e53 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9537,9 +9537,6 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) - MulModifier(&modifier, UQ_4_12(1.5)); - // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. // Having the 5th badge boosts physical defense while having the 7th badge boosts special defense. if (ShouldGetStatBadgeBoost(FLAG_BADGE05_GET, battlerDef) && IS_MOVE_PHYSICAL(move)) From 6758f6ebe755e6961d365ec2ecdfffea6f9c6f2f Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 2 May 2023 16:16:58 +0200 Subject: [PATCH 60/88] add some snow related tests --- include/config/battle.h | 2 +- test/ability_snow_warning.c | 20 +++++ test/weather_snow.c | 159 ++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 test/ability_snow_warning.c create mode 100644 test/weather_snow.c diff --git a/include/config/battle.h b/include/config/battle.h index 4daeb59b4b..6ba84409f6 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -120,7 +120,7 @@ #define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply. #define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied. #define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases. -#define B_SNOW_WARNING GEN_9 // In Gen9+, Snow Warning will summon snow instead of hail. +#define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. diff --git a/test/ability_snow_warning.c b/test/ability_snow_warning.c new file mode 100644 index 0000000000..6c0753ad39 --- /dev/null +++ b/test/ability_snow_warning.c @@ -0,0 +1,20 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Snow Warning summons hail(snow)") +{ + GIVEN { + PLAYER(SPECIES_ABOMASNOW) { Ability(ABILITY_SNOW_WARNING); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN {} + } SCENE { + #if B_SNOW_WARNING < GEN_9 + MESSAGE("It started to hail!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HAIL_CONTINUES); + #elif B_SNOW_WARNING >= GEN_9 + MESSAGE("It started to snow!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SNOW_CONTINUES); + #endif + } +} diff --git a/test/weather_snow.c b/test/weather_snow.c new file mode 100644 index 0000000000..07afa48d57 --- /dev/null +++ b/test/weather_snow.c @@ -0,0 +1,159 @@ +#include "global.h" +#include "test_battle.h" + +#define TEST_HP 1 +#define MAX_HP 400 + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_SNOWSCAPE].effect == EFFECT_SNOW); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[1] == TYPE_ICE); +} + +SINGLE_BATTLE_TEST("Snow increases the defense of Ice types by 50 %", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE{ move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GLALIE); + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Snow turns Weather Ball to an Ice-type move and doubles its power", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_WEATHER_BALL].effect == EFFECT_WEATHER_BALL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_DRAGONAIR); + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(player, MOVE_WEATHER_BALL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.0), results[1].damage); // double base power + type effectiveness. + } +} + +SINGLE_BATTLE_TEST("Snow allows Blizzard to bypass accuracy checks") +{ + PASSES_RANDOMLY(100, 100); + GIVEN { + ASSUME(gBattleMoves[MOVE_BLIZZARD].accuracy == 70); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_BLIZZARD); } + } SCENE { + NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } + } +} + +SINGLE_BATTLE_TEST("Snow halves the power of Solar Beam", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Snow halves the power of Solar Blade", s16 damage) +{ + u16 move; + KNOWN_FAILING; // fails bc the bp of solar blade gets rounded up which leads to slightly incorrect calcs down the line + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Snow causes Moonlight to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_MOONLIGHT].effect == EFFECT_MOONLIGHT); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MOONLIGHT); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +SINGLE_BATTLE_TEST("Snow causes Moonlight to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_MOONLIGHT].effect == EFFECT_MOONLIGHT); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MOONLIGHT); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +SINGLE_BATTLE_TEST("Snow causes Synthesis to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_SYNTHESIS].effect == EFFECT_SYNTHESIS); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_SYNTHESIS); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +SINGLE_BATTLE_TEST("Snow causes Morning Sun to recover 1/4 of the user's max HP") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_MORNING_SUN].effect == EFFECT_MORNING_SUN); + PLAYER(SPECIES_WOBBUFFET) { HP(TEST_HP); MaxHP(MAX_HP); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_MORNING_SUN); } + } SCENE { + HP_BAR(player, hp: TEST_HP + (MAX_HP/4)); + } +} + +#undef MAX_HP +#undef TEST_HP \ No newline at end of file From c32d5f455b7dcfcd9c34542e9c2a06fabd8381df Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 2 May 2023 16:44:42 +0200 Subject: [PATCH 61/88] fix rebase issues --- data/battle_scripts_1.s | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 6e32ce0afd..d1795dcf55 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8880,16 +8880,7 @@ BattleScript_SnowWarningActivatesSnow:: printstring STRINGID_SNOWWARNINGSNOW waitstate playanimation BS_BATTLER_0, B_ANIM_SNOW_CONTINUES - call BattleScript_WeatherFormChanges - end3 - -BattleScript_SnowWarningActivatesSnow:: - pause B_WAIT_TIME_SHORT - call BattleScript_AbilityPopUp - printstring STRINGID_SNOWWARNINGSNOW - waitstate - playanimation BS_BATTLER_0, B_ANIM_SNOW_CONTINUES - call BattleScript_WeatherFormChanges + call BattleScript_ActivateWeatherAbilities end3 BattleScript_ActivateTerrainEffects: From 095422c0c065247ca685c08cdafc9c35db585975 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 2 May 2023 19:48:03 +0200 Subject: [PATCH 62/88] small ai logic fix --- src/battle_ai_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index de64913fac..2aedc32ee8 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1372,7 +1372,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_AURORA_VEIL: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) - || !(gBattleWeather & B_WEATHER_HAIL && gBattleWeather & B_WEATHER_SNOW)) + || !(gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) score -= 10; break; case EFFECT_OHKO: From c5a73f59a4a34949c8c17185eaf29ef76cbdd7d1 Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Tue, 2 May 2023 21:17:14 +0200 Subject: [PATCH 63/88] ad suggested changes --- data/battle_anim_scripts.s | 27 ++++++++++++------------- data/battle_scripts_1.s | 2 +- include/constants/battle_move_effects.h | 2 +- src/battle_ai_main.c | 10 ++++----- src/battle_ai_util.c | 4 ++-- src/battle_message.c | 4 ++-- src/battle_script_commands.c | 2 +- src/battle_tv.c | 2 +- src/data/battle_moves.h | 2 +- test/ability_snow_warning.c | 6 +++++- test/weather_snow.c | 10 ++++----- 11 files changed, 36 insertions(+), 35 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 01311c35d1..a0b035704d 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -947,7 +947,7 @@ gBattleAnims_General:: .4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP .4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE .4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON - .4byte General_Snow @ B_ANIM_SNOW_CONTINUES + .4byte General_Snow @ B_ANIM_SNOW_CONTINUES .align 2 gBattleAnims_Special:: @@ -14386,6 +14386,18 @@ Move_SILK_TRAP:: clearmonbg ANIM_ATK_PARTNER end +@ Also used by Snow weather. Currently identical with Move_HAIL +Move_SNOWSCAPE:: + loadspritegfx ANIM_TAG_HAIL + loadspritegfx ANIM_TAG_ICE_CRYSTALS + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 6, RGB_BLACK + waitforvisualfinish + createvisualtask AnimTask_Hail, 5 + loopsewithpan SE_M_HAIL, 0, 8, 10 + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 6, 0, RGB_BLACK + end + Move_WICKED_BLOW:: Move_SURGING_STRIKES:: Move_THUNDER_CAGE:: @@ -14449,19 +14461,6 @@ Move_ELECTRO_DRIFT:: Move_SHED_TAIL:: Move_CHILLY_RECEPTION:: Move_TIDY_UP:: - -@ Also used by Snow weather. Currently identical with Move_HAIL -Move_SNOWSCAPE: - loadspritegfx ANIM_TAG_HAIL - loadspritegfx ANIM_TAG_ICE_CRYSTALS - createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 0, 6, RGB_BLACK - waitforvisualfinish - createvisualtask AnimTask_Hail, 5 - loopsewithpan SE_M_HAIL, 0, 8, 10 - waitforvisualfinish - createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 3, 6, 0, RGB_BLACK - end - Move_POUNCE:: Move_TRAILBLAZE:: Move_CHILLING_WATER:: diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d1795dcf55..90459c5ba8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -429,7 +429,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE .4byte BattleScript_EffectRevivalBlessing @ EFFECT_REVIVAL_BLESSING .4byte BattleScript_EffectFrostbiteHit @ EFFECT_FROSTBITE_HIT - .4byte BattleScript_EffectSnow @ EFFECT_SNOW + .4byte BattleScript_EffectSnow @ EFFECT_SNOWSCAPE BattleScript_EffectRevivalBlessing:: attackcanceler diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index bcc077055e..4d8330498d 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -406,7 +406,7 @@ #define EFFECT_BARB_BARRAGE 400 #define EFFECT_REVIVAL_BLESSING 401 #define EFFECT_FROSTBITE_HIT 402 -#define EFFECT_SNOW 403 +#define EFFECT_SNOWSCAPE 403 #define NUM_BATTLE_MOVE_EFFECTS 404 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2aedc32ee8..1fd07ef79f 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1582,7 +1582,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; - case EFFECT_SNOW: + case EFFECT_SNOWSCAPE: if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) // should snow be discouraged if hail is up? || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; @@ -2808,7 +2808,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) RETURN_SCORE_PLUS(2); // our partner benefits from hail } break; - case EFFECT_SNOW: + case EFFECT_SNOWSCAPE: if (IsBattlerAlive(battlerAtkPartner) && ShouldSetSnow(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect)) { @@ -3977,7 +3977,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; } break; - case EFFECT_SNOW: // any other reasons? + case EFFECT_SNOWSCAPE: // any other reasons? if (ShouldSetSnow(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL)) @@ -4976,7 +4976,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: case EFFECT_HAIL: - case EFFECT_SNOW: + case EFFECT_SNOWSCAPE: case EFFECT_GEOMANCY: case EFFECT_VICTORY_DANCE: case EFFECT_HIT_SET_ENTRY_HAZARD: @@ -5203,7 +5203,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: case EFFECT_HAIL: - case EFFECT_SNOW: + case EFFECT_SNOWSCAPE: case EFFECT_RAIN_DANCE: score -= 2; break; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 010c5a01c9..18acf15cc2 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -331,7 +331,7 @@ static const u16 sEncouragedEncoreEffects[] = EFFECT_SPIT_UP, EFFECT_SWALLOW, EFFECT_HAIL, - EFFECT_SNOW, + EFFECT_SNOWSCAPE, EFFECT_TORMENT, EFFECT_WILL_O_WISP, EFFECT_FOLLOW_ME, @@ -3264,7 +3264,7 @@ bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove) || gBattleMoves[partnerMove].effect == EFFECT_RAIN_DANCE || gBattleMoves[partnerMove].effect == EFFECT_SANDSTORM || gBattleMoves[partnerMove].effect == EFFECT_HAIL - || gBattleMoves[partnerMove].effect == EFFECT_SNOW)) + || gBattleMoves[partnerMove].effect == EFFECT_SNOWSCAPE)) return TRUE; return FALSE; diff --git a/src/battle_message.c b/src/battle_message.c index 66bc4a7737..e2a8eb0cb6 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -363,10 +363,10 @@ static const u8 sText_SunlightGotBright[] = _("The sunlight got bright!"); static const u8 sText_SunlightStrong[] = _("The sunlight is strong."); static const u8 sText_SunlightFaded[] = _("The sunlight faded."); static const u8 sText_StartedHail[] = _("It started to hail!"); -static const u8 sText_StartedSnow[] = _("It started to snow!"); static const u8 sText_HailContinues[] = _("Hail continues to fall."); -static const u8 sText_SnowContinues[] = _("Snow continues to fall."); static const u8 sText_HailStopped[] = _("The hail stopped."); +static const u8 sText_StartedSnow[] = _("It started to snow!"); +static const u8 sText_SnowContinues[] = _("Snow continues to fall."); static const u8 sText_SnowStopped[] = _("The snow stopped."); static const u8 sText_FailedToSpitUp[] = _("But it failed to spit up\na thing!"); static const u8 sText_FailedToSwallow[] = _("But it failed to swallow\na thing!"); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3bc77e8485..847ddb9aa9 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16577,4 +16577,4 @@ void BS_SetSnow(void) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW; } gBattlescriptCurrInstr = cmd->nextInstr; -} \ No newline at end of file +} diff --git a/src/battle_tv.c b/src/battle_tv.c index 74f8c7008c..7379b72e4b 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -251,7 +251,7 @@ static const u16 sPoints_MoveEffect[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SWALLOW] = 3, // [EFFECT_UNUSED_A3] = 1, [EFFECT_HAIL] = 4, - [EFFECT_SNOW] = 4, + [EFFECT_SNOWSCAPE] = 4, [EFFECT_TORMENT] = 7, [EFFECT_FLATTER] = 7, [EFFECT_WILL_O_WISP] = 5, diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 6d88acb8cd..522895992e 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -13356,7 +13356,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_SNOWSCAPE] = { - .effect = EFFECT_SNOW, + .effect = EFFECT_SNOWSCAPE, .power = 0, .type = TYPE_ICE, .accuracy = 0, diff --git a/test/ability_snow_warning.c b/test/ability_snow_warning.c index 6c0753ad39..200a14acaf 100644 --- a/test/ability_snow_warning.c +++ b/test/ability_snow_warning.c @@ -1,7 +1,11 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Snow Warning summons hail(snow)") +#if B_SNOW_WARNING < GEN_9 +SINGLE_BATTLE_TEST("Snow Warning summons hail") +#elif B_SNOW_WARNING >= GEN_9 +SINGLE_BATTLE_TEST("Snow Warning summons snow") +#endif { GIVEN { PLAYER(SPECIES_ABOMASNOW) { Ability(ABILITY_SNOW_WARNING); }; diff --git a/test/weather_snow.c b/test/weather_snow.c index 07afa48d57..d313c921bb 100644 --- a/test/weather_snow.c +++ b/test/weather_snow.c @@ -6,11 +6,9 @@ ASSUMPTIONS { - ASSUME(gBattleMoves[MOVE_SNOWSCAPE].effect == EFFECT_SNOW); - ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC); - ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC); - ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE); - ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[1] == TYPE_ICE); + ASSUME(gBattleMoves[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_ICE && gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_ICE); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE || gSpeciesInfo[SPECIES_GLALIE].types[1] == TYPE_ICE); } SINGLE_BATTLE_TEST("Snow increases the defense of Ice types by 50 %", s16 damage) @@ -156,4 +154,4 @@ SINGLE_BATTLE_TEST("Snow causes Morning Sun to recover 1/4 of the user's max HP" } #undef MAX_HP -#undef TEST_HP \ No newline at end of file +#undef TEST_HP From 74f074f5f39da88848cf80a6bfd3463f7fad3a17 Mon Sep 17 00:00:00 2001 From: CallmeEchoo <65783283+CallmeEchoo@users.noreply.github.com> Date: Wed, 3 May 2023 16:26:41 +0200 Subject: [PATCH 64/88] ai scoring for snow and hail relation we generally dont want to ecourage to use snow when hail is up and vice versa to avoid looping between the two weathers if score adds up. so we return false in the ShouldSetX functions if either weather is up We also reduce score by a little as further disincentive to loop and since both effect are so similar that switching between them is usually not a good idea --- src/battle_ai_main.c | 12 ++++++++---- src/battle_ai_util.c | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 1fd07ef79f..76bf2261e6 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -280,7 +280,7 @@ void Ai_InitPartyStruct(void) { if (GetMonData(&gPlayerParty[i], MON_DATA_HP) == 0) AI_PARTY->mons[B_SIDE_PLAYER][i].isFainted = TRUE; - + if (isOmniscient) { u32 j; @@ -1578,14 +1578,18 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 8; break; case EFFECT_HAIL: - if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) // should hail be discouraged if snow is up? + if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; + else if (gBattleWeather & B_WEATHER_SNOW) + score -= 2; // mainly to prevent looping between hail and snow break; case EFFECT_SNOWSCAPE: - if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) // should snow be discouraged if hail is up? + if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; + else if (gBattleWeather & B_WEATHER_HAIL) + score -= 2; // mainly to prevent looping between hail and snow break; case EFFECT_ATTRACT: if (!AI_CanBeInfatuated(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], @@ -3977,7 +3981,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; } break; - case EFFECT_SNOWSCAPE: // any other reasons? + case EFFECT_SNOWSCAPE: if (ShouldSetSnow(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL)) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 02af845344..13d70f28f2 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1580,7 +1580,7 @@ bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect) { if (!AI_WeatherHasEffect()) return FALSE; - else if (gBattleWeather & B_WEATHER_HAIL) // dont set hail if snow is up? + else if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) return FALSE; if (ability == ABILITY_SNOW_CLOAK @@ -1650,11 +1650,11 @@ bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect) return FALSE; } -bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect) // any other reasons snow should be set? +bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect) { if (!AI_WeatherHasEffect()) return FALSE; - else if (gBattleWeather & B_WEATHER_SNOW) // dont set snow if hail is up? + else if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_HAIL)) return FALSE; if (ability == ABILITY_SNOW_CLOAK From eb5b81684c4409ed00be42b1a29b3f599a7036ba Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 5 May 2023 01:38:12 +0200 Subject: [PATCH 65/88] refactored snow into damaging weather this is necessary for ice body to properly work. doesnt actually do any damage --- data/battle_scripts_1.s | 14 +++----------- include/battle_scripts.h | 2 +- include/constants/battle_string_ids.h | 7 ++----- src/battle_message.c | 12 ++++-------- src/battle_script_commands.c | 14 ++++++++++++++ src/battle_util.c | 19 +++++++++---------- src/data/text/abilities.h | 2 +- 7 files changed, 34 insertions(+), 36 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 7e1811c0b9..256db2a1ef 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6851,7 +6851,7 @@ BattleScript_RainContinuesOrEndsEnd:: end2 BattleScript_DamagingWeatherContinues:: - printfromtable gSandStormHailContinuesStringIds + printfromtable gSandStormHailSnowContinuesStringIds waitmessage B_WAIT_TIME_LONG playanimation_var BS_ATTACKER, sB_ANIM_ARG1 setbyte gBattleCommunication, 0 @@ -6883,16 +6883,8 @@ BattleScript_DamagingWeatherContinuesEnd:: bicword gHitMarker, HITMARKER_SKIP_DMG_TRACK | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE | HITMARKER_GRUDGE end2 -BattleScript_SnowContinuesOrEnds:: - printfromtable gSnowContinuesStringIds - waitmessage B_WAIT_TIME_LONG - jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SNOW_STOPPED, BattleScript_SnowContinuesOrEndsEnd - playanimation BS_ATTACKER, B_ANIM_SNOW_CONTINUES -BattleScript_SnowContinuesOrEndsEnd:: - end2 - -BattleScript_SandStormHailEnds:: - printfromtable gSandStormHailEndStringIds +BattleScript_SandStormHailSnowEnds:: + printfromtable gSandStormHailSnowEndStringIds waitmessage B_WAIT_TIME_LONG end2 diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 68b3a0c965..320743694b 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -45,7 +45,7 @@ extern const u8 BattleScript_LevelUp[]; extern const u8 BattleScript_RainContinuesOrEnds[]; extern const u8 BattleScript_SnowContinuesOrEnds[]; extern const u8 BattleScript_DamagingWeatherContinues[]; -extern const u8 BattleScript_SandStormHailEnds[]; +extern const u8 BattleScript_SandStormHailSnowEnds[]; extern const u8 BattleScript_SunlightContinues[]; extern const u8 BattleScript_SunlightFaded[]; extern const u8 BattleScript_OverworldWeatherStarts[]; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 4946f74e77..ddf1be7736 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -737,13 +737,10 @@ #define B_MSG_DOWNPOUR_CONTINUES 1 #define B_MSG_RAIN_STOPPED 2 -// gSnowContinuesStringIds -#define B_MSG_SNOW_CONTINUES 0 -#define B_MSG_SNOW_STOPPED 1 - -// gSandStormHailContinuesStringIds / gSandStormHailDmgStringIds/ gSandStormHailEndStringIds +// gSandStormHailSnowContinuesStringIds / gSandStormHailDmgStringIds/ gSandStormHailSnowEndStringIds #define B_MSG_SANDSTORM 0 #define B_MSG_HAIL 1 +#define B_MSG_SNOW 2 // gReflectLightScreenSafeguardStringIds #define B_MSG_SIDE_STATUS_FAILED 0 diff --git a/src/battle_message.c b/src/battle_message.c index e2a8eb0cb6..472cdf4d7f 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -1568,10 +1568,11 @@ const u16 gMoveWeatherChangeStringIds[] = [B_MSG_STARTED_SNOW] = STRINGID_STARTEDSNOW, }; -const u16 gSandStormHailContinuesStringIds[] = +const u16 gSandStormHailSnowContinuesStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMRAGES, [B_MSG_HAIL] = STRINGID_HAILCONTINUES, + [B_MSG_SNOW] = STRINGID_SNOWCONTINUES, }; const u16 gSandStormHailDmgStringIds[] = @@ -1580,10 +1581,11 @@ const u16 gSandStormHailDmgStringIds[] = [B_MSG_HAIL] = STRINGID_PKMNPELTEDBYHAIL }; -const u16 gSandStormHailEndStringIds[] = +const u16 gSandStormHailSnowEndStringIds[] = { [B_MSG_SANDSTORM] = STRINGID_SANDSTORMSUBSIDED, [B_MSG_HAIL] = STRINGID_HAILSTOPPED, + [B_MSG_SNOW] = STRINGID_SNOWSTOPPED, }; const u16 gRainContinuesStringIds[] = @@ -1593,12 +1595,6 @@ const u16 gRainContinuesStringIds[] = [B_MSG_RAIN_STOPPED] = STRINGID_RAINSTOPPED }; -const u16 gSnowContinuesStringIds[] = -{ - [B_MSG_SNOW_CONTINUES] = STRINGID_SNOWCONTINUES, - [B_MSG_SNOW_STOPPED] = STRINGID_SNOWSTOPPED, -}; - const u16 gProtectLikeUsedStringIds[] = { [B_MSG_PROTECTED_ITSELF] = STRINGID_PKMNPROTECTEDITSELF2, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 847ddb9aa9..553b4ff97c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12769,6 +12769,20 @@ static void Cmd_weatherdamage(void) gBattleMoveDamage = 1; } } + if (gBattleWeather & B_WEATHER_SNOW) + { + if (ability == ABILITY_ICE_BODY + && !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) + && !BATTLER_MAX_HP(gBattlerAttacker) + && !(gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK)) + { + gBattlerAbility = gBattlerAttacker; + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + } + } } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 37fc3d81b4..5d13461cf9 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2341,7 +2341,7 @@ u8 DoFieldEndTurnEffects(void) if (!(gBattleWeather & B_WEATHER_SANDSTORM_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~B_WEATHER_SANDSTORM_TEMPORARY; - gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; + gBattlescriptCurrInstr = BattleScript_SandStormHailSnowEnds; } else { @@ -2381,7 +2381,7 @@ u8 DoFieldEndTurnEffects(void) if (!(gBattleWeather & B_WEATHER_HAIL_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~B_WEATHER_HAIL_TEMPORARY; - gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; + gBattlescriptCurrInstr = BattleScript_SandStormHailSnowEnds; } else { @@ -2398,20 +2398,19 @@ u8 DoFieldEndTurnEffects(void) case ENDTURN_SNOW: if (gBattleWeather & B_WEATHER_SNOW) { - if (!(gBattleWeather & B_WEATHER_SNOW_PERMANENT)) + if (!(gBattleWeather & B_WEATHER_SNOW_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { - if (--gWishFutureKnock.weatherDuration == 0) - { - gBattleWeather &= ~B_WEATHER_SNOW_TEMPORARY; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW_STOPPED; - } + gBattleWeather &= ~B_WEATHER_SNOW_TEMPORARY; + gBattlescriptCurrInstr = BattleScript_SandStormHailSnowEnds; } else { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW_CONTINUES; + gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } - BattleScriptExecute(BattleScript_SnowContinuesOrEnds); + gBattleScripting.animArg1 = B_ANIM_SNOW_CONTINUES; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SNOW; + BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnCountersTracker++; diff --git a/src/data/text/abilities.h b/src/data/text/abilities.h index 96407c3409..368b0cb4dc 100644 --- a/src/data/text/abilities.h +++ b/src/data/text/abilities.h @@ -113,7 +113,7 @@ static const u8 sFilterDescription[] = _("Weakens “supereffective”."); static const u8 sSlowStartDescription[] = _("Takes a while to get going."); static const u8 sScrappyDescription[] = _("Hits Ghost-type Pokémon."); static const u8 sStormDrainDescription[] = _("Draws in Water moves."); -static const u8 sIceBodyDescription[] = _("Slight HP recovery in Hail."); +static const u8 sIceBodyDescription[] = _("HP recovery in Hail or Snow."); static const u8 sSnowWarningDescription[] = _("Summons a hailstorm."); static const u8 sHoneyGatherDescription[] = _("May gather Honey."); static const u8 sFriskDescription[] = _("Checks a foe's item."); From 488cc410a000b6096132b5d90ee345e3dbff406e Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Fri, 5 May 2023 10:24:12 +0200 Subject: [PATCH 66/88] update ability descriptions --- src/data/text/abilities.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/data/text/abilities.h b/src/data/text/abilities.h index 368b0cb4dc..a3bad09b1b 100644 --- a/src/data/text/abilities.h +++ b/src/data/text/abilities.h @@ -79,7 +79,7 @@ static const u8 sTangledFeetDescription[] = _("Ups evasion if confused."); static const u8 sMotorDriveDescription[] = _("Electricity raises Speed."); static const u8 sRivalryDescription[] = _("Powers up against rivals."); static const u8 sSteadfastDescription[] = _("Flinching raises Speed."); -static const u8 sSnowCloakDescription[] = _("Ups evasion in Hail."); +static const u8 sSnowCloakDescription[] = _("Ups evasion in Hail or Snow."); static const u8 sGluttonyDescription[] = _("Eats Berries early."); static const u8 sAngerPointDescription[] = _("Critical hits raise Attack."); static const u8 sUnburdenDescription[] = _("Using a hold item ups Speed."); @@ -114,7 +114,11 @@ static const u8 sSlowStartDescription[] = _("Takes a while to get going."); static const u8 sScrappyDescription[] = _("Hits Ghost-type Pokémon."); static const u8 sStormDrainDescription[] = _("Draws in Water moves."); static const u8 sIceBodyDescription[] = _("HP recovery in Hail or Snow."); -static const u8 sSnowWarningDescription[] = _("Summons a hailstorm."); +#if B_SNOW_WARNING < GEN_9 +static const u8 sSnowWarningDescription[] = _("Summons a Hailstorm."); +#elif B_SNOW_WARNING >= GEN_9 +static const u8 sSnowWarningDescription[] = _("Summons a Snowstorm."); +#endif static const u8 sHoneyGatherDescription[] = _("May gather Honey."); static const u8 sFriskDescription[] = _("Checks a foe's item."); static const u8 sRecklessDescription[] = _("Boosts moves with recoil."); @@ -192,7 +196,7 @@ static const u8 sStakeoutDescription[] = _("Stronger as foes switch in."); static const u8 sWaterBubbleDescription[] = _("Guards from fire and burns."); static const u8 sSteelworkerDescription[] = _("Powers up Steel moves."); static const u8 sBerserkDescription[] = _("Boosts Sp. Atk at low HP."); -static const u8 sSlushRushDescription[] = _("Raises Speed in hail."); +static const u8 sSlushRushDescription[] = _("Raises Speed in Hail or Snow."); static const u8 sLongReachDescription[] = _("Never makes contact."); static const u8 sLiquidVoiceDescription[] = _("Makes sound moves Water."); static const u8 sTriageDescription[] = _("Healing moves go first."); @@ -234,7 +238,7 @@ static const u8 sPunkRockDescription[] = _("Ups and resists sound."); static const u8 sSandSpitDescription[] = _("Creates a sandstorm if hit."); static const u8 sIceScalesDescription[] = _("Halves special damage."); static const u8 sRipenDescription[] = _("Doubles effect of Berries."); -static const u8 sIceFaceDescription[] = _("Take a free hit. Hail renews."); +static const u8 sIceFaceDescription[] = _("Hail or Snow renew free hit."); static const u8 sPowerSpotDescription[] = _("Powers up ally moves."); static const u8 sMimicryDescription[] = _("Changes type on terrain."); static const u8 sScreenCleanerDescription[] = _("Removes walls of light."); From 7e7384ee0225ccd470a3724186ad01f2e2437b8d Mon Sep 17 00:00:00 2001 From: CallmeEchoo Date: Sat, 6 May 2023 19:12:49 +0200 Subject: [PATCH 67/88] tiny test doc fix --- test/test_battle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_battle.h b/test/test_battle.h index 432e33fa80..81cb7e5943 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -369,7 +369,7 @@ * s16 damage; * HP_BAR(player, captureDamage: &damage); * If none of the above are used, causes the test to fail if the HP - * changes at all. + * does not change at all. * * MESSAGE(pattern) * Causes the test to fail if the message in pattern is not displayed. From e0bed6776b215e96f45799f685af1aaad9bc5408 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 26 Apr 2023 11:12:41 +0100 Subject: [PATCH 68/88] INVALID if FINALLY without PARAMETRIZE --- test/ability_volt_absorb.c | 3 +-- test/item_effect_restore_hp.c | 2 +- test/move_effect_bide.c | 4 ++-- test/move_effect_triple_kick.c | 8 ++++---- test/move_flag_three_strikes.c | 4 ++-- test/terrain_electric.c | 2 +- test/terrain_grassy.c | 2 +- test/terrain_misty.c | 2 +- test/terrain_psychic.c | 2 +- test/test_battle.h | 4 +++- test/test_runner_battle.c | 9 ++++++++- 11 files changed, 25 insertions(+), 17 deletions(-) diff --git a/test/ability_volt_absorb.c b/test/ability_volt_absorb.c index 18902eaa20..8a3c21ef6a 100644 --- a/test/ability_volt_absorb.c +++ b/test/ability_volt_absorb.c @@ -80,8 +80,7 @@ DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from dama MESSAGE("Jolteon restored HP using its Volt Absorb!"); HP_BAR(playerRight, captureDamage: &results->damage1); HP_BAR(opponentRight, captureDamage: &results->damage2); - } - FINALLY { + } THEN { EXPECT_NE(results[0].damage1, 0); EXPECT_NE(results[0].damage2, 0); } diff --git a/test/item_effect_restore_hp.c b/test/item_effect_restore_hp.c index 4662e9f25c..6fd58fe824 100644 --- a/test/item_effect_restore_hp.c +++ b/test/item_effect_restore_hp.c @@ -183,4 +183,4 @@ SINGLE_BATTLE_TEST("Sitrus Berry restores a battler's HP by 25% of its max HP(30 } #undef TEST_HP -#undef MAX_HP \ No newline at end of file +#undef MAX_HP diff --git a/test/move_effect_bide.c b/test/move_effect_bide.c index 3a5c387353..0b0e184087 100644 --- a/test/move_effect_bide.c +++ b/test/move_effect_bide.c @@ -28,7 +28,7 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns") MESSAGE("Wobbuffet unleashed energy!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player); HP_BAR(opponent, captureDamage: &bideDamage); - } FINALLY { - EXPECT_EQ(bideDamage, damage1 + damage2); + } THEN { + EXPECT_EQ(bideDamage, 2 * (damage1 + damage2)); } } diff --git a/test/move_effect_triple_kick.c b/test/move_effect_triple_kick.c index d3187aefec..e0a91b0112 100644 --- a/test/move_effect_triple_kick.c +++ b/test/move_effect_triple_kick.c @@ -6,7 +6,7 @@ ASSUMPTIONS ASSUME(gBattleMoves[MOVE_TRIPLE_KICK].effect & EFFECT_TRIPLE_KICK); } -SINGLE_BATTLE_TEST("Triple Kick damage is increaased by its base damage for each hit") +SINGLE_BATTLE_TEST("Triple Kick damage is increased by its base damage for each hit") { s16 firstHit; s16 secondHit; @@ -24,8 +24,8 @@ SINGLE_BATTLE_TEST("Triple Kick damage is increaased by its base damage for each HP_BAR(opponent, captureDamage: &secondHit); ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_KICK, player); HP_BAR(opponent, captureDamage: &thirdHit); - } FINALLY { - EXPECT_EQ(secondHit, firstHit * 2); - EXPECT_EQ(thirdHit, firstHit * 3); + } THEN { + EXPECT_MUL_EQ(firstHit, Q_4_12(2.0), secondHit); + EXPECT_MUL_EQ(firstHit, Q_4_12(3.0), thirdHit); } } diff --git a/test/move_flag_three_strikes.c b/test/move_flag_three_strikes.c index 7ea7844c4f..b597b4efef 100644 --- a/test/move_flag_three_strikes.c +++ b/test/move_flag_three_strikes.c @@ -20,7 +20,7 @@ SINGLE_BATTLE_TEST("Three-strike flag turns a move into a 3-hit move") HP_BAR(opponent, captureDamage: &secondHit); ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_DIVE, player); HP_BAR(opponent, captureDamage: &thirdHit); - } FINALLY { + } THEN { EXPECT_EQ(firstHit, secondHit); EXPECT_EQ(secondHit, thirdHit); EXPECT_EQ(firstHit, thirdHit); @@ -49,7 +49,7 @@ SINGLE_BATTLE_TEST("Surging Strikes hits 3 times with each hit being a critical ANIMATION(ANIM_TYPE_MOVE, MOVE_SURGING_STRIKES, player); HP_BAR(opponent, captureDamage: &thirdHit); MESSAGE("A critical hit!"); - } FINALLY { + } THEN { EXPECT_EQ(firstHit, secondHit); EXPECT_EQ(secondHit, thirdHit); EXPECT_EQ(firstHit, thirdHit); diff --git a/test/terrain_electric.c b/test/terrain_electric.c index e9c34ef923..78f490c425 100644 --- a/test/terrain_electric.c +++ b/test/terrain_electric.c @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Electric Terrain activates Electric Seed and Mimicry") MESSAGE("Using Electric Seed, the Defense of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Electr!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_ELECTRIC); } } diff --git a/test/terrain_grassy.c b/test/terrain_grassy.c index 7668c49bde..21d64f61e4 100644 --- a/test/terrain_grassy.c +++ b/test/terrain_grassy.c @@ -30,7 +30,7 @@ SINGLE_BATTLE_TEST("Grassy Terrain activates Grassy Seed and Mimicry") MESSAGE("Using Grassy Seed, the Defense of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Grass!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_GRASS); } } diff --git a/test/terrain_misty.c b/test/terrain_misty.c index 4f8793d9d0..f60122075c 100644 --- a/test/terrain_misty.c +++ b/test/terrain_misty.c @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Misty Terrain activates Misty Seed and Mimicry") MESSAGE("Using Misty Seed, the Sp. Def of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Fairy!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_FAIRY); } } diff --git a/test/terrain_psychic.c b/test/terrain_psychic.c index 3ecfa99e06..f7e3a2af73 100644 --- a/test/terrain_psychic.c +++ b/test/terrain_psychic.c @@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain activates Psychic Seed and Mimicry") MESSAGE("Using Psychic Seed, the Sp. Def of Wobbuffet rose!"); ABILITY_POPUP(opponent); MESSAGE("Foe Stunfisk's type changed to Psychc!"); - } FINALLY { + } THEN { EXPECT_EQ(gBattleMons[B_POSITION_OPPONENT_LEFT].type1, TYPE_PSYCHIC); } } diff --git a/test/test_battle.h b/test/test_battle.h index 4c563c91d6..205c699192 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -897,7 +897,9 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve /* Finally */ -#define FINALLY for (; gBattleTestRunnerState->runFinally; gBattleTestRunnerState->runFinally = FALSE) if ((gBattleTestRunnerState->runningFinally = TRUE)) +#define FINALLY for (ValidateFinally(__LINE__); gBattleTestRunnerState->runFinally; gBattleTestRunnerState->runFinally = FALSE) if ((gBattleTestRunnerState->runningFinally = TRUE)) + +void ValidateFinally(u32 sourceLine); /* Expect */ diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 7740783b2c..2c26e39180 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1719,7 +1719,6 @@ void QueueMessage(u32 sourceLine, const u8 *pattern) }; } - void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEventContext ctx) { s32 battlerId = battler - gBattleMons; @@ -1759,3 +1758,11 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve }}, }; } + +void ValidateFinally(u32 sourceLine) +{ + // Defer this error until after estimating the cost. + if (STATE->results == NULL) + return; + INVALID_IF(STATE->parametersCount == 0, "FINALLY without PARAMETRIZE"); +} From 9cd13cc52024fbdaca2e1515e47d354084371ae4 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 20 Apr 2023 21:45:16 +0100 Subject: [PATCH 69/88] Fix USE_ITEM explicit targets --- include/battle.h | 1 + src/battle_controller_recorded_opponent.c | 2 +- src/battle_controller_recorded_player.c | 2 +- src/battle_script_commands.c | 6 +++--- src/party_menu.c | 2 +- test/test_runner_battle.c | 4 ++++ 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/include/battle.h b/include/battle.h index 466a9138d5..00c7081fb8 100644 --- a/include/battle.h +++ b/include/battle.h @@ -658,6 +658,7 @@ struct BattleStruct u8 storedLunarDance:4; // Each battler as a bit. u16 supremeOverlordModifier[MAX_BATTLERS_COUNT]; u8 itemPartyIndex[MAX_BATTLERS_COUNT]; + u8 itemMoveIndex[MAX_BATTLERS_COUNT]; bool8 trainerSlideHalfHpMsgDone; u8 trainerSlideFirstCriticalHitMsgState:2; u8 trainerSlideFirstSuperEffectiveHitMsgState:2; diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 7515840761..d1c60343ac 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -1443,7 +1443,7 @@ static void RecordedOpponentHandleChooseItem(void) u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); - gChosenMovePos = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); + gBattleStruct->itemMoveIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedOpponentBufferExecCompleted(); } diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 1a56220d7f..46f3de4f12 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -1467,7 +1467,7 @@ static void RecordedPlayerHandleChooseItem(void) u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, gActiveBattler); gBattleStruct->chosenItem[gActiveBattler] = (byte1 << 8) | byte2; gBattleStruct->itemPartyIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_TARGET, gActiveBattler); - gChosenMovePos = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); + gBattleStruct->itemMoveIndex[gActiveBattler] = RecordedBattle_GetBattlerAction(RECORDED_ITEM_MOVE, gActiveBattler); BtlController_EmitOneReturnValue(BUFFER_B, gBattleStruct->chosenItem[gActiveBattler]); RecordedPlayerBufferExecCompleted(); } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1187267b2a..0842884311 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16536,10 +16536,10 @@ void BS_ItemRestorePP(void) { // Check whether to apply to all moves. if (effect[4] & ITEM4_HEAL_PP_ONE) { - i = gChosenMovePos; - loopEnd = gChosenMovePos + 1; + i = gBattleStruct->itemMoveIndex[gBattlerAttacker]; + loopEnd = i + 1; } - else + else { i = 0; loopEnd = MAX_MON_MOVES; diff --git a/src/party_menu.c b/src/party_menu.c index 773495a4d5..aa37b1c691 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4944,7 +4944,7 @@ static void TryUseItemOnMove(u8 taskId) else { gBattleStruct->itemPartyIndex[gBattlerInMenuId] = GetPartyIdFromBattleSlot(gPartyMenu.slotId); - gChosenMovePos = ptr->data1; + gBattleStruct->itemMoveIndex[gBattlerInMenuId] = ptr->data1; gPartyMenuUseExitCallback = TRUE; RemoveBagItem(gSpecialVar_ItemId, 1); ScheduleBgCopyTilemapToVram(2); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 2c26e39180..b7360e19c8 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1564,6 +1564,10 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c } INVALID_IF(i == MAX_MON_MOVES, "USE_ITEM on invalid move: %d", ctx.move); } + else + { + i = 0; + } PushBattlerAction(sourceLine, battlerId, RECORDED_ACTION_TYPE, B_ACTION_USE_ITEM); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, (ctx.itemId >> 8) & 0xFF); PushBattlerAction(sourceLine, battlerId, RECORDED_ITEM_ID, ctx.itemId & 0xFF); From 29fa6ba5743f2a1c636af0c7e3491f2f2e3bb81d Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 26 Apr 2023 11:31:19 +0100 Subject: [PATCH 70/88] INVALID if PASSES_RANDOMLY and results --- test/hold_effect_berserk_gene.c | 17 +++++------------ test/test_runner_battle.c | 2 ++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/test/hold_effect_berserk_gene.c b/test/hold_effect_berserk_gene.c index 361d148611..103a0a1a3e 100644 --- a/test/hold_effect_berserk_gene.c +++ b/test/hold_effect_berserk_gene.c @@ -11,14 +11,11 @@ SINGLE_BATTLE_TEST("Berserk Gene sharply raises attack at the start of battle", u16 useItem; PARAMETRIZE { useItem = FALSE; } PARAMETRIZE { useItem = TRUE; } - if (useItem) PASSES_RANDOMLY(66, 100, RNG_CONFUSION); GIVEN { - PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); }; + PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { - MOVE(player, MOVE_TACKLE); - } + TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_CONFUSION, FALSE)); } } SCENE { if (useItem) { @@ -38,17 +35,13 @@ SINGLE_BATTLE_TEST("Berserk Gene activates on switch in", s16 damage) u16 useItem; PARAMETRIZE { useItem = FALSE; } PARAMETRIZE { useItem = TRUE; } - if (useItem) PASSES_RANDOMLY(66, 100, RNG_CONFUSION); GIVEN { PLAYER(SPECIES_WYNAUT); - PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); }; + PLAYER(SPECIES_WOBBUFFET) { if (useItem) Item(ITEM_BERSERK_GENE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { - SWITCH(player, 1); - } TURN { - MOVE(player, MOVE_TACKLE); - } + TURN { SWITCH(player, 1); } + TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_CONFUSION, FALSE)); } } SCENE { if (useItem) { diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index b7360e19c8..c1ea34e8cb 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -939,6 +939,8 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx) { + const struct BattleTest *test = gTestRunnerState.test->data; + INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results"); INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials); STATE->rngTag = ctx.tag; STATE->runTrial = 0; From efab893c470eed58e41727852440f58c528b60bc Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Wed, 26 Apr 2023 11:38:52 +0100 Subject: [PATCH 71/88] INVALID if results without PARAMETRIZE --- test/ability_dry_skin.c | 5 ++++- test/ability_volt_absorb.c | 11 ++++++----- test/hold_effect_mirror_herb.c | 6 +++--- test/item_effect_increase_stat.c | 2 +- test/terrain_psychic.c | 10 +++++----- test/test_runner_battle.c | 2 ++ 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/test/ability_dry_skin.c b/test/ability_dry_skin.c index 6c28d0c428..855872d761 100644 --- a/test/ability_dry_skin.c +++ b/test/ability_dry_skin.c @@ -33,10 +33,13 @@ SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain") SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%", s16 damage) { + u32 ability; + PARAMETRIZE { ability = ABILITY_EFFECT_SPORE; } + PARAMETRIZE { ability = ABILITY_DRY_SKIN; } GIVEN { ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_PARASECT) { Ability(ABILITY_DRY_SKIN); }; + OPPONENT(SPECIES_PARASECT) { Ability(ability); }; } WHEN { TURN {MOVE(player, MOVE_EMBER); } } SCENE { diff --git a/test/ability_volt_absorb.c b/test/ability_volt_absorb.c index 8a3c21ef6a..6b9296268c 100644 --- a/test/ability_volt_absorb.c +++ b/test/ability_volt_absorb.c @@ -63,8 +63,9 @@ SINGLE_BATTLE_TEST("Volt Absorb is only triggered once on multi strike moves") } } -DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon", s16 damage1, s16 damage2) // Fixed issue #1961 +DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from damaging other pokemon") // Fixed issue #1961 { + s16 damage1, damage2; GIVEN { ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION); ASSUME(gBattleMoves[MOVE_EXPLOSION].type == TYPE_NORMAL); @@ -78,11 +79,11 @@ DOUBLE_BATTLE_TEST("Volt Absorb does not stop Electric Typed Explosion from dama ABILITY_POPUP(playerLeft, ABILITY_VOLT_ABSORB); HP_BAR(playerLeft, hp: TEST_MAX_HP / 4 + 1); MESSAGE("Jolteon restored HP using its Volt Absorb!"); - HP_BAR(playerRight, captureDamage: &results->damage1); - HP_BAR(opponentRight, captureDamage: &results->damage2); + HP_BAR(playerRight, captureDamage: &damage1); + HP_BAR(opponentRight, captureDamage: &damage2); } THEN { - EXPECT_NE(results[0].damage1, 0); - EXPECT_NE(results[0].damage2, 0); + EXPECT_NE(damage1, 0); + EXPECT_NE(damage2, 0); } } diff --git a/test/hold_effect_mirror_herb.c b/test/hold_effect_mirror_herb.c index fc0a6de957..22f564b850 100644 --- a/test/hold_effect_mirror_herb.c +++ b/test/hold_effect_mirror_herb.c @@ -9,8 +9,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 damage) { u32 item; - PARAMETRIZE{ item = ITEM_NONE; } - PARAMETRIZE{ item = ITEM_MIRROR_HERB; } + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_MIRROR_HERB; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { Speed(4); } OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Item(item); } @@ -34,7 +34,7 @@ SINGLE_BATTLE_TEST("Mirror Herb copies all of foe's stat changes in a turn", s16 } } -SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks", s16 damage) +SINGLE_BATTLE_TEST("Mirror Herb copies all of of Stuff Cheeks") { GIVEN { ASSUME(gItems[ITEM_LIECHI_BERRY].holdEffect == HOLD_EFFECT_ATTACK_UP); diff --git a/test/item_effect_increase_stat.c b/test/item_effect_increase_stat.c index bbaf20e2fa..310932040c 100644 --- a/test/item_effect_increase_stat.c +++ b/test/item_effect_increase_stat.c @@ -126,7 +126,7 @@ SINGLE_BATTLE_TEST("X Speed sharply raises battler's Speed stat", s16 damage) } } -SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat", s16 damage) +SINGLE_BATTLE_TEST("X Accuracy sharply raises battler's Accuracy stat") { ASSUME(gBattleMoves[MOVE_SING].accuracy == 55); diff --git a/test/terrain_psychic.c b/test/terrain_psychic.c index f7e3a2af73..f9f95a47db 100644 --- a/test/terrain_psychic.c +++ b/test/terrain_psychic.c @@ -61,7 +61,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain increases power of Psychic-type moves by 30/ } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user") { GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); HP(1); } @@ -76,7 +76,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers") { KNOWN_FAILING; GIVEN { @@ -91,7 +91,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents") { KNOWN_FAILING; GIVEN { @@ -106,7 +106,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all } } -DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target allies", s16 damage) +DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target allies") { GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } @@ -122,7 +122,7 @@ DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves", s16 damage) +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves") { KNOWN_FAILING; GIVEN { diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index c1ea34e8cb..cfe5db9d6e 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -134,6 +134,8 @@ static void BattleTest_SetUp(void *data) Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE = AllocZerod(%d)", sizeof(*STATE)); InvokeTestFunction(test); STATE->parameters = STATE->parametersCount; + if (STATE->parametersCount == 0 && test->resultsSize > 0) + Test_ExitWithResult(TEST_RESULT_INVALID, "results without PARAMETRIZE"); STATE->results = AllocZeroed(test->resultsSize * STATE->parameters); if (!STATE->results) Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE->results = AllocZerod(%d)", sizeof(test->resultsSize * STATE->parameters)); From a2ca2aa7d90c091aea4d72df81237fa75843e212 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 27 Apr 2023 09:25:04 +0100 Subject: [PATCH 72/88] Report errors in estimateCost --- test/test_runner.c | 88 ++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/test/test_runner.c b/test/test_runner.c index 64cb20e269..bfd7b47a56 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -42,7 +42,14 @@ static bool32 PrefixMatch(const char *pattern, const char *string) } } -enum { STATE_INIT, STATE_NEXT_TEST, STATE_REPORT_RESULT, STATE_EXIT }; +enum +{ + STATE_INIT, + STATE_NEXT_TEST, + STATE_RUN_TEST, + STATE_REPORT_RESULT, + STATE_EXIT, +}; void CB2_TestRunner(void) { @@ -81,6 +88,26 @@ void CB2_TestRunner(void) return; } + MgbaPrintf_(":N%s", gTestRunnerState.test->name); + gTestRunnerState.result = TEST_RESULT_PASS; + gTestRunnerState.expectedResult = TEST_RESULT_PASS; + gTestRunnerState.expectLeaks = FALSE; + if (gTestRunnerHeadless) + gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS; + else + gTestRunnerState.timeoutSeconds = UINT_MAX; + InitHeap(gHeap, HEAP_SIZE); + EnableInterrupts(INTR_FLAG_TIMER2); + REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second. + REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK; + + // NOTE: Assumes that the compiler interns __FILE__. + if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) + { + gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL; + return; + } + // Greedily assign tests to processes based on estimated cost. // TODO: Make processCosts a min heap. if (gTestRunnerState.test->runner != &gAssumptionsRunner) @@ -98,40 +125,26 @@ void CB2_TestRunner(void) } } + if (minCostProcess == gTestRunnerI) + gTestRunnerState.state = STATE_RUN_TEST; + else + gTestRunnerState.state = STATE_NEXT_TEST; + + // XXX: If estimateCost exits only on some processes then + // processCosts will be inconsistent. if (gTestRunnerState.test->runner->estimateCost) gTestRunnerState.processCosts[minCostProcess] += gTestRunnerState.test->runner->estimateCost(gTestRunnerState.test->data); else gTestRunnerState.processCosts[minCostProcess] += 1; - - if (minCostProcess != gTestRunnerI) - return; } - MgbaPrintf_(":N%s", gTestRunnerState.test->name); + break; + + case STATE_RUN_TEST: gTestRunnerState.state = STATE_REPORT_RESULT; - gTestRunnerState.result = TEST_RESULT_PASS; - gTestRunnerState.expectedResult = TEST_RESULT_PASS; - gTestRunnerState.expectLeaks = FALSE; - if (gTestRunnerHeadless) - gTestRunnerState.timeoutSeconds = TIMEOUT_SECONDS; - else - gTestRunnerState.timeoutSeconds = UINT_MAX; - InitHeap(gHeap, HEAP_SIZE); - EnableInterrupts(INTR_FLAG_TIMER2); - REG_TM2CNT_L = UINT16_MAX - (274 * 60); // Approx. 1 second. - REG_TM2CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE | TIMER_1024CLK; - - // NOTE: Assumes that the compiler interns __FILE__. - if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) - { - gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL; - } - else - { - if (gTestRunnerState.test->runner->setUp) - gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data); - gTestRunnerState.test->runner->run(gTestRunnerState.test->data); - } + if (gTestRunnerState.test->runner->setUp) + gTestRunnerState.test->runner->setUp(gTestRunnerState.test->data); + gTestRunnerState.test->runner->run(gTestRunnerState.test->data); break; case STATE_REPORT_RESULT: @@ -345,6 +358,8 @@ static void Intr_Timer2(void) } else { + if (gTestRunnerState.state == STATE_RUN_TEST) + gTestRunnerState.state = STATE_REPORT_RESULT; gTestRunnerState.result = TEST_RESULT_TIMEOUT; ReinitCallbacks(); IRQ_LR = ((uintptr_t)JumpToAgbMainLoop & ~1) + 4; @@ -354,16 +369,19 @@ static void Intr_Timer2(void) void Test_ExitWithResult(enum TestResult result, const char *fmt, ...) { - bool32 handled = FALSE; gTestRunnerState.result = result; ReinitCallbacks(); - if (gTestRunnerState.test->runner->handleExitWithResult) - handled = gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result); - if (!handled && gTestRunnerState.result != gTestRunnerState.expectedResult) + if (gTestRunnerState.state == STATE_REPORT_RESULT + && gTestRunnerState.test->runner->handleExitWithResult) { - va_list va; - va_start(va, fmt); - MgbaVPrintf_(fmt, va); + if (!gTestRunnerState.test->runner->handleExitWithResult(gTestRunnerState.test->data, result) + && gTestRunnerState.result != gTestRunnerState.expectedResult) + { + va_list va; + va_start(va, fmt); + MgbaVPrintf_(fmt, va); + va_end(va); + } } JumpToAgbMainLoop(); } From e52e8ce7e15f96f753f37af1f44e62ac1c90c52a Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Sun, 7 May 2023 07:50:29 +0100 Subject: [PATCH 73/88] Cleanup Compound Eyes tests --- test/ability_compound_eyes.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/ability_compound_eyes.c b/test/ability_compound_eyes.c index d90604d5c3..6bdb15fa78 100644 --- a/test/ability_compound_eyes.c +++ b/test/ability_compound_eyes.c @@ -6,7 +6,7 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") PASSES_RANDOMLY(91, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_THUNDER].accuracy == 70); - PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }; + PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_THUNDER); } @@ -16,16 +16,13 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy") } } -// This fails even though the ability works correctly. The failure is due to -// a statistical anomaly in the test system where FISSURE hits 3 times more often -// than we expect. SINGLE_BATTLE_TEST("Compound Eyes does not affect OHKO moves") { PASSES_RANDOMLY(30, 100, RNG_ACCURACY); GIVEN { ASSUME(gBattleMoves[MOVE_FISSURE].accuracy == 30); ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO); - PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); }; + PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_FISSURE); } From ebc8edd5a65c92b1211aacb3ac4efdbeffd7d207 Mon Sep 17 00:00:00 2001 From: Ultimate Bob Date: Tue, 9 May 2023 00:58:38 +1000 Subject: [PATCH 74/88] Fix shiny animation not respecting illusion mon. --- src/battle_anim_throw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/battle_anim_throw.c b/src/battle_anim_throw.c index 3fc2000e4a..4a3cf7cf27 100755 --- a/src/battle_anim_throw.c +++ b/src/battle_anim_throw.c @@ -2484,9 +2484,14 @@ void TryShinyAnimation(u8 battler, struct Pokemon *mon) u32 otId, personality; u32 shinyValue; u8 taskCirc, taskDgnl; + struct Pokemon* illusionMon; isShiny = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = TRUE; + illusionMon = GetIllusionMonPtr(battler); + if (illusionMon != NULL) + mon = illusionMon; + otId = GetMonData(mon, MON_DATA_OT_ID); personality = GetMonData(mon, MON_DATA_PERSONALITY); From f985eefaa139017c2d67b1ea1591bd3d36bb6af4 Mon Sep 17 00:00:00 2001 From: Ultimate Bob Date: Tue, 9 May 2023 01:40:28 +1000 Subject: [PATCH 75/88] Show Imposter abilty in popup instead of opponent's ability. --- data/battle_scripts_1.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index fdc7c8a3fd..b62eb465eb 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -9373,8 +9373,8 @@ BattleScript_FriskActivates:: end3 BattleScript_ImposterActivates:: - transformdataexecution call BattleScript_AbilityPopUp + transformdataexecution playmoveanimation BS_ATTACKER, MOVE_TRANSFORM waitanimation printstring STRINGID_IMPOSTERTRANSFORM From 64c2eb39c0f65632ed06b1b19bbf2fca4dc7b089 Mon Sep 17 00:00:00 2001 From: Ultimate Bob Date: Tue, 9 May 2023 02:33:12 +1000 Subject: [PATCH 76/88] Ignore Illusion if the pokemon is the last slot in the party. --- src/battle_util.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/battle_util.c b/src/battle_util.c index cd65d58093..dd6e2bc06e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10398,12 +10398,19 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId) { struct Pokemon *party, *partnerMon; s32 i, id; + u8 side, partyCount; gBattleStruct->illusion[battlerId].set = 1; if (GetMonAbility(mon) != ABILITY_ILLUSION) return FALSE; party = GetBattlerParty(battlerId); + side = GetBattlerSide(battlerId); + partyCount = side == B_SIDE_PLAYER ? gPlayerPartyCount : gEnemyPartyCount; + + // If this pokemon is last in the party, ignore Illusion. + if (&party[partyCount - 1] == mon) + return FALSE; if (IsBattlerAlive(BATTLE_PARTNER(battlerId))) partnerMon = &party[gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)]]; From 8c537ccd727946b81a204c9fe0147ec06c4b4578 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Wed, 10 May 2023 00:33:43 -0400 Subject: [PATCH 77/88] Colorize bag select button --- graphics/bag/select_button.png | Bin 135 -> 195 bytes src/item_menu.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics/bag/select_button.png b/graphics/bag/select_button.png index bded587fd1d071464de8d5b9032433a760c183cf..52cdc1ed5acb5ca9c9164e0db127cee7509eaa83 100644 GIT binary patch delta 162 zcmZo?Jj^&j(uJ9Uf#Dz5qGdqJAiyWYH7ThW$o&8R|JAE!_s)s>&%m%{%NC#r5WG9v z{Qv*iD2D2*S^vF&OrWwUTV54^+dENFIJk*j|L0V8xf8R(r!6z#np!2Sp19#G^J^gu zH?BPgwk+Uy>cPuBJy`F+_LMg7cU!~f{P9S=W@>l+*8F=9_P;pDYIxTptX1s_E65&C LS3j3^P6dXGLHcW-t6b~08)OQE{-7_GfVpn`3@LxF#k*mlRNPsKK0`R^C` Date: Thu, 11 May 2023 00:16:57 +0200 Subject: [PATCH 78/88] Remove duplicate CanBePoisoned condition (#2988) --- src/battle_util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index cd65d58093..e8c9b8d8cd 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6413,7 +6413,6 @@ bool32 CanBePoisoned(u8 battlerAttacker, u8 battlerTarget) || ability == ABILITY_IMMUNITY || ability == ABILITY_COMATOSE || IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL) - || gBattleMons[battlerTarget].status1 & STATUS1_ANY || IsAbilityStatusProtected(battlerTarget) || IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; From 292d460810f6a40550f6f3521374eb334243db12 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 12 May 2023 14:02:14 +0200 Subject: [PATCH 79/88] Fix ability select in debug givemon --- src/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debug.c b/src/debug.c index 3fb79d2e86..8fff84d57d 100644 --- a/src/debug.c +++ b/src/debug.c @@ -2327,7 +2327,7 @@ static void DebugAction_Give_Pokemon_SelectNature(u8 taskId) } static void DebugAction_Give_Pokemon_SelectAbility(u8 taskId) { - u8 abilityId; + u16 abilityId; u8 abilityCount = NUM_ABILITY_SLOTS - 1; //-1 for proper iteration u8 i = 0; From a89288e30e958add69295c910eb8b8f041add5c5 Mon Sep 17 00:00:00 2001 From: AaghatIsLive <109757010+AaghatIsLive@users.noreply.github.com> Date: Fri, 12 May 2023 17:54:47 +0530 Subject: [PATCH 80/88] Fix Illumise pallete (#2995) --- graphics/pokemon/illumise/normal.pal | 242 +-------------------------- 1 file changed, 1 insertion(+), 241 deletions(-) diff --git a/graphics/pokemon/illumise/normal.pal b/graphics/pokemon/illumise/normal.pal index b1f415775a..71e5ab84d8 100644 --- a/graphics/pokemon/illumise/normal.pal +++ b/graphics/pokemon/illumise/normal.pal @@ -1,6 +1,6 @@ JASC-PAL 0100 -256 +16 152 208 160 152 128 80 224 176 72 @@ -17,243 +17,3 @@ JASC-PAL 0 88 208 56 56 56 120 120 120 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 -0 0 0 From d09072fd0dc8da040956bf28cc94793100e56727 Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Sun, 14 May 2023 12:55:08 -0400 Subject: [PATCH 81/88] Updated Esper Wing's accuracy if B_UPDATED_MOVE_DATA is Gen9 (#3004) --- src/data/battle_moves.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index b30f62a684..ef6ad52a1c 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -12699,12 +12699,13 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = { #if B_UPDATED_MOVE_DATA >= GEN_9 .power = 80, + .accuracy = 100, #else .power = 75, + .accuracy = 90, #endif .effect = EFFECT_SPEED_UP_HIT, .type = TYPE_PSYCHIC, - .accuracy = 90, .pp = 10, .secondaryEffectChance = 100, .target = MOVE_TARGET_SELECTED, From f6f0e5fa33dfe3154195619da940642b2b3cb06c Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 15 May 2023 01:03:14 +0200 Subject: [PATCH 82/88] Fix for wrong mon position for scripted wild doubles (#2996) --- src/script_pokemon_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 34f75ef8e5..bfe2e7581a 100755 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -172,12 +172,12 @@ void CreateScriptedDoubleWildMon(u16 species1, u8 level1, u16 item1, u16 species SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem1); } - CreateMon(&gEnemyParty[3], species2, level2, 32, 0, 0, OT_ID_PLAYER_ID, 0); + CreateMon(&gEnemyParty[1], species2, level2, 32, 0, 0, OT_ID_PLAYER_ID, 0); if (item2) { heldItem2[0] = item2; heldItem2[1] = item2 >> 8; - SetMonData(&gEnemyParty[3], MON_DATA_HELD_ITEM, heldItem2); + SetMonData(&gEnemyParty[1], MON_DATA_HELD_ITEM, heldItem2); } } From 2420134e4532d87f44768a919229fe908a685c63 Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Sun, 14 May 2023 19:30:30 -0400 Subject: [PATCH 83/88] Updated Ability Patch for Gen 9 functionality (#2989) --- src/party_menu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/party_menu.c b/src/party_menu.c index aa37b1c691..27f4e0da42 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -4663,7 +4663,6 @@ void Task_AbilityPatch(u8 taskId) // Can't use. if (gSpeciesInfo[tSpecies].abilities[tAbilityNum] == 0 || !tSpecies - || GetMonData(&gPlayerParty[tMonId], MON_DATA_ABILITY_NUM, NULL) > 1 ) { gPartyMenuUseExitCallback = FALSE; @@ -4734,7 +4733,10 @@ void ItemUseCB_AbilityPatch(u8 taskId, TaskFunc task) tState = 0; tMonId = gPartyMenu.slotId; tSpecies = GetMonData(&gPlayerParty[tMonId], MON_DATA_SPECIES, NULL); - tAbilityNum = 2; + if (GetMonData(&gPlayerParty[tMonId], MON_DATA_ABILITY_NUM, NULL) == 2) + tAbilityNum = 0; + else + tAbilityNum = 2; SetWordTaskArg(taskId, tOldFunc, (uintptr_t)(gTasks[taskId].func)); gTasks[taskId].func = Task_AbilityPatch; } From 76379661d05525b3fef87f6a640f003744475515 Mon Sep 17 00:00:00 2001 From: Eclipse <115349505+SubzeroEclipse@users.noreply.github.com> Date: Mon, 15 May 2023 04:09:45 +0200 Subject: [PATCH 84/88] Fixed mons mot disobeying with Gen8 mechanics disabled (#2990) --- src/battle_util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index e8c9b8d8cd..5aa2fc153f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8114,8 +8114,6 @@ u8 IsMonDisobedient(void) if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) levelReferenced = gBattleMons[gBattlerAttacker].metLevel; else -#else - if (gBattleMons[gBattlerAttacker].level <= obedienceLevel) #endif levelReferenced = gBattleMons[gBattlerAttacker].level; From 6458cab70a2533263518a877013a230512dacd39 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 15 May 2023 15:07:09 -0400 Subject: [PATCH 85/88] use NATIVE_ARGS in various to callnative conversions --- src/battle_script_commands.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index eddf6d6e19..896b57ffcc 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8805,7 +8805,7 @@ static bool32 CanTeleport(u8 battlerId) { struct Pokemon *party = GetBattlerParty(battlerId); u32 species, count, i; - + for (i = 0; i < PARTY_SIZE; i++) { species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); @@ -16317,33 +16317,33 @@ static bool8 IsFinalStrikeEffect(u16 move) // 10 bytes long (callnative(5) + counter(1) + ptr(4)) void BS_CheckParentalBondCounter(void) { + NATIVE_ARGS(u8 counter, const u8 *jumpInstr); // Some effects should only happen on the first or second strike of Parental Bond, // so a way to check this in battle scripts is useful - u8 counter = T1_READ_8(gBattlescriptCurrInstr + 5); - if (gSpecialStatuses[gBattlerAttacker].parentalBondState == counter && gBattleMons[gBattlerTarget].hp != 0) - gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 6); + if (gSpecialStatuses[gBattlerAttacker].parentalBondState == cmd->counter && gBattleMons[gBattlerTarget].hp != 0) + gBattlescriptCurrInstr = cmd->jumpInstr; else - gBattlescriptCurrInstr += 10; + gBattlescriptCurrInstr = cmd->nextInstr; } // 6 bytes long (callnative(5) + battler(1)) void BS_GetBattlerSide(void) { - gBattleCommunication[0] = GetBattlerSide(gBattlescriptCurrInstr[5]); - gBattlescriptCurrInstr += 6; + NATIVE_ARGS(u8 battler); + gBattleCommunication[0] = GetBattlerSide(cmd->battler); + gBattlescriptCurrInstr = cmd->nextInstr; } -// 6 bytes long (callnative(5) + battler(1)) void BS_CanTeleport(void) { - u8 battler = gBattlescriptCurrInstr[5]; - gBattleCommunication[0] = CanTeleport(battler); - gBattlescriptCurrInstr += 6; + NATIVE_ARGS(u8 battler); + gBattleCommunication[0] = CanTeleport(cmd->battler); + gBattlescriptCurrInstr = cmd->nextInstr; } -// 5 bytes long void BS_TrySymbiosis(void) { + NATIVE_ARGS(); //called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem. gActiveBattler = gBattlerAttacker; if (SYMBIOSIS_CHECK(gBattlerAttacker, BATTLE_PARTNER(gActiveBattler))) @@ -16357,7 +16357,7 @@ void BS_TrySymbiosis(void) return; } - gBattlescriptCurrInstr += 5; + gBattlescriptCurrInstr = cmd->nextInstr; } void BS_SetZEffect(void) From eaa44cc8b5c62d70792868d8982ed1920f31f49e Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 15 May 2023 15:20:44 -0400 Subject: [PATCH 86/88] fix syntax --- src/battle_script_commands.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 896b57ffcc..88a76adff1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8805,7 +8805,7 @@ static bool32 CanTeleport(u8 battlerId) { struct Pokemon *party = GetBattlerParty(battlerId); u32 species, count, i; - + for (i = 0; i < PARTY_SIZE; i++) { species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); @@ -16314,7 +16314,6 @@ static bool8 IsFinalStrikeEffect(u16 move) return FALSE; } -// 10 bytes long (callnative(5) + counter(1) + ptr(4)) void BS_CheckParentalBondCounter(void) { NATIVE_ARGS(u8 counter, const u8 *jumpInstr); @@ -16326,7 +16325,6 @@ void BS_CheckParentalBondCounter(void) gBattlescriptCurrInstr = cmd->nextInstr; } -// 6 bytes long (callnative(5) + battler(1)) void BS_GetBattlerSide(void) { NATIVE_ARGS(u8 battler); From 49a9210e9dfafeb1e984a000c73cdd063e0db948 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Mon, 15 May 2023 16:45:30 -0300 Subject: [PATCH 87/88] Fixed typo in include/config/battle.h --- include/config/battle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index 6475f6664c..4a1bbdc2f9 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -191,7 +191,7 @@ // Other settings #define B_DOUBLE_WILD_CHANCE 0 // % chance of encountering two Pokémon in a Wild Encounter. -#define B_DOUBLE_WILD_REQUIRE_2_MONS FALSE // If set to TRUE, Wild Double Battles will default to Single Battles when the player only has 1 usuable Pokémon, ignoring B_DOUBLE_WILD_CHANCE and B_FLAG_FORCE_DOUBLE_WILD. +#define B_DOUBLE_WILD_REQUIRE_2_MONS FALSE // If set to TRUE, Wild Double Battles will default to Single Battles when the player only has 1 usable Pokémon, ignoring B_DOUBLE_WILD_CHANCE and B_FLAG_FORCE_DOUBLE_WILD. #define B_MULTI_BATTLE_WHITEOUT GEN_LATEST // In Gen4+, multi battles end when the Player and also their Partner don't have any more Pokémon to fight. #define B_EVOLUTION_AFTER_WHITEOUT GEN_LATEST // In Gen6+, Pokemon that qualify for evolution after battle will evolve even if the player loses. #define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper) From 37874fe5c167602960811919fc1ffcfffb6133c2 Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Tue, 16 May 2023 16:21:05 -0700 Subject: [PATCH 88/88] fixed revival blessing failure still showing anim (#3010) --- data/battle_scripts_1.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index bcb1275e20..2fe9c74ee6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -435,9 +435,9 @@ BattleScript_EffectRevivalBlessing:: attackcanceler attackstring ppreduce + tryrevivalblessing BattleScript_ButItFailed attackanimation waitanimation - tryrevivalblessing BattleScript_ButItFailed printstring STRINGID_PKMNREVIVEDREADYTOFIGHT waitmessage B_WAIT_TIME_LONG jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_EffectRevivalBlessingSendOut