diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 2d8abefbe9..aa2a5f8784 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1765,6 +1765,10 @@ .4byte \ptr .endm + .macro tryactivategrimneigh, battler:req + various \battler, VARIOUS_TRY_ACTIVATE_GRIM_NEIGH + .endm + @ helpful macros .macro setstatchanger stat:req, stages:req, down:req setbyte sSTATCHANGER \stat | \stages << 3 | \down << 7 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 4bbae8e8ea..fa30e86a1c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4966,8 +4966,9 @@ BattleScript_FaintTarget:: tryactivatefellstinger BS_ATTACKER tryactivatesoulheart tryactivatereceiver BS_TARGET - tryactivatemoxie BS_ATTACKER + tryactivatemoxie BS_ATTACKER @ and chilling neigh, as one ice rider tryactivatebeastboost BS_ATTACKER + tryactivategrimneigh BS_ATTACKER @ and as one shadow rider trytrainerslidefirstdownmsg BS_TARGET return @@ -6634,6 +6635,7 @@ BattleScript_AbilityPopUp: showabilitypopup BS_ABILITY_BATTLER recordability BS_ABILITY_BATTLER pause 40 + sethword sABILITY_OVERWRITE, 0 return BattleScript_SpeedBoostActivates:: @@ -7227,6 +7229,16 @@ BattleScript_WeakArmorSpeedAnim: BattleScript_WeakArmorActivatesEnd: return +BattleScript_RaiseStatOnFaintingTarget:: + copybyte gBattlerAbility, gBattlerAttacker + call BattleScript_AbilityPopUp + setgraphicalstatchangevalues + playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 + waitanimation + printstring STRINGID_LASTABILITYRAISEDSTAT + waitmessage 0x40 + return + BattleScript_AttackerAbilityStatRaise:: copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUp @@ -7256,6 +7268,18 @@ BattleScript_SwitchInAbilityMsg:: printfromtable gSwitchInAbilityStringIds waitmessage 0x40 end3 + +BattleScript_ActivateAsOne:: + call BattleScript_AbilityPopUp + printfromtable gSwitchInAbilityStringIds + waitmessage 0x40 + @ show unnerve + sethword sABILITY_OVERWRITE, ABILITY_UNNERVE + setbyte cMULTISTRING_CHOOSER, MULTI_SWITCHIN_UNNERVE + call BattleScript_AbilityPopUp + printfromtable gSwitchInAbilityStringIds + waitmessage 0x40 + end3 BattleScript_FriskMsgWithPopup:: copybyte gBattlerAbility, gBattlerAttacker diff --git a/include/battle.h b/include/battle.h index 3fced78f62..0644f4b1d6 100644 --- a/include/battle.h +++ b/include/battle.h @@ -614,6 +614,7 @@ struct BattleScripting u16 multihitMoveEffect; u8 illusionNickHack; // To properly display nick in STRINGID_ENEMYABOUTTOSWITCHPKMN. bool8 fixedPopup; // force ability popup to stick until manually called back + u16 abilityPopupOverwrite; }; // rom_80A5C6C diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 454b6ab345..ff35570088 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -26,6 +26,7 @@ u32 IsFlowerVeilProtected(u32 battler); u32 IsLeafGuardProtected(u32 battler); bool32 IsShieldsDownProtected(u32 battler); u32 IsAbilityStatusProtected(u32 battler); +bool32 TryResetBattlerStatChanges(u8 battler); extern void (* const gBattleScriptingCommandsTable[])(void); extern const u8 gBattlePalaceNatureToMoveGroupLikelihood[NUM_NATURES][4]; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index bea372f963..2d39d72eb3 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -358,5 +358,7 @@ extern const u8 BattleScript_CottonDownActivates[]; extern const u8 BattleScript_BallFetch[]; extern const u8 BattleScript_SandSpitActivates[]; extern const u8 BattleScript_PerishBodyActivates[]; +extern const u8 BattleScript_ActivateAsOne[]; +extern const u8 BattleScript_RaiseStatOnFaintingTarget[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 424009d20c..0b8b84d7c3 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -36,6 +36,7 @@ #define sMULTIHIT_EFFECT gBattleScripting + 0x30 #define sILLUSION_NICK_HACK gBattleScripting + 0x32 #define sFIXED_ABILITY_POPUP gBattleScripting + 0x33 +#define sABILITY_OVERWRITE gBattleScripting + 0x34 #define cMULTISTRING_CHOOSER gBattleCommunication + 5 @@ -168,6 +169,7 @@ #define VARIOUS_JUMP_IF_ABSENT 101 #define VARIOUS_DESTROY_ABILITY_POPUP 102 #define VARIOUS_TOTEM_BOOST 103 +#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 104 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index f89edc8ea2..2cadcb1ca5 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -470,7 +470,7 @@ #define STRINGID_FRISKACTIVATES 466 #define STRINGID_UNNERVEENTERS 467 #define STRINGID_HARVESTBERRY 468 -#define STRINGID_MOXIEATKRISE 469 +#define STRINGID_LASTABILITYRAISEDSTAT 469 #define STRINGID_MAGICBOUNCEACTIVATES 470 #define STRINGID_PROTEANTYPECHANGE 471 #define STRINGID_SYMBIOSISITEMPASS 472 @@ -564,8 +564,10 @@ #define STRINGID_PKMNSWILLPERISHIN3TURNS 560 #define STRINGID_ABILITYRAISEDSTATDRASTICALLY 561 #define STRINGID_AURAFLAREDTOLIFE 562 +#define STRINGID_ASONEENTERS 563 +#define STRINGID_CURIOUSMEDICINEENTERS 564 -#define BATTLESTRINGS_COUNT 563 +#define BATTLESTRINGS_COUNT 565 //// multichoice message IDs // switch in ability message @@ -582,5 +584,7 @@ #define MULTI_SWITCHIN_AURABREAK 10 #define MULTI_SWITCHIN_COMATOSE 11 #define MULTI_SWITCHIN_SCREENCLEANER 12 +#define MULTI_SWITCHIN_ASONE 13 +#define MULTI_SWITCHIN_CURIOUS_MEDICINE 14 #endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H diff --git a/src/battle_interface.c b/src/battle_interface.c index 137560ed10..5cd87c2c6e 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3014,6 +3014,9 @@ void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle) if (!B_ABILITY_POP_UP) return; + + if (gBattleScripting.abilityPopupOverwrite != 0) + ability = gBattleScripting.abilityPopupOverwrite; if (!gBattleStruct->activeAbilityPopUps) { diff --git a/src/battle_message.c b/src/battle_message.c index ff62d60a36..2dbce6b83b 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -601,7 +601,7 @@ static const u8 sText_SnowWarningHail[] = _("It started to hail!"); 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}!"); -static const u8 sText_MoxieAtkRise[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} raised its Attack!"); +static const u8 sText_LastAbilityRaisedBuff1[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}\nraised its {B_BUFF1}!"); static const u8 sText_MagicBounceActivates[] = _("The {B_DEF_NAME_WITH_PREFIX} bounced the\n{B_ATK_NAME_WITH_PREFIX} back!"); static const u8 sText_ProteanTypeChange[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} transformed\nit into the {B_BUFF1} type!"); static const u8 sText_SymbiosisItemPass[] = _("{B_ATK_NAME_WITH_PREFIX} passed its {B_LAST_ITEM}\nto {B_SCR_ACTIVE_NAME_WITH_PREFIX} through {B_ATK_ABILITY}!"); @@ -691,9 +691,13 @@ static const u8 sText_BattlerAbilityRaisedStat[] = _("{B_SCR_ACTIVE_NAME_WITH_PR static const u8 sText_ASandstormKickedUp[] = _("A sandstorm kicked up!"); static const u8 sText_PkmnsWillPerishIn3Turns[] = _("Both Pokémon will perish\nin three turns!"); static const u8 sText_AbilityRaisedStatDrastically[] = _("{B_DEF_ABILITY} raised {B_DEF_NAME_WITH_PREFIX}'s\n{B_BUFF1} drastically!"); +static const u8 sText_AsOneEnters[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} has two Abilities!"); +static const u8 sText_CuriousMedicineEnters[] = _("{B_EFF_NAME_WITH_PREFIX}'s\nstat changes were reset!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_CURIOUSMEDICINEENTERS - 12] = sText_CuriousMedicineEnters, + [STRINGID_ASONEENTERS - 12] = sText_AsOneEnters, [STRINGID_ABILITYRAISEDSTATDRASTICALLY - 12] = sText_AbilityRaisedStatDrastically, [STRINGID_PKMNSWILLPERISHIN3TURNS - 12] = sText_PkmnsWillPerishIn3Turns, [STRINGID_ASANDSTORMKICKEDUP - 12] = sText_ASandstormKickedUp, @@ -1185,7 +1189,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_FRISKACTIVATES - 12] = sText_FriskActivates, [STRINGID_UNNERVEENTERS - 12] = sText_UnnerveEnters, [STRINGID_HARVESTBERRY - 12] = sText_HarvestBerry, - [STRINGID_MOXIEATKRISE - 12] = sText_MoxieAtkRise, + [STRINGID_LASTABILITYRAISEDSTAT - 12] = sText_LastAbilityRaisedBuff1, [STRINGID_MAGICBOUNCEACTIVATES - 12] = sText_MagicBounceActivates, [STRINGID_PROTEANTYPECHANGE - 12] = sText_ProteanTypeChange, [STRINGID_SYMBIOSISITEMPASS - 12] = sText_SymbiosisItemPass, @@ -1287,6 +1291,8 @@ const u16 gSwitchInAbilityStringIds[] = [MULTI_SWITCHIN_AURABREAK] = STRINGID_AURABREAKENTERS, [MULTI_SWITCHIN_COMATOSE] = STRINGID_COMATOSEENTERS, [MULTI_SWITCHIN_SCREENCLEANER] = STRINGID_SCREENCLEANERENTERS, + [MULTI_SWITCHIN_ASONE] = STRINGID_ASONEENTERS, + [MULTI_SWITCHIN_CURIOUS_MEDICINE] = STRINGID_CURIOUSMEDICINEENTERS, }; const u16 gMissStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0fd75ca4ae..14460dbe71 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7533,17 +7533,40 @@ static void Cmd_various(void) BtlController_EmitSetMonData(0, REQUEST_PP_DATA_BATTLE, 0, 5, data); MarkBattlerForControllerExec(gActiveBattler); break; - case VARIOUS_TRY_ACTIVATE_MOXIE: - if (GetBattlerAbility(gActiveBattler) == ABILITY_MOXIE - && HasAttackerFaintedTarget() - && !NoAliveMonsForEitherParty() - && gBattleMons[gBattlerAttacker].statStages[STAT_ATK] != 12) + case VARIOUS_TRY_ACTIVATE_MOXIE: // and chilling neigh + as one ice rider + if ((GetBattlerAbility(gActiveBattler) == ABILITY_MOXIE + || GetBattlerAbility(gActiveBattler) == ABILITY_CHILLING_NEIGH + || GetBattlerAbility(gActiveBattler) == ABILITY_AS_ONE_ICE_RIDER) + && HasAttackerFaintedTarget() + && !NoAliveMonsForEitherParty() + && gBattleMons[gBattlerAttacker].statStages[STAT_ATK] != 12) { gBattleMons[gBattlerAttacker].statStages[STAT_ATK]++; SET_STATCHANGER(STAT_ATK, 1, FALSE); PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); BattleScriptPush(gBattlescriptCurrInstr + 3); - gBattlescriptCurrInstr = BattleScript_AttackerAbilityStatRaise; + gLastUsedAbility = GetBattlerAbility(gActiveBattler); + if (GetBattlerAbility(gActiveBattler) == ABILITY_AS_ONE_ICE_RIDER) + gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_CHILLING_NEIGH; + gBattlescriptCurrInstr = BattleScript_RaiseStatOnFaintingTarget; + return; + } + break; + case VARIOUS_TRY_ACTIVATE_GRIM_NEIGH: // and as one shadow rider + if ((GetBattlerAbility(gActiveBattler) == ABILITY_GRIM_NEIGH + || GetBattlerAbility(gActiveBattler) == ABILITY_AS_ONE_SHADOW_RIDER) + && HasAttackerFaintedTarget() + && !NoAliveMonsForEitherParty() + && gBattleMons[gBattlerAttacker].statStages[STAT_SPATK] != 12) + { + gBattleMons[gBattlerAttacker].statStages[STAT_SPATK]++; + SET_STATCHANGER(STAT_SPATK, 1, FALSE); + PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); + BattleScriptPush(gBattlescriptCurrInstr + 3); + gLastUsedAbility = GetBattlerAbility(gActiveBattler); + if (GetBattlerAbility(gActiveBattler) == ABILITY_AS_ONE_SHADOW_RIDER) + gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_GRIM_NEIGH; + gBattlescriptCurrInstr = BattleScript_RaiseStatOnFaintingTarget; return; } break; @@ -9098,17 +9121,30 @@ static void Cmd_statbuffchange(void) gBattlescriptCurrInstr = jumpPtr; } +bool32 TryResetBattlerStatChanges(u8 battler) +{ + u32 j; + bool32 ret = FALSE; + + gDisableStructs[battler].stockpileDef = 0; + gDisableStructs[battler].stockpileSpDef = 0; + for (j = 0; j < NUM_BATTLE_STATS; j++) + { + if (gBattleMons[battler].statStages[j] != DEFAULT_STAT_STAGE) + ret = TRUE; // returns TRUE if any stat was reset + + gBattleMons[battler].statStages[j] = DEFAULT_STAT_STAGE; + } + + return ret; +} + static void Cmd_normalisebuffs(void) // haze { s32 i, j; for (i = 0; i < gBattlersCount; i++) - { - gDisableStructs[i].stockpileDef = 0; - gDisableStructs[i].stockpileSpDef = 0; - for (j = 0; j < NUM_BATTLE_STATS; j++) - gBattleMons[i].statStages[j] = DEFAULT_STAT_STAGE; - } + TryResetBattlerStatChanges(i); gBattlescriptCurrInstr++; } diff --git a/src/battle_util.c b/src/battle_util.c index d6c56a0f91..22eb028854 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -53,6 +53,9 @@ match the ROM; this is also why sSoundMovesTable's declaration is in the middle functions instead of at the top of the file with the other declarations. */ +static bool32 TryRemoveScreens(u8 battler); +static bool32 IsUnnerveAbilityOnOpposingSide(u8 battlerId); + extern const u8 *const gBattleScriptsForMoveEffects[]; extern const u8 *const gBattlescriptsForBallThrow[]; extern const u8 *const gBattlescriptsForRunningByItem[]; @@ -3773,6 +3776,28 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move effect++; } break; + case ABILITY_AS_ONE_ICE_RIDER: + case ABILITY_AS_ONE_SHADOW_RIDER: + if (!gSpecialStatuses[battler].switchInAbilityDone) + { + gBattleCommunication[MULTISTRING_CHOOSER] = MULTI_SWITCHIN_ASONE; + gSpecialStatuses[battler].switchInAbilityDone = 1; + BattleScriptPushCursorAndCallback(BattleScript_ActivateAsOne); + effect++; + } + break; + case ABILITY_CURIOUS_MEDICINE: + if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() + && IsBattlerAlive(BATTLE_PARTNER(battler)) && TryResetBattlerStatChanges(BATTLE_PARTNER(battler))) + { + u32 i; + gEffectBattler = BATTLE_PARTNER(battler); + gBattleCommunication[MULTISTRING_CHOOSER] = MULTI_SWITCHIN_CURIOUS_MEDICINE; + gSpecialStatuses[battler].switchInAbilityDone = 1; + BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + effect++; + } + break; case ABILITY_ANTICIPATION: if (!gSpecialStatuses[battler].switchInAbilityDone) { @@ -3904,7 +3929,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITY_SCREEN_CLEANER: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (!gSpecialStatuses[battler].switchInAbilityDone && TryRemoveScreens(battler)) { gBattleCommunication[MULTISTRING_CHOOSER] = MULTI_SWITCHIN_SCREENCLEANER; gSpecialStatuses[battler].switchInAbilityDone = 1; @@ -5151,7 +5176,7 @@ static bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId) if (gBattleMons[battlerId].hp == 0) return FALSE; // Unnerve prevents consumption of opponents' berries. - if (isBerry && IsAbilityOnOpposingSide(battlerId, ABILITY_UNNERVE)) + if (isBerry && IsUnnerveAbilityOnOpposingSide(battlerId)) return FALSE; if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / hpFraction) return TRUE; @@ -5265,7 +5290,7 @@ static u8 ItemHealHp(u32 battlerId, u32 itemId, bool32 end2, bool32 percentHeal) static bool32 UnnerveOn(u32 battlerId, u32 itemId) { - if (ItemId_GetPocket(itemId) == POCKET_BERRIES && IsAbilityOnOpposingSide(battlerId, ABILITY_UNNERVE)) + if (ItemId_GetPocket(itemId) == POCKET_BERRIES && IsUnnerveAbilityOnOpposingSide(battlerId)) return TRUE; return FALSE; } @@ -6854,6 +6879,18 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe if (gBattleMoves[move].flags & FLAG_SOUND) MulModifier(&modifier, UQ_4_12(1.3)); break; + case ABILITY_STEELY_SPIRIT: + if (moveType == TYPE_STEEL) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_TRANSISTOR: + if (moveType == TYPE_ELECTRIC) + MulModifier(&modifier, UQ_4_12(1.5)); + break; + case ABILITY_DRAGONS_MAW: + if (moveType == TYPE_DRAGON) + MulModifier(&modifier, UQ_4_12(1.5)); + break; } // field abilities @@ -6878,6 +6915,10 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe case ABILITY_POWER_SPOT: MulModifier(&modifier, UQ_4_12(1.3)); break; + case ABILITY_STEELY_SPIRIT: + if (moveType == TYPE_STEEL) + MulModifier(&modifier, UQ_4_12(1.5)); + break; } } @@ -8015,3 +8056,42 @@ u8 GetBattleMoveSplit(u32 moveId) else return SPLIT_SPECIAL; } + +static bool32 TryRemoveScreens(u8 battler) +{ + bool32 removed = FALSE; + u8 battlerSide = GetBattlerSide(battler); + u8 enemySide = GetBattlerSide(BATTLE_OPPOSITE(battler)); + + // try to remove from battler's side + if (gSideStatuses[battlerSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)) + { + gSideStatuses[battlerSide] &= ~(SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL); + gSideTimers[battlerSide].reflectTimer = 0; + gSideTimers[battlerSide].lightscreenTimer = 0; + gSideTimers[battlerSide].auroraVeilTimer = 0; + removed = TRUE; + } + + // try to remove from battler opponent's side + if (gSideStatuses[enemySide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)) + { + gSideStatuses[enemySide] &= ~(SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL); + gSideTimers[enemySide].reflectTimer = 0; + gSideTimers[enemySide].lightscreenTimer = 0; + gSideTimers[enemySide].auroraVeilTimer = 0; + removed = TRUE; + } + + return removed; +} + +static bool32 IsUnnerveAbilityOnOpposingSide(u8 battlerId) +{ + if (IsAbilityOnOpposingSide(battlerId, ABILITY_UNNERVE) + || IsAbilityOnOpposingSide(battlerId, ABILITY_AS_ONE_ICE_RIDER) + || IsAbilityOnOpposingSide(battlerId, ABILITY_AS_ONE_SHADOW_RIDER)) + return TRUE; + return FALSE; +} +