From 31c845e887e32ca6ae18daf155c8b6047a147927 Mon Sep 17 00:00:00 2001 From: cawtds Date: Thu, 2 May 2024 01:50:13 +0200 Subject: [PATCH] updated up to Cmd_statbuffchange --- asm/macros/battle_script.inc | 4 +- data/battle_scripts_1.s | 67 ++++-- include/battle.h | 7 +- include/constants/battle_string_ids.h | 5 +- src/battle_message.c | 6 + src/battle_script_commands.c | 330 +++++++++++++++++++++----- 6 files changed, 328 insertions(+), 91 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 21be0ee21..ff4fb401b 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -759,7 +759,7 @@ .4byte \failInstr .endm - .macro negativedamage + .macro setdrainedhp .byte 0x88 .endm @@ -1929,7 +1929,7 @@ @ helpful macros .macro setstatchanger stat:req, stages:req, down:req - setbyte sSTATCHANGER, \stat | \stages << 4 | \down << 7 + setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7 .endm .macro setmoveeffect effect:req diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0157abce3..f31df68bd 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -119,31 +119,16 @@ BattleScript_EffectPoisonHit:: goto BattleScript_EffectHit BattleScript_EffectAbsorb:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - ppreduce - critcalc - damagecalc - typecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG - negativedamage - orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE + call BattleScript_EffectHit_Ret + jumpifstatus3 BS_ATTACKER, STATUS3_HEAL_BLOCK, BattleScript_AbsorbHealBlock + setdrainedhp + manipulatedamage DMG_BIG_ROOT + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE jumpifability BS_TARGET, ABILITY_LIQUID_OOZE, BattleScript_AbsorbLiquidOoze setbyte cMULTISTRING_CHOOSER, B_MSG_ABSORB goto BattleScript_AbsorbUpdateHp BattleScript_AbsorbLiquidOoze:: + call BattleScript_AbilityPopUpTarget manipulatedamage DMG_CHANGE_SIGN setbyte cMULTISTRING_CHOOSER, B_MSG_ABSORB_OOZE BattleScript_AbsorbUpdateHp:: @@ -154,6 +139,7 @@ BattleScript_AbsorbUpdateHp:: waitmessage B_WAIT_TIME_LONG BattleScript_AbsorbTryFainting:: tryfaintmon BS_ATTACKER +BattleScript_AbsorbHealBlock:: tryfaintmon BS_TARGET goto BattleScript_MoveEnd @@ -222,8 +208,9 @@ BattleScript_PreserveMissedBitDoMoveAnim: BattleScript_EffectDreamEater:: attackcanceler - jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_DreamEaterNoEffect + jumpifsubstituteblocks BattleScript_DreamEaterNoEffect jumpifstatus BS_TARGET, STATUS1_SLEEP, BattleScript_DreamEaterWorked + jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_DreamEaterWorked BattleScript_DreamEaterNoEffect: attackstring ppreduce @@ -235,7 +222,6 @@ BattleScript_DreamEaterWorked: ppreduce critcalc damagecalc - typecalc adjustdamage attackanimation waitanimation @@ -248,7 +234,9 @@ BattleScript_DreamEaterWorked: waitmessage B_WAIT_TIME_LONG resultmessage waitmessage B_WAIT_TIME_LONG - negativedamage + jumpifstatus3 BS_ATTACKER, STATUS3_HEAL_BLOCK, BattleScript_DreamEaterTryFaintEnd + setdrainedhp + manipulatedamage DMG_BIG_ROOT orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE healthbarupdate BS_ATTACKER datahpupdate BS_ATTACKER @@ -6323,3 +6311,34 @@ BattleScript_LeafGuardPreventsRest:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd +BattleScript_EffectHit_Ret:: + attackcanceler +BattleScript_EffectHit_RetFromAccCheck:: + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce +BattleScript_EffectHit_RetFromCritCalc:: + critcalc + damagecalc + adjustdamage +BattleScript_Hit_RetFromAtkAnimation:: + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + setadditionaleffects + return + +BattleScript_ItemNoStatLoss:: + pause B_WAIT_TIME_SHORT + printstring STRINGID_CLEARAMULETWONTLOWERSTATS + waitmessage B_WAIT_TIME_LONG + return + diff --git a/include/battle.h b/include/battle.h index 31e4caf00..ad5785aa2 100644 --- a/include/battle.h +++ b/include/battle.h @@ -774,13 +774,12 @@ extern struct BattleStruct *gBattleStruct; || gProtectStructs[battlerId].obstructed \ || gProtectStructs[battlerId].silkTrapped) -#define GET_STAT_BUFF_ID(n)((n & 0xF)) // first four bits 0x1, 0x2, 0x4, 0x8 -#define GET_STAT_BUFF_VALUE2(n)((n & 0xF0)) +#define GET_STAT_BUFF_ID(n)((n & 7)) // first three bits 0x1, 0x2, 0x4 #define GET_STAT_BUFF_VALUE_WITH_SIGN(n)((n & 0xF8)) -#define GET_STAT_BUFF_VALUE(n)(((n >> 4) & 7)) // 0x10, 0x20, 0x40 +#define GET_STAT_BUFF_VALUE(n)(((n >> 3) & 0xF)) // 0x8, 0x10, 0x20, 0x40 #define STAT_BUFF_NEGATIVE 0x80 // 0x80, the sign bit -#define SET_STAT_BUFF_VALUE(n)(((s8)(((s8)(n) << 4)) & 0xF0)) +#define SET_STAT_BUFF_VALUE(n)((((n) << 3) & 0xF8)) #define SET_STATCHANGER(statId, stage, goesDown)(gBattleScripting.statChanger = (statId) + (stage << 4) + (goesDown << 7)) #define SET_STATCHANGER2(dst, statId, stage, goesDown)(dst = (statId) + ((stage) << 3) + (goesDown << 7)) diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 430df8102..df2a1f9e4 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -563,8 +563,11 @@ #define STRINGID_MISTYTERRAINPREVENTS 560 #define STRINGID_PSYCHICTERRAINPREVENTS 561 #define STRINGID_FLOWERVEILPROTECTED 562 +#define STRINGID_DRASTICALLY 563 +#define STRINGID_SEVERELY 564 +#define STRINGID_CLEARAMULETWONTLOWERSTATS 565 -#define BATTLESTRINGS_COUNT 563 +#define BATTLESTRINGS_COUNT 566 // 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 6e69f593e..6f19a36f3 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -692,6 +692,9 @@ static const u8 sText_ElectricTerrainPreventsSleep[] = _("{B_DEF_NAME_WITH_PREFI static const u8 sText_MistyTerrainPreventsStatus[] = _("{B_DEF_NAME_WITH_PREFIX} surrounds itself\nwith a protective mist!"); static const u8 sText_PsychicTerrainPreventsPriority[] = _("{B_DEF_NAME_WITH_PREFIX} surrounds itself\nwith psychic terrain!"); static const u8 sText_FlowerVeilProtected[] = _("{B_DEF_NAME_WITH_PREFIX} surrounded itself\nwith a veil of petals!"); +static const u8 sText_drastically[] = _("drastically "); +static const u8 sText_severely[] = _("severely "); +static const u8 sText_ClearAmuletWontLowerStats[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ITEM} prevents\nits stats from being lowered!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT - BATTLESTRINGS_TABLE_START] = { @@ -1246,6 +1249,9 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT - BATTLESTRINGS_TABLE_ST [STRINGID_MISTYTERRAINPREVENTS - BATTLESTRINGS_TABLE_START] = sText_MistyTerrainPreventsStatus, [STRINGID_PSYCHICTERRAINPREVENTS - BATTLESTRINGS_TABLE_START] = sText_PsychicTerrainPreventsPriority, [STRINGID_FLOWERVEILPROTECTED - BATTLESTRINGS_TABLE_START] = sText_FlowerVeilProtected, + [STRINGID_DRASTICALLY - BATTLESTRINGS_TABLE_START] = sText_drastically, + [STRINGID_SEVERELY - BATTLESTRINGS_TABLE_START] = sText_severely, + [STRINGID_CLEARAMULETWONTLOWERSTATS - BATTLESTRINGS_TABLE_START] = sText_ClearAmuletWontLowerStats, }; const u16 gTrainerUsedItemStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 287c8a70d..a6c7a1393 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -323,7 +323,7 @@ static bool8 IsTwoTurnsMove(u16 move); static void TrySetDestinyBondToHappen(void); static u8 AttacksThisTurn(u8 battlerId, u16 move); // Note: returns 1 if it's a charging turn, otherwise 2. static void CheckWonderGuardAndLevitate(void); -static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32, const u8 *BS_ptr); +static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr); static void InitLevelUpBanner(void); static bool8 SlideInLevelUpBanner(void); static bool8 SlideOutLevelUpBanner(void); @@ -340,6 +340,7 @@ static void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 fai static bool8 CanBurnHitThaw(u16 move); static bool32 ChangeOrderTargetAfterAttacker(void); static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount); +static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -477,7 +478,7 @@ static void Cmd_jumpifuproarwakes(void); static void Cmd_stockpile(void); static void Cmd_stockpiletobasedamage(void); static void Cmd_stockpiletohpheal(void); -static void Cmd_negativedamage(void); +static void Cmd_setdrainedhp(void); static void Cmd_statbuffchange(void); static void Cmd_normalisebuffs(void); static void Cmd_setbide(void); @@ -729,8 +730,8 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_stockpile, //0x85 // done Cmd_stockpiletobasedamage, //0x86 // done Cmd_stockpiletohpheal, //0x87 // done - Cmd_negativedamage, //0x88 - Cmd_statbuffchange, //0x89 + Cmd_setdrainedhp, //0x88 // done + Cmd_statbuffchange, //0x89 // done Cmd_normalisebuffs, //0x8A Cmd_setbide, //0x8B Cmd_confuseifrepeatingattackends, //0x8C @@ -11689,120 +11690,264 @@ static void Cmd_stockpiletohpheal(void) } } -static void Cmd_negativedamage(void) +// Sign change for drained HP handled in GetDrainedBigRootHp +static void Cmd_setdrainedhp(void) { - gBattleMoveDamage = -(gHpDealt / 2); - if (gBattleMoveDamage == 0) - gBattleMoveDamage = -1; + CMD_ARGS(); - gBattlescriptCurrInstr++; + if (gMovesInfo[gCurrentMove].argument != 0) + gBattleMoveDamage = (gHpDealt * gMovesInfo[gCurrentMove].argument / 100); + else + gBattleMoveDamage = (gHpDealt / 2); + + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + + gBattlescriptCurrInstr = cmd->nextInstr; } -static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr) +static u16 ReverseStatChangeMoveEffect(u16 moveEffect) +{ + switch (moveEffect) + { + // +1 + case MOVE_EFFECT_ATK_PLUS_1: + return MOVE_EFFECT_ATK_MINUS_1; + case MOVE_EFFECT_DEF_PLUS_1: + return MOVE_EFFECT_DEF_MINUS_1; + case MOVE_EFFECT_SPD_PLUS_1: + return MOVE_EFFECT_SPD_MINUS_1; + case MOVE_EFFECT_SP_ATK_PLUS_1: + return MOVE_EFFECT_SP_ATK_MINUS_1; + case MOVE_EFFECT_SP_DEF_PLUS_1: + return MOVE_EFFECT_SP_DEF_MINUS_1; + case MOVE_EFFECT_ACC_PLUS_1: + return MOVE_EFFECT_ACC_MINUS_1; + case MOVE_EFFECT_EVS_PLUS_1: + return MOVE_EFFECT_EVS_MINUS_1; + // -1 + case MOVE_EFFECT_ATK_MINUS_1: + return MOVE_EFFECT_ATK_PLUS_1; + case MOVE_EFFECT_DEF_MINUS_1: + return MOVE_EFFECT_DEF_PLUS_1; + case MOVE_EFFECT_SPD_MINUS_1: + return MOVE_EFFECT_SPD_PLUS_1; + case MOVE_EFFECT_SP_ATK_MINUS_1: + return MOVE_EFFECT_SP_ATK_PLUS_1; + case MOVE_EFFECT_SP_DEF_MINUS_1: + return MOVE_EFFECT_SP_DEF_PLUS_1; + case MOVE_EFFECT_ACC_MINUS_1: + return MOVE_EFFECT_ACC_PLUS_1; + case MOVE_EFFECT_EVS_MINUS_1: + // +2 + case MOVE_EFFECT_ATK_PLUS_2: + return MOVE_EFFECT_ATK_MINUS_2; + case MOVE_EFFECT_DEF_PLUS_2: + return MOVE_EFFECT_DEF_MINUS_2; + case MOVE_EFFECT_SPD_PLUS_2: + return MOVE_EFFECT_SPD_MINUS_2; + case MOVE_EFFECT_SP_ATK_PLUS_2: + return MOVE_EFFECT_SP_ATK_MINUS_2; + case MOVE_EFFECT_SP_DEF_PLUS_2: + return MOVE_EFFECT_SP_DEF_MINUS_2; + case MOVE_EFFECT_ACC_PLUS_2: + return MOVE_EFFECT_ACC_MINUS_2; + case MOVE_EFFECT_EVS_PLUS_2: + return MOVE_EFFECT_EVS_MINUS_2; + // -2 + case MOVE_EFFECT_ATK_MINUS_2: + return MOVE_EFFECT_ATK_PLUS_2; + case MOVE_EFFECT_DEF_MINUS_2: + return MOVE_EFFECT_DEF_PLUS_2; + case MOVE_EFFECT_SPD_MINUS_2: + return MOVE_EFFECT_SPD_PLUS_2; + case MOVE_EFFECT_SP_ATK_MINUS_2: + return MOVE_EFFECT_SP_ATK_PLUS_2; + case MOVE_EFFECT_SP_DEF_MINUS_2: + return MOVE_EFFECT_SP_DEF_PLUS_2; + case MOVE_EFFECT_ACC_MINUS_2: + return MOVE_EFFECT_ACC_PLUS_2; + case MOVE_EFFECT_EVS_MINUS_2: + return MOVE_EFFECT_EVS_PLUS_2; + default: + return 0; + } +} + +static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr) { bool32 certain = FALSE; bool32 notProtectAffected = FALSE; - u32 index; + u32 index, battler, battlerAbility, battlerHoldEffect; + bool32 affectsUser = (flags & MOVE_EFFECT_AFFECTS_USER); + bool32 mirrorArmored = (flags & STAT_CHANGE_MIRROR_ARMOR); - if (flags & MOVE_EFFECT_AFFECTS_USER) - gActiveBattler = gBattlerAttacker; + if (affectsUser) + battler = gBattlerAttacker; else - gActiveBattler = gBattlerTarget; + battler = gBattlerTarget; + gActiveBattler = battler; - flags &= ~MOVE_EFFECT_AFFECTS_USER; + battlerAbility = GetBattlerAbility(battler); + battlerHoldEffect = GetBattlerHoldEffect(battler, TRUE); + + gSpecialStatuses[battler].changedStatsBattlerId = gBattlerAttacker; + + flags &= ~(MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_MIRROR_ARMOR); if (flags & MOVE_EFFECT_CERTAIN) - certain++; + certain = TRUE; flags &= ~MOVE_EFFECT_CERTAIN; if (flags & STAT_CHANGE_NOT_PROTECT_AFFECTED) notProtectAffected++; flags &= ~STAT_CHANGE_NOT_PROTECT_AFFECTED; - PREPARE_STAT_BUFFER(gBattleTextBuff1, statId) + if (battlerAbility == ABILITY_CONTRARY) + { + statValue ^= STAT_BUFF_NEGATIVE; + gBattleScripting.statChanger ^= STAT_BUFF_NEGATIVE; + RecordAbilityBattle(battler, battlerAbility); + if (flags & STAT_CHANGE_UPDATE_MOVE_EFFECT) + { + flags &= ~STAT_CHANGE_UPDATE_MOVE_EFFECT; + gBattleScripting.moveEffect = ReverseStatChangeMoveEffect(gBattleScripting.moveEffect); + } + } + else if (battlerAbility == ABILITY_SIMPLE) + { + statValue = (SET_STAT_BUFF_VALUE(GET_STAT_BUFF_VALUE(statValue) * 2)) | ((statValue <= -1) ? STAT_BUFF_NEGATIVE : 0); + } + + PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); if (statValue <= -1) // Stat decrease. { - if (gSideTimers[GET_BATTLER_SIDE(gActiveBattler)].mistTimer - && !certain && gCurrentMove != MOVE_CURSE) + if (gSideTimers[GetBattlerSide(battler)].mistTimer + && !certain && gCurrentMove != MOVE_CURSE + && !(battler == gBattlerTarget && GetBattlerAbility(gBattlerAttacker) == ABILITY_INFILTRATOR)) { if (flags == STAT_CHANGE_ALLOW_PTR) { - if (gSpecialStatuses[gActiveBattler].statLowered) + if (gSpecialStatuses[battler].statLowered) { gBattlescriptCurrInstr = BS_ptr; } else { BattleScriptPush(BS_ptr); - gBattleScripting.battler = gActiveBattler; + gBattleScripting.battler = battler; gBattlescriptCurrInstr = BattleScript_MistProtected; - gSpecialStatuses[gActiveBattler].statLowered = 1; + gSpecialStatuses[battler].statLowered = TRUE; } } return STAT_CHANGE_DIDNT_WORK; } else if (gCurrentMove != MOVE_CURSE - && notProtectAffected != TRUE && JumpIfMoveAffectedByProtect(0)) + && notProtectAffected != TRUE && JumpIfMoveAffectedByProtect(gCurrentMove)) { gBattlescriptCurrInstr = BattleScript_ButItFailed; return STAT_CHANGE_DIDNT_WORK; } - else if ((gBattleMons[gActiveBattler].ability == ABILITY_CLEAR_BODY - || gBattleMons[gActiveBattler].ability == ABILITY_WHITE_SMOKE) - && !certain && gCurrentMove != MOVE_CURSE) + else if ((battlerHoldEffect == HOLD_EFFECT_CLEAR_AMULET + || CanAbilityPreventStatLoss(battlerAbility, GetBattlerAbility(gBattlerAttacker) == ABILITY_INTIMIDATE)) + && (!affectsUser || mirrorArmored) && !certain && gCurrentMove != MOVE_CURSE) { if (flags == STAT_CHANGE_ALLOW_PTR) { - if (gSpecialStatuses[gActiveBattler].statLowered) + if (gSpecialStatuses[battler].statLowered) { gBattlescriptCurrInstr = BS_ptr; } else { BattleScriptPush(BS_ptr); - gBattleScripting.battler = gActiveBattler; - gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss; - gLastUsedAbility = gBattleMons[gActiveBattler].ability; - RecordAbilityBattle(gActiveBattler, gLastUsedAbility); - gSpecialStatuses[gActiveBattler].statLowered = 1; + gBattleScripting.battler = battler; + if (battlerHoldEffect == HOLD_EFFECT_CLEAR_AMULET) + { + gLastUsedItem = gBattleMons[battler].item; + gBattlescriptCurrInstr = BattleScript_ItemNoStatLoss; + RecordItemEffectBattle(battler, HOLD_EFFECT_CLEAR_AMULET); + } + else + { + gBattlerAbility = battler; + gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss; + gLastUsedAbility = battlerAbility; + RecordAbilityBattle(battler, gLastUsedAbility); + } + gSpecialStatuses[battler].statLowered = TRUE; } } return STAT_CHANGE_DIDNT_WORK; } - else if (gBattleMons[gActiveBattler].ability == ABILITY_KEEN_EYE - && !certain && statId == STAT_ACC) + else if ((index = IsFlowerVeilProtected(battler)) && !certain) + { + if (flags == STAT_CHANGE_ALLOW_PTR) + { + if (gSpecialStatuses[battler].statLowered) + { + gBattlescriptCurrInstr = BS_ptr; + } + else + { + BattleScriptPush(BS_ptr); + gBattleScripting.battler = battler; + gBattlerAbility = index - 1; + gBattlescriptCurrInstr = BattleScript_FlowerVeilProtectsRet; + gLastUsedAbility = ABILITY_FLOWER_VEIL; + gSpecialStatuses[battler].statLowered = TRUE; + } + } + return STAT_CHANGE_DIDNT_WORK; + } + else if (!certain + && (((battlerAbility == ABILITY_KEEN_EYE || battlerAbility == ABILITY_MINDS_EYE) && statId == STAT_ACC) + || (B_ILLUMINATE_EFFECT >= GEN_9 && battlerAbility == ABILITY_ILLUMINATE && statId == STAT_ACC) + || (battlerAbility == ABILITY_HYPER_CUTTER && statId == STAT_ATK) + || (battlerAbility == ABILITY_BIG_PECKS && statId == STAT_DEF))) { if (flags == STAT_CHANGE_ALLOW_PTR) { BattleScriptPush(BS_ptr); - gBattleScripting.battler = gActiveBattler; + gBattleScripting.battler = battler; + gBattlerAbility = battler; gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss; - gLastUsedAbility = gBattleMons[gActiveBattler].ability; - RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + gLastUsedAbility = battlerAbility; + RecordAbilityBattle(battler, gLastUsedAbility); } return STAT_CHANGE_DIDNT_WORK; } - else if (gBattleMons[gActiveBattler].ability == ABILITY_HYPER_CUTTER - && !certain && statId == STAT_ATK) + else if (battlerAbility == ABILITY_MIRROR_ARMOR && !affectsUser && !mirrorArmored && gBattlerAttacker != gBattlerTarget && battler == gBattlerTarget) { if (flags == STAT_CHANGE_ALLOW_PTR) { + SET_STATCHANGER(statId, GET_STAT_BUFF_VALUE(statValue) | STAT_BUFF_NEGATIVE, TRUE); BattleScriptPush(BS_ptr); - gBattleScripting.battler = gActiveBattler; - gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss; - gLastUsedAbility = gBattleMons[gActiveBattler].ability; - RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + gBattleScripting.battler = battler; + gBattlerAbility = battler; + gBattlescriptCurrInstr = BattleScript_MirrorArmorReflect; + RecordAbilityBattle(battler, gBattleMons[battler].ability); } return STAT_CHANGE_DIDNT_WORK; } - else if (gBattleMons[gActiveBattler].ability == ABILITY_SHIELD_DUST && flags == 0) + else if (battlerAbility == ABILITY_SHIELD_DUST && flags == 0) { + RecordAbilityBattle(battler, ABILITY_SHIELD_DUST); + return STAT_CHANGE_DIDNT_WORK; + } + else if (flags == 0 && battlerHoldEffect == HOLD_EFFECT_COVERT_CLOAK) + { + RecordItemEffectBattle(battler, HOLD_EFFECT_COVERT_CLOAK); return STAT_CHANGE_DIDNT_WORK; } else // try to decrease { statValue = -GET_STAT_BUFF_VALUE(statValue); + if (gBattleMons[battler].statStages[statId] == 1) + statValue = -1; + else if (gBattleMons[battler].statStages[statId] == 2 && statValue < -2) + statValue = -2; gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; index = 1; if (statValue == -2) @@ -11812,20 +11957,36 @@ static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr) gBattleTextBuff2[3] = STRINGID_STATHARSHLY >> 8; index = 4; } + else if (statValue <= -3) + { + gBattleTextBuff2[1] = B_BUFF_STRING; + gBattleTextBuff2[2] = STRINGID_SEVERELY & 0xFF; + gBattleTextBuff2[3] = STRINGID_SEVERELY >> 8; + index = 4; + } gBattleTextBuff2[index++] = B_BUFF_STRING; gBattleTextBuff2[index++] = STRINGID_STATFELL; gBattleTextBuff2[index++] = STRINGID_STATFELL >> 8; gBattleTextBuff2[index] = B_BUFF_EOS; - if (gBattleMons[gActiveBattler].statStages[statId] == MIN_STAT_STAGE) + if (gBattleMons[battler].statStages[statId] == MIN_STAT_STAGE) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_DECREASE; + } else - gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL + { + gProtectStructs[battler].statFell = TRUE; // Eject pack, lash out + gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL + } } } else // stat increase { statValue = GET_STAT_BUFF_VALUE(statValue); + if (gBattleMons[battler].statStages[statId] == 11) + statValue = 1; + else if (gBattleMons[battler].statStages[statId] == 10 && statValue > 2) + statValue = 2; gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; index = 1; if (statValue == 2) @@ -11835,22 +11996,55 @@ static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr) gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8; index = 4; } + else if (statValue >= 3) + { + gBattleTextBuff2[1] = B_BUFF_STRING; + gBattleTextBuff2[2] = STRINGID_DRASTICALLY & 0xFF; + gBattleTextBuff2[3] = STRINGID_DRASTICALLY >> 8; + index = 4; + } gBattleTextBuff2[index++] = B_BUFF_STRING; gBattleTextBuff2[index++] = STRINGID_STATROSE; gBattleTextBuff2[index++] = STRINGID_STATROSE >> 8; gBattleTextBuff2[index] = B_BUFF_EOS; - if (gBattleMons[gActiveBattler].statStages[statId] == MAX_STAT_STAGE) + if (gBattleMons[battler].statStages[statId] == MAX_STAT_STAGE) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_INCREASE; + } else - gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); // B_MSG_ATTACKER_STAT_ROSE or B_MSG_DEFENDER_STAT_ROSE + { + gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); + gProtectStructs[battler].statRaised = TRUE; + + // check mirror herb + for (index = 0; index < gBattlersCount; index++) + { + if (GetBattlerSide(index) == GetBattlerSide(battler)) + continue; // Only triggers on opposing side + if (GetBattlerAbility(index) == ABILITY_OPPORTUNIST + && gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises + { + gProtectStructs[index].activateOpportunist = 2; // set stats to copy + gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk + gQueuedStatBoosts[index].statChanges[statId - 1] += statValue; // cumulative in case of multiple opponent boosts + } + else if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB + && gBattleMons[index].statStages[statId] < MAX_STAT_STAGE) + { + gProtectStructs[index].eatMirrorHerb = 1; + gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk + gQueuedStatBoosts[index].statChanges[statId - 1] = statValue; + } + } + } } - gBattleMons[gActiveBattler].statStages[statId] += statValue; - if (gBattleMons[gActiveBattler].statStages[statId] < MIN_STAT_STAGE) - gBattleMons[gActiveBattler].statStages[statId] = MIN_STAT_STAGE; - if (gBattleMons[gActiveBattler].statStages[statId] > MAX_STAT_STAGE) - gBattleMons[gActiveBattler].statStages[statId] = MAX_STAT_STAGE; + gBattleMons[battler].statStages[statId] += statValue; + if (gBattleMons[battler].statStages[statId] < MIN_STAT_STAGE) + gBattleMons[battler].statStages[statId] = MIN_STAT_STAGE; + if (gBattleMons[battler].statStages[statId] > MAX_STAT_STAGE) + gBattleMons[battler].statStages[statId] = MAX_STAT_STAGE; if (gBattleCommunication[MULTISTRING_CHOOSER] == B_MSG_STAT_WONT_INCREASE && flags & STAT_CHANGE_ALLOW_PTR) gMoveResultFlags |= MOVE_RESULT_MISSED; @@ -11864,18 +12058,15 @@ static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr) static void Cmd_statbuffchange(void) { CMD_ARGS(u16 flags, const u8 *failInstr); + u16 flags = cmd->flags; const u8 *ptrBefore = gBattlescriptCurrInstr; const u8 *failInstr = cmd->failInstr; - if (ChangeStatBuffs(gBattleScripting.statChanger & 0xF0, GET_STAT_BUFF_ID(gBattleScripting.statChanger), flags, failInstr) == STAT_CHANGE_WORKED) - { + if (ChangeStatBuffs(GET_STAT_BUFF_VALUE_WITH_SIGN(gBattleScripting.statChanger), GET_STAT_BUFF_ID(gBattleScripting.statChanger), flags, failInstr) == STAT_CHANGE_WORKED) gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattlescriptCurrInstr = ptrBefore; - } + else if (gBattlescriptCurrInstr == ptrBefore) // Prevent infinite looping. + gBattlescriptCurrInstr = failInstr; } // Haze @@ -15493,3 +15684,22 @@ void BS_CheckParentalBondCounter(void) gBattlescriptCurrInstr = cmd->nextInstr; } +static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 byIntimidate) +{ + switch (abilityDef) + { + case ABILITY_CLEAR_BODY: + case ABILITY_FULL_METAL_BODY: + case ABILITY_WHITE_SMOKE: + return TRUE; + case ABILITY_INNER_FOCUS: + case ABILITY_SCRAPPY: + case ABILITY_OWN_TEMPO: + case ABILITY_OBLIVIOUS: + if (byIntimidate && (B_UPDATED_INTIMIDATE >= GEN_8)) + return TRUE; + break; + } + return FALSE; +} +