From 31ac151e29faf60ab322f0e2453826a016f84b47 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 9 Feb 2024 13:58:16 +0100 Subject: [PATCH] Fix Full Restore / Antidote not reseting Toxic Counter (#4135) * Fix Full Restore / Antidote not reseting Toxic Counter * Update battle_scripts_2.s --------- Co-authored-by: Bassoonian --- asm/macros/battle_script.inc | 6 +- data/battle_scripts_2.s | 34 +++-- src/battle_script_commands.c | 135 ++++++++++-------- src/item.c | 4 +- test/battle/item_effect/cure_status.c | 18 +++ .../battle/item_effect/heal_and_cure_status.c | 51 ++++++- 6 files changed, 168 insertions(+), 80 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index dd5d10296d..99ee9a4e5e 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1332,12 +1332,14 @@ .4byte \jumpInstr .endm - .macro itemrestorehp + .macro itemrestorehp jumpInstr:req callnative BS_ItemRestoreHP + .4byte \jumpInstr .endm - .macro itemcurestatus + .macro itemcurestatus jumpInstr:req callnative BS_ItemCureStatus + .4byte \jumpInstr .endm .macro itemincreasestat diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 78e077fbd5..6895be7966 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -45,16 +45,21 @@ BattleScript_UseItemMessage: printfromtable gTrainerUsedItemStringIds waitmessage B_WAIT_TIME_LONG return - -BattleScript_ItemRestoreHP:: - call BattleScript_UseItemMessage - itemrestorehp + +BattleScript_ItemRestoreHPRet: bichalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE healthbarupdate BS_SCRIPTING datahpupdate BS_SCRIPTING printstring STRINGID_ITEMRESTOREDSPECIESHEALTH waitmessage B_WAIT_TIME_LONG + return + +BattleScript_ItemRestoreHP:: + call BattleScript_UseItemMessage + itemrestorehp BattleScript_ItemRestoreHPEnd + call BattleScript_ItemRestoreHPRet +BattleScript_ItemRestoreHPEnd: end BattleScript_ItemRestoreHP_Party:: @@ -72,26 +77,19 @@ BattleScript_ItemRestoreHP_SendOutRevivedBattler: BattleScript_ItemCureStatus:: call BattleScript_UseItemMessage - itemcurestatus +BattleScript_ItemCureStatusAfterItemMsg: + itemcurestatus BattleScript_ItemCureStatusEnd updatestatusicon BS_SCRIPTING printstring STRINGID_ITEMCUREDSPECIESSTATUS waitmessage B_WAIT_TIME_LONG +BattleScript_ItemCureStatusEnd: end BattleScript_ItemHealAndCureStatus:: call BattleScript_UseItemMessage - itemrestorehp - itemcurestatus - printstring STRINGID_ITEMRESTOREDSPECIESHEALTH - waitmessage B_WAIT_TIME_LONG - bichalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT - orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE - healthbarupdate BS_SCRIPTING - datahpupdate BS_SCRIPTING - updatestatusicon BS_SCRIPTING - printstring STRINGID_ITEMRESTOREDSPECIESHEALTH - waitmessage B_WAIT_TIME_LONG - end + itemrestorehp BattleScript_ItemCureStatusAfterItemMsg + call BattleScript_ItemRestoreHPRet + goto BattleScript_ItemCureStatusAfterItemMsg BattleScript_ItemIncreaseStat:: call BattleScript_UseItemMessage @@ -118,7 +116,7 @@ BattleScript_ItemSetFocusEnergy:: setfocusenergy playmoveanimation BS_ATTACKER, MOVE_FOCUS_ENERGY waitanimation - copybyte sBATTLER, gBattlerAttacker + copybyte sBATTLER, gBattlerAttacker printstring STRINGID_PKMNUSEDXTOGETPUMPED waitmessage B_WAIT_TIME_LONG end diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index cb82e32176..606e5f0c93 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -15793,7 +15793,7 @@ void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBat void BS_ItemRestoreHP(void) { - NATIVE_ARGS(); + NATIVE_ARGS(const u8 *alreadyMaxHpInstr); u16 healAmount; u32 battler = MAX_BATTLERS_COUNT; u32 healParam = GetItemEffect(gLastUsedItem)[6]; @@ -15803,90 +15803,113 @@ void BS_ItemRestoreHP(void) u16 maxHP = GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_MAX_HP); gBattleCommunication[MULTIUSE_STATE] = 0; - // Track the number of Revives used in a battle. - if (hp == 0 && side == B_SIDE_PLAYER && gBattleResults.numRevivesUsed < 255) - gBattleResults.numRevivesUsed++; - - // Check if the recipient is an active battler. - if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker]) - battler = gBattlerAttacker; - else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)]) - battler = BATTLE_PARTNER(gBattlerAttacker); - - // Get amount to heal. - switch (healParam) + if (hp == maxHP) { - case ITEM6_HEAL_HP_FULL: - healAmount = maxHP; - break; - case ITEM6_HEAL_HP_HALF: - healAmount = maxHP / 2; - break; - case ITEM6_HEAL_HP_QUARTER: - healAmount = maxHP / 4; - break; - default: - healAmount = healParam; - break; - } - if (hp + healAmount > maxHP) - healAmount = maxHP - hp; - - gBattleScripting.battler = battler; - PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES)); - - // Heal is applied as move damage if battler is active. - if (battler != MAX_BATTLERS_COUNT && hp != 0) - { - gBattleMoveDamage = -healAmount; - gBattlescriptCurrInstr = cmd->nextInstr; + gBattlescriptCurrInstr = cmd->alreadyMaxHpInstr; } else { - hp += healAmount; - SetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP, &hp); + // Track the number of Revives used in a battle. + if (hp == 0 && side == B_SIDE_PLAYER && gBattleResults.numRevivesUsed < 255) + gBattleResults.numRevivesUsed++; - // Revived battlers on the field need to be brought back. - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && battler != MAX_BATTLERS_COUNT) + // Check if the recipient is an active battler. + if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker]) + battler = gBattlerAttacker; + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)]) + battler = BATTLE_PARTNER(gBattlerAttacker); + + // Get amount to heal. + switch (healParam) { - gAbsentBattlerFlags &= ~gBitTable[battler]; - gBattleCommunication[MULTIUSE_STATE] = TRUE; + case ITEM6_HEAL_HP_FULL: + healAmount = maxHP; + break; + case ITEM6_HEAL_HP_HALF: + healAmount = maxHP / 2; + break; + case ITEM6_HEAL_HP_QUARTER: + healAmount = maxHP / 4; + break; + default: + healAmount = healParam; + break; + } + if (hp + healAmount > maxHP) + healAmount = maxHP - hp; + + gBattleScripting.battler = battler; + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES)); + + // Heal is applied as move damage if battler is active. + if (battler != MAX_BATTLERS_COUNT && hp != 0) + { + gBattleMoveDamage = -healAmount; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + hp += healAmount; + SetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP, &hp); + + // Revived battlers on the field need to be brought back. + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && battler != MAX_BATTLERS_COUNT) + { + gAbsentBattlerFlags &= ~gBitTable[battler]; + gBattleCommunication[MULTIUSE_STATE] = TRUE; + } + gBattlescriptCurrInstr = BattleScript_ItemRestoreHP_Party; } - gBattlescriptCurrInstr = BattleScript_ItemRestoreHP_Party; } } void BS_ItemCureStatus(void) { - NATIVE_ARGS(); + NATIVE_ARGS(const u8 *noStatusInstr); u32 battler = gBattlerAttacker; u32 side = GetBattlerSide(gBattlerAttacker); + u32 previousStatus2 = 0; + bool32 statusChanged = FALSE; struct Pokemon *party = GetSideParty(side); // Heal Status2 conditions if battler is active. if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker]) { + previousStatus2 = gBattleMons[battler].status2; gBattleMons[gBattlerAttacker].status2 &= ~GetItemStatus2Mask(gLastUsedItem); } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)]) { - gBattleMons[gBattlerAttacker].status2 &= ~GetItemStatus2Mask(gLastUsedItem); battler = BATTLE_PARTNER(gBattlerAttacker); + previousStatus2 = gBattleMons[battler].status2; + gBattleMons[battler].status2 &= ~GetItemStatus2Mask(gLastUsedItem); } + if (previousStatus2 != gBattleMons[battler].status2) + statusChanged = TRUE; + // Heal Status1 conditions. - HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], GetItemStatus1Mask(gLastUsedItem), battler); + if (!HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], GetItemStatus1Mask(gLastUsedItem), battler)) + { + statusChanged = TRUE; + if (GetItemStatus1Mask(gLastUsedItem) & STATUS1_SLEEP) + gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; + if (GetItemStatus2Mask(gLastUsedItem) & STATUS2_CONFUSION) + gStatuses4[battler] &= ~STATUS4_INFINITE_CONFUSION; + } - if (GetItemStatus1Mask(gLastUsedItem) & STATUS1_SLEEP) - gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; - if (GetItemStatus2Mask(gLastUsedItem) & STATUS2_CONFUSION) - gStatuses4[battler] &= ~STATUS4_INFINITE_CONFUSION; - - gBattleScripting.battler = battler; - PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES)); - gBattlescriptCurrInstr = cmd->nextInstr; + if (statusChanged) + { + gBattleScripting.battler = battler; + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES)); + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->noStatusInstr; + } } void BS_ItemIncreaseStat(void) diff --git a/src/item.c b/src/item.c index 7c19b316d6..97b0f0c361 100644 --- a/src/item.c +++ b/src/item.c @@ -974,11 +974,11 @@ u32 GetItemStatus1Mask(u16 itemId) case ITEM3_BURN: return STATUS1_BURN; case ITEM3_POISON: - return STATUS1_POISON | STATUS1_TOXIC_POISON; + return STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER; case ITEM3_SLEEP: return STATUS1_SLEEP; case ITEM3_STATUS_ALL: - return STATUS1_ANY; + return STATUS1_ANY | STATUS1_TOXIC_COUNTER; } return 0; } diff --git a/test/battle/item_effect/cure_status.c b/test/battle/item_effect/cure_status.c index 02ba76bcc7..9efb0cbdf3 100644 --- a/test/battle/item_effect/cure_status.c +++ b/test/battle/item_effect/cure_status.c @@ -46,6 +46,24 @@ SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned") } } +SINGLE_BATTLE_TEST("Antidote resets Toxic Counter") +{ + GIVEN { + ASSUME(gItems[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TOXIC); } + TURN { ; } + TURN { USE_ITEM(player, ITEM_ANTIDOTE, partyIndex: 0); } + } SCENE { + MESSAGE("Foe Wobbuffet used Toxic!"); + MESSAGE("Wobbuffet had its status healed!"); + } THEN { + EXPECT_EQ(player->status1, STATUS1_NONE); + } +} + SINGLE_BATTLE_TEST("Awakening heals a battler from being asleep") { GIVEN { diff --git a/test/battle/item_effect/heal_and_cure_status.c b/test/battle/item_effect/heal_and_cure_status.c index de6a459a18..4200261696 100644 --- a/test/battle/item_effect/heal_and_cure_status.c +++ b/test/battle/item_effect/heal_and_cure_status.c @@ -1,6 +1,11 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS); +} + SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status") { u16 status; @@ -10,24 +15,48 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s PARAMETRIZE{ status = STATUS1_POISON; } PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE{ status = STATUS1_NONE; } 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("Wobbuffet had its HP restored!"); + if (status != STATUS1_NONE) { + MESSAGE("Wobbuffet had its status healed!"); // The message is not printed if status wasn't healed. + } } THEN { EXPECT_EQ(player->hp, player->maxHP); EXPECT_EQ(player->status1, STATUS1_NONE); } } +SINGLE_BATTLE_TEST("Full Restore 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 { + PLAYER(SPECIES_WOBBUFFET) { Status1(status); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } + } SCENE { + NOT MESSAGE("Wobbuffet had its HP restored!"); // The message is not printed if mon has max HP. + MESSAGE("Wobbuffet had its status healed!"); + } THEN { + EXPECT_EQ(player->status1, STATUS1_NONE); + } +} + SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") { GIVEN { - ASSUME(gItems[ITEM_FULL_RESTORE].battleUsage == EFFECT_ITEM_HEAL_AND_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -41,3 +70,21 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures confusion") EXPECT_EQ(player->hp, player->maxHP); } } + +SINGLE_BATTLE_TEST("Full Restore resets Toxic Counter") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TOXIC); } + TURN { ; } + TURN { USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 0); } + } SCENE { + MESSAGE("Foe Wobbuffet used Toxic!"); + MESSAGE("Wobbuffet had its HP restored!"); + MESSAGE("Wobbuffet had its status healed!"); + } THEN { + EXPECT_EQ(player->status1, STATUS1_NONE); + } +}