From 5eb49722c0a14cf6197d41952c0dd5bc92a1f0ab Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 7 Jan 2021 19:33:39 -0700 Subject: [PATCH] add red card effect --- data/battle_scripts_1.s | 39 ++++++++++++++++++ include/battle.h | 1 + include/battle_scripts.h | 1 + include/battle_util.h | 3 +- include/constants/battle_script_commands.h | 6 +++ include/constants/battle_string_ids.h | 3 +- src/battle_main.c | 4 ++ src/battle_message.c | 2 + src/battle_script_commands.c | 40 +++++++++++++++---- src/battle_util.c | 46 ++++++++++++++++++++++ 10 files changed, 136 insertions(+), 9 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 40f4d0d938..e4e9802769 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5592,16 +5592,27 @@ BattleScript_RoarSuccessSwitch:: waitstate printstring STRINGID_PKMNWASDRAGGEDOUT switchineffects BS_TARGET + jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_RED_CARD, BattleScript_RoarSuccessSwitch_Ret + setbyte sSWITCH_CASE, B_SWITCH_NORMAL goto BattleScript_MoveEnd +BattleScript_RoarSuccessSwitch_Ret: + swapattackerwithtarget @ continuation of RedCardActivates + removeitem BS_TARGET + setbyte sSWITCH_CASE, B_SWITCH_NORMAL + return BattleScript_RoarSuccessEndBattle:: call BattleScript_RoarSuccessRet + setbyte sSWITCH_CASE, B_SWITCH_NORMAL setoutcomeonteleport BS_ATTACKER finishaction BattleScript_RoarSuccessRet: + jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_HIT, BattleScript_RoarSuccessRet_Ret + jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_RED_CARD, BattleScript_RoarSuccessRet_Ret attackanimation waitanimation +BattleScript_RoarSuccessRet_Ret: switchoutabilities BS_TARGET returntoball BS_TARGET waitstate @@ -7790,3 +7801,31 @@ BattleScript_AnnounceAirLockCloudNine:: waitmessage 0x40 call BattleScript_WeatherFormChanges end3 + +BattleScript_RedCardActivates:: + jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_RedCardEnd + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL + printstring STRINGID_REDCARDACTIVATE + waitmessage 0x40 + swapattackerwithtarget + jumpifstatus3 BS_EFFECT_BATTLER, STATUS3_ROOTED, BattleScript_RedCardIngrain + jumpifability BS_EFFECT_BATTLER, ABILITY_SUCTION_CUPS, BattleScript_RedCardSuctionCups + setbyte sSWITCH_CASE, B_SWITCH_RED_CARD + forcerandomswitch BattleScript_RedCardEnd + @ changes the current battle script. the rest happens in BattleScript_RoarSuccessSwitch_Ret, if switch is successful + return +BattleScript_RedCardEnd: + return +BattleScript_RedCardIngrain: + printstring STRINGID_PKMNANCHOREDITSELF + waitmessage 0x40 + removeitem BS_SCRIPTING + swapattackerwithtarget + return +BattleScript_RedCardSuctionCups: + printstring STRINGID_PKMNANCHORSITSELFWITH + waitmessage 0x40 + removeitem BS_SCRIPTING + swapattackerwithtarget + return + diff --git a/include/battle.h b/include/battle.h index 15658fdb5c..a7b23061d8 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 + u8 switchCase; // special switching conditions, eg. red card }; // rom_80A5C6C diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 7153eebb82..2c5134ade1 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -352,5 +352,6 @@ extern const u8 BattleScript_EmergencyExitWildNoPopUp[]; extern const u8 BattleScript_CheekPouchActivates[]; extern const u8 BattleScript_AnnounceAirLockCloudNine[]; extern const u8 BattleScript_AttackerItemStatRaise[]; +extern const u8 BattleScript_RedCardActivates[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_util.h b/include/battle_util.h index 40f92f7046..fe7972b493 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -132,5 +132,6 @@ bool8 ShouldGetStatBadgeBoost(u16 flagId, u8 battlerId); u8 GetBattleMoveSplit(u32 moveId); u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute); bool32 IsBattlerAffectedByHazards(u8 battlerId, bool32 toxicSpikes); - +void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast); +bool32 TestSheerForceFlag(u8 battler, u16 move); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index d06db22b47..17e02a50cd 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 sSWITCH_CASE gBattleScripting + 0x34 #define cMULTISTRING_CHOOSER gBattleCommunication + 5 @@ -234,4 +235,9 @@ #define BIT_ACC 0x40 #define BIT_EVASION 0x80 +// switch cases +#define B_SWITCH_NORMAL 0 +#define B_SWITCH_HIT 1 // dragon tail, circle throw +#define B_SWITCH_RED_CARD 2 + #endif // GUARD_CONSTANTS_BATTLE_SCRIPT_COMMANDS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 9348d67d5d..76ac139404 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -558,8 +558,9 @@ #define STRINGID_AURABREAKENTERS 554 #define STRINGID_COMATOSEENTERS 555 #define STRINGID_SCREENCLEANERENTERS 556 +#define STRINGID_REDCARDACTIVATE 557 -#define BATTLESTRINGS_COUNT 557 +#define BATTLESTRINGS_COUNT 558 //// multichoice message IDs // switch in ability message diff --git a/src/battle_main.c b/src/battle_main.c index 48129c5230..87f020227c 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3041,6 +3041,10 @@ void SwitchInClearSetData(void) gBattleResources->flags->flags[gActiveBattler] = 0; gCurrentMove = 0; gBattleStruct->arenaTurnCounter = 0xFF; + + // reset damage to prevent things like red card activating if the switched-in mon is holding it + gSpecialStatuses[gActiveBattler].physicalDmg = 0; + gSpecialStatuses[gActiveBattler].specialDmg = 0; ClearBattlerMoveHistory(gActiveBattler); ClearBattlerAbilityHistory(gActiveBattler); diff --git a/src/battle_message.c b/src/battle_message.c index 35ccc6e268..4a35e29390 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -685,9 +685,11 @@ static const u8 sText_FairyAuraActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} static const u8 sText_AuraBreakActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} reversed all\nother POKéMON's auras!"); static const u8 sText_ComatoseActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is drowsing!"); static const u8 sText_ScreenCleanerActivates[] = _("All screens on the field were\ncleansed!"); +static const u8 sText_RedCardActivate[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} held up its {B_LAST_ITEM}\nagainst {B_ATK_NAME_WITH_PREFIX}!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_REDCARDACTIVATE - 12] = sText_RedCardActivate, [STRINGID_STATWASNOTLOWERED - 12] = sText_StatWasNotLowered, [STRINGID_CLOAKEDINAFREEZINGLIGHT - 12] = sText_CloakedInAFreezingLight, [STRINGID_DESTINYKNOTACTIVATES - 12] = sText_DestinyKnotActivates, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6185c5231f..a5e959d9b9 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5010,22 +5010,47 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_EJECT_BUTTON: - /*if (gCurrentMove != MOVE_DRAGON_TAIL && gCurrentMove != MOVE_CIRCLE_THROW) - { - u8 battlers[4] = {0, 1, 2, 3}; - SortBattlersBySpeed - } - - BattleScript_ForceRandomSwitch*/ gBattleScripting.moveendState++; break; case MOVEEND_RED_CARD: + if (gCurrentMove != MOVE_DRAGON_TAIL + && gCurrentMove != MOVE_CIRCLE_THROW + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && IsBattlerAlive(gBattlerAttacker) + && !TestSheerForceFlag(gBattlerAttacker, gCurrentMove) + && (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER || (gBattleTypeFlags & BATTLE_TYPE_TRAINER))) + { + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + u8 battler = battlers[i]; + // attacker is the one to be switched out, battler is one with red card + if (battler != gBattlerAttacker + && IsBattlerAlive(battler) + && !DoesSubstituteBlockMove(gCurrentMove, gBattlerAttacker, battler) + && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RED_CARD + && (gSpecialStatuses[battler].physicalDmg != 0 || gSpecialStatuses[battler].specialDmg != 0)) + { + gLastUsedItem = gBattleMons[battler].item; + gActiveBattler = gBattleScripting.battler = battler; // battler with red card + gEffectBattler = gBattlerAttacker; + if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE) + gBattlescriptCurrInstr = BattleScript_MoveEnd; // prevent user switch-in selection + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_RedCardActivates; + effect = TRUE; + break; // only fastest red card activates + } + } + } gBattleScripting.moveendState++; break; case MOVEEND_EJECT_PACK: gBattleScripting.moveendState++; break; case MOVEEND_LIFE_ORB: + // TODO shell bell goes here too if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIFE_ORB && IsBattlerAlive(gBattlerAttacker) && !(GetBattlerAbility(gBattlerAttacker) == ABILITY_SHEER_FORCE && gBattleMoves[gCurrentMove].flags & FLAG_SHEER_FORCE_BOOST) @@ -7981,6 +8006,7 @@ static void Cmd_various(void) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED) { + gBattleScripting.switchCase = B_SWITCH_HIT; gBattlescriptCurrInstr = BattleScript_ForceRandomSwitch; } else diff --git a/src/battle_util.c b/src/battle_util.c index 56de96be33..df79bccea6 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7930,3 +7930,49 @@ bool32 IsBattlerAffectedByHazards(u8 battlerId, bool32 toxicSpikes) } return ret; } + +bool32 TestSheerForceFlag(u8 battler, u16 move) +{ + if (GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST) + return TRUE; + else + return FALSE; +} + +void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) +{ + int i, j, currSpeed, currBattler; + u16 speeds[4] = {0}; + + for (i = 0; i < gBattlersCount; i++) + speeds[i] = GetBattlerTotalSpeedStat(battlers[i]); + + for (i = 1; i < gBattlersCount; i++) + { + currBattler = battlers[i]; + currSpeed = speeds[i]; + j = i - 1; + + if (slowToFast) + { + while (j >= 0 && speeds[j] > currSpeed) + { + battlers[j + 1] = battlers[j]; + speeds[j + 1] = speeds[j]; + j = j - 1; + } + } + else + { + while (j >= 0 && speeds[j] < currSpeed) + { + battlers[j + 1] = battlers[j]; + speeds[j + 1] = speeds[j]; + j = j - 1; + } + } + + battlers[j + 1] = currBattler; + speeds[j + 1] = currSpeed; + } +}