From fa7618174b42fd29864600e40bf21d35dd46d2be Mon Sep 17 00:00:00 2001 From: kleeenexfeu Date: Tue, 30 Nov 2021 19:03:45 +0100 Subject: [PATCH 1/6] fix multi hit/stormdrain-like interactions --- data/battle_scripts_1.s | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e9d019e430d0..4669bb8a101b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8072,7 +8072,8 @@ BattleScript_MoveStatDrain:: printstring STRINGID_TARGETABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG clearsemiinvulnerablebit - tryfaintmon BS_ATTACKER, FALSE, NULL + tryfaintmon BS_ATTACKER, FALSE, NULL @ this should be removed, unless I'm missing something + orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd BattleScript_MonMadeMoveUseless_PPLoss:: @@ -8083,7 +8084,7 @@ BattleScript_MonMadeMoveUseless:: call BattleScript_AbilityPopUp printstring STRINGID_PKMNSXMADEYUSELESS waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL + tryfaintmon BS_ATTACKER, FALSE, NULL @ this should be removed, unless I'm missing something orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8095,7 +8096,8 @@ BattleScript_FlashFireBoost:: call BattleScript_AbilityPopUp printfromtable gFlashFireStringIds waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL + tryfaintmon BS_ATTACKER, FALSE, NULL @ this should be removed, unless I'm missing something + orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd BattleScript_AbilityPreventsPhasingOut:: From 18773f6d661ffd1aa2e07b33c787d540a1bfe0b2 Mon Sep 17 00:00:00 2001 From: kleeenexfeu Date: Tue, 30 Nov 2021 22:03:13 +0100 Subject: [PATCH 2/6] turns out tryfaint mon is necessary, and one was even lacking --- data/battle_scripts_1.s | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 4669bb8a101b..e0b384b3a1c0 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8057,6 +8057,7 @@ BattleScript_MoveHPDrain:: datahpupdate BS_TARGET printstring STRINGID_PKMNRESTOREDHPUSING waitmessage B_WAIT_TIME_LONG + tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8072,7 +8073,7 @@ BattleScript_MoveStatDrain:: printstring STRINGID_TARGETABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG clearsemiinvulnerablebit - tryfaintmon BS_ATTACKER, FALSE, NULL @ this should be removed, unless I'm missing something + tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8084,7 +8085,7 @@ BattleScript_MonMadeMoveUseless:: call BattleScript_AbilityPopUp printstring STRINGID_PKMNSXMADEYUSELESS waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL @ this should be removed, unless I'm missing something + tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8096,7 +8097,7 @@ BattleScript_FlashFireBoost:: call BattleScript_AbilityPopUp printfromtable gFlashFireStringIds waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL @ this should be removed, unless I'm missing something + tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd From 72eab2025752d8a3962d9cc208aaac92a6c321b0 Mon Sep 17 00:00:00 2001 From: kleeenexfeu Date: Fri, 3 Dec 2021 12:19:13 +0100 Subject: [PATCH 3/6] mind blown and explosion fixes --- charmap.txt | 1 + data/battle_scripts_1.s | 100 +++++++-------------- include/battle_message.h | 1 + include/battle_scripts.h | 3 + include/constants/battle_script_commands.h | 19 ++-- src/battle_message.c | 5 +- src/battle_script_commands.c | 52 ++++++++++- 7 files changed, 100 insertions(+), 81 deletions(-) diff --git a/charmap.txt b/charmap.txt index e5e075316e0a..39a1558e89ed 100644 --- a/charmap.txt +++ b/charmap.txt @@ -409,6 +409,7 @@ B_DEF_TEAM1 = FD 3A B_DEF_TEAM2 = FD 3B B_ACTIVE_NAME = FD 3C B_ACTIVE_NAME2 = FD 3D @ no Illusion check +B_BATTLER_ABILITY_NAME_WITH_PREFIX = FD 3E @ uses gBattlerAbility, as the ability pop up battlescript @ indicates the end of a town/city name (before " TOWN" or " CITY") NAME_END = FC 00 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e0b384b3a1c0..710829752b0c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2622,6 +2622,19 @@ BattleScript_EffectPlaceholder: pause 5 printstring STRINGID_NOTDONEYET goto BattleScript_MoveEnd + +BattleScript_EffectMindBlown:: +BattleScript_EffectExplosion:: + attackcanceler + attackstring + ppreduce + attackanimation + waitanimation + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + critcalc + damagecalc + adjustdamage + goto BattleScript_HitFromEffectivenessSound BattleScript_EffectHit:: BattleScript_HitFromAtkCanceler:: @@ -2638,6 +2651,7 @@ BattleScript_HitFromCritCalc:: BattleScript_HitFromAtkAnimation:: attackanimation waitanimation +BattleScript_HitFromEffectivenessSound: effectivenesssound hitanimation BS_TARGET waitstate @@ -2878,69 +2892,6 @@ BattleScript_EffectParalyzeHit:: setmoveeffect MOVE_EFFECT_PARALYSIS goto BattleScript_EffectHit -BattleScript_EffectExplosion:: - attackcanceler - attackstring - ppreduce - faintifabilitynotdamp - setatkhptozero - waitstate - jumpifbyte CMP_NO_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_MISSED, BattleScript_ExplosionDoAnimStartLoop - call BattleScript_PreserveMissedBitDoMoveAnim - goto BattleScript_ExplosionLoop -BattleScript_ExplosionDoAnimStartLoop: - attackanimation - waitanimation -BattleScript_ExplosionLoop: - movevaluescleanup - critcalc - damagecalc - adjustdamage - accuracycheck BattleScript_ExplosionMissed, ACC_CURR_MOVE - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_TARGET, FALSE, NULL - moveendto MOVEEND_NEXT_TARGET - jumpifnexttargetvalid BattleScript_ExplosionLoop - tryfaintmon BS_ATTACKER, FALSE, NULL - moveendcase MOVEEND_CLEAR_BITS - end -BattleScript_ExplosionMissed: - effectivenesssound - resultmessage - waitmessage B_WAIT_TIME_LONG - moveendto MOVEEND_NEXT_TARGET - jumpifnexttargetvalid BattleScript_ExplosionLoop - tryfaintmon BS_ATTACKER, FALSE, NULL - end - -BattleScript_EffectMindBlown:: - attackcanceler - attackstring - ppreduce - faintifabilitynotdamp - dmg_1_2_attackerhp - healthbarupdate BS_ATTACKER - datahpupdate BS_ATTACKER - waitstate - jumpifbyte CMP_NO_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_MISSED, BattleScript_ExplosionDoAnimStartLoop - call BattleScript_PreserveMissedBitDoMoveAnim - goto BattleScript_ExplosionLoop - -BattleScript_PreserveMissedBitDoMoveAnim: - bichalfword gMoveResultFlags, MOVE_RESULT_MISSED - attackanimation - waitanimation - orhalfword gMoveResultFlags, MOVE_RESULT_MISSED - return - BattleScript_EffectDreamEater:: attackcanceler jumpifsubstituteblocks BattleScript_DreamEaterNoEffect @@ -4958,6 +4909,14 @@ BattleScript_ButItFailed:: resultmessage waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd + +BattleScript_ExplosionInVoid:: + attackstring + bichalfword gMoveResultFlags, MOVE_RESULT_FAILED @ to avoid getting "but it failed" and thus not play the animation + attackanimation + waitanimation + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED @ restores the flag + goto BattleScript_ButItFailedPpReduce BattleScript_NotAffected:: pause B_WAIT_TIME_SHORT @@ -7607,6 +7566,14 @@ BattleScript_DoRecoil:: tryfaintmon BS_ATTACKER, FALSE, NULL BattleScript_RecoilEnd:: return + +BattleScript_DoRecoilNoString:: @ mind blown, explosion + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE | HITMARKER_IGNORE_DISGUISE + healthbarupdate BS_ATTACKER + datahpupdate BS_ATTACKER + pause B_WAIT_TIME_SHORT + tryfaintmon BS_ATTACKER, FALSE, NULL + return BattleScript_EffectWithChance:: seteffectwithchance @@ -8037,12 +8004,11 @@ BattleScript_SturdyPreventsOHKO:: goto BattleScript_MoveEnd BattleScript_DampStopsExplosion:: + call BattleScript_FlushMessageBox @ smother transition when our second poke uses explosion in double pause B_WAIT_TIME_SHORT - copybyte gBattlerAbility, gBattlerTarget call BattleScript_AbilityPopUp printstring STRINGID_PKMNPREVENTSUSAGE pause B_WAIT_TIME_LONG - moveendto MOVEEND_NEXT_TARGET moveendcase MOVEEND_CLEAR_BITS end @@ -8057,7 +8023,6 @@ BattleScript_MoveHPDrain:: datahpupdate BS_TARGET printstring STRINGID_PKMNRESTOREDHPUSING waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8073,7 +8038,6 @@ BattleScript_MoveStatDrain:: printstring STRINGID_TARGETABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG clearsemiinvulnerablebit - tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8085,7 +8049,6 @@ BattleScript_MonMadeMoveUseless:: call BattleScript_AbilityPopUp printstring STRINGID_PKMNSXMADEYUSELESS waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd @@ -8097,7 +8060,6 @@ BattleScript_FlashFireBoost:: call BattleScript_AbilityPopUp printfromtable gFlashFireStringIds waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_ATTACKER, FALSE, NULL @ necessary for moves that faint the user and could change type (galvanized explosion..) orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd diff --git a/include/battle_message.h b/include/battle_message.h index 5c10f9e04a11..77fd2ab67c57 100644 --- a/include/battle_message.h +++ b/include/battle_message.h @@ -66,6 +66,7 @@ #define B_TXT_DEF_TEAM2 0x3B // your/the opposing #define B_TXT_ACTIVE_NAME 0x3C #define B_TXT_ACTIVE_NAME2 0x3D // no Illusion check +#define B_TXT_BATTLER_ABILITY_NAME_WITH_PREFIX 0x3E // uses gBattlerAbility, as the pop up ability battlescript // for B_TXT_BUFF1, B_TXT_BUFF2 and B_TXT_BUFF3 diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 415eaa6bb40c..fec525a67231 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -139,6 +139,8 @@ extern const u8 BattleScript_MoveEffectPayDay[]; extern const u8 BattleScript_MoveEffectWrap[]; extern const u8 BattleScript_MoveEffectConfusion[]; extern const u8 BattleScript_MoveEffectRecoil[]; +extern const u8 BattleScript_DoRecoil[]; +extern const u8 BattleScript_DoRecoilNoString[]; extern const u8 BattleScript_DoRecoil33[]; extern const u8 BattleScript_Recoil33End[]; extern const u8 BattleScript_ItemSteal[]; @@ -158,6 +160,7 @@ extern const u8 BattleScript_DroughtActivates[]; extern const u8 BattleScript_TookAttack[]; extern const u8 BattleScript_SturdyPreventsOHKO[]; extern const u8 BattleScript_DampStopsExplosion[]; +extern const u8 BattleScript_ExplosionInVoid[]; extern const u8 BattleScript_MoveHPDrain_PPLoss[]; extern const u8 BattleScript_MoveHPDrain[]; extern const u8 BattleScript_MonMadeMoveUseless_PPLoss[]; diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index bd16f741be4d..fb4c9370fa23 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -268,15 +268,16 @@ #define MOVEEND_UPDATE_LAST_MOVES 18 #define MOVEEND_MIRROR_MOVE 19 #define MOVEEND_NEXT_TARGET 20 // Everything up until here is handled for each strike of a multi-hit move -#define MOVEEND_EJECT_BUTTON 21 -#define MOVEEND_RED_CARD 22 -#define MOVEEND_EJECT_PACK 23 -#define MOVEEND_LIFEORB_SHELLBELL 24 // Includes shell bell, throat spray, etc -#define MOVEEND_PICKPOCKET 25 -#define MOVEEND_DANCER 26 -#define MOVEEND_EMERGENCY_EXIT 27 -#define MOVEEND_CLEAR_BITS 28 -#define MOVEEND_COUNT 29 +#define MOVEEND_KO_USER 21 // Explosion like move, also mind blown +#define MOVEEND_EJECT_BUTTON 22 +#define MOVEEND_RED_CARD 23 +#define MOVEEND_EJECT_PACK 24 +#define MOVEEND_LIFEORB_SHELLBELL 25 // Includes shell bell, throat spray, etc +#define MOVEEND_PICKPOCKET 26 +#define MOVEEND_DANCER 27 +#define MOVEEND_EMERGENCY_EXIT 28 +#define MOVEEND_CLEAR_BITS 29 +#define MOVEEND_COUNT 30 // switch cases #define B_SWITCH_NORMAL 0 diff --git a/src/battle_message.c b/src/battle_message.c index d64d02f902c4..d9397cb4a06a 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -270,7 +270,7 @@ static const u8 sText_PkmnCantUseMoveThroatChop[] = _("{B_ACTIVE_NAME_WITH_PREFI static const u8 sText_PkmnMadeItRain[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nmade it rain!"); static const u8 sText_PkmnRaisedSpeed[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nraised its SPEED!"); static const u8 sText_PkmnProtectedBy[] = _("{B_DEF_NAME_WITH_PREFIX} was protected\nby {B_DEF_ABILITY}!"); -static const u8 sText_PkmnPreventsUsage[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}\nprevents {B_ATK_NAME_WITH_PREFIX}\lfrom using {B_CURRENT_MOVE}!"); +static const u8 sText_PkmnPreventsUsage[] = _("{B_BATTLER_ABILITY_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}\nprevents {B_ATK_NAME_WITH_PREFIX}\lfrom using {B_CURRENT_MOVE}!"); static const u8 sText_PkmnRestoredHPUsing[] = _("{B_DEF_NAME_WITH_PREFIX} restored HP\nusing its {B_DEF_ABILITY}!"); static const u8 sText_PkmnsXMadeYUseless[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}\nmade {B_CURRENT_MOVE} useless!"); static const u8 sText_PkmnChangedTypeWith[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}\nmade it the {B_BUFF1} type!"); @@ -3199,6 +3199,9 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) case B_TXT_ACTIVE_NAME_WITH_PREFIX: // active battlerId name with prefix HANDLE_NICKNAME_STRING_CASE(gActiveBattler) break; + case B_TXT_BATTLER_ABILITY_NAME_WITH_PREFIX: + HANDLE_NICKNAME_STRING_CASE(gBattlerAbility) + break; case B_TXT_SCR_ACTIVE_NAME_WITH_PREFIX: // scripting active battlerId name with prefix HANDLE_NICKNAME_STRING_CASE(gBattleScripting.battler) break; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4ae42cb48776..4ae5523a78e1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1424,7 +1424,24 @@ static void Cmd_attackcanceler(void) gBattlescriptCurrInstr = BattleScript_ProteanActivates; return; } - + + // damp check for explosion and mind blown + if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && ((gBattleMoves[gCurrentMove].effect == EFFECT_EXPLOSION) || (gBattleMoves[gCurrentMove].effect == EFFECT_MIND_BLOWN))) + { + u8 battlerWithDamp = IsAbilityOnField(ABILITY_DAMP); + if (battlerWithDamp) + { + battlerWithDamp = battlerWithDamp - 1; // IsAbilityOnField returns the battlerId+1, so we put it back to being battlerId + gLastUsedAbility = ABILITY_DAMP; // for the string ability prevents usage move + RecordAbilityBattle(battlerWithDamp, ABILITY_DAMP); + gBattlerAbility = battlerWithDamp; // For ability pop-up + gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; + // I choose to not push the current battlescript because damps stops entirely these moves + return; + } + } + if (AtkCanceller_UnableToUseMove2()) return; if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0)) @@ -1461,6 +1478,12 @@ static void Cmd_attackcanceler(void) gHitMarker |= HITMARKER_OBEYS; if (NoTargetPresent(gCurrentMove) && (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))) { + if (gBattleMoves[gCurrentMove].effect == EFFECT_EXPLOSION) // explosion can happen in void and you die! + { + gBattlescriptCurrInstr = BattleScript_ExplosionInVoid; + return; + } + gBattlescriptCurrInstr = BattleScript_ButItFailedAtkStringPpReduce; if (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) CancelMultiTurnMoves(gBattlerAttacker); @@ -5266,10 +5289,35 @@ static void Cmd_moveend(void) RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove); gBattleScripting.moveendState++; break; + case MOVEEND_KO_USER: // Explosion/selfdestruct and mind blown + if (IsBattlerAlive(gBattlerAttacker) + && (gBattleMoves[gCurrentMove].effect == EFFECT_EXPLOSION) + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) // Only condition for explosion not to faint the user. Even a failed move KOes user + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_DoRecoilNoString; // instant bar drop doesn't exist in later generations, and it saves a battlescript command + effect = TRUE; + } + else if (gSpecialStatuses[gBattlerAttacker].parentalBondOn != 1 // parental bonded mind blown should only hurt user on FIRST and NOT SECOND STRIKE + // that's how it's written in bulbapedia at least at the time I'm writing this code (kleenexfeu) + && IsBattlerAlive(gBattlerAttacker) + && (gBattleMoves[gCurrentMove].effect == EFFECT_MIND_BLOWN) + && (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) + && !(gMoveResultFlags & MOVE_RESULT_FAILED) + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) + { + gBattleMoveDamage = ((gBattleMons[gBattlerAttacker].maxHP + 1)/2); // damage = half max HP of user rounded up + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_DoRecoilNoString; + effect = TRUE; + } + gBattleScripting.moveendState++; + break; case MOVEEND_EJECT_BUTTON: if (gCurrentMove != MOVE_DRAGON_TAIL && gCurrentMove != MOVE_CIRCLE_THROW - && IsBattlerAlive(gBattlerAttacker) + && IsBattlerAlive(gBattlerAttacker) // might have to do more check for explosion, because it currently doesn't activate eject button && !TestSheerForceFlag(gBattlerAttacker, gCurrentMove) && (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER || (gBattleTypeFlags & BATTLE_TYPE_TRAINER))) { From 1d170a9baa58440274f4dc960d9df9375159bb8c Mon Sep 17 00:00:00 2001 From: kleeenexfeu Date: Fri, 3 Dec 2021 12:59:16 +0100 Subject: [PATCH 4/6] spaces --- src/battle_script_commands.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4ae5523a78e1..0f40be32b9ae 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5299,19 +5299,19 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_DoRecoilNoString; // instant bar drop doesn't exist in later generations, and it saves a battlescript command effect = TRUE; } - else if (gSpecialStatuses[gBattlerAttacker].parentalBondOn != 1 // parental bonded mind blown should only hurt user on FIRST and NOT SECOND STRIKE - // that's how it's written in bulbapedia at least at the time I'm writing this code (kleenexfeu) + else if (gSpecialStatuses[gBattlerAttacker].parentalBondOn != 1 // parental bonded mind blown should only hurt user on FIRST and NOT SECOND STRIKE + // that's how it's written in bulbapedia at least at the time I'm writing this code (kleenexfeu) && IsBattlerAlive(gBattlerAttacker) && (gBattleMoves[gCurrentMove].effect == EFFECT_MIND_BLOWN) - && (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) - && !(gMoveResultFlags & MOVE_RESULT_FAILED) + && (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) + && !(gMoveResultFlags & MOVE_RESULT_FAILED) && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) - { + { gBattleMoveDamage = ((gBattleMons[gBattlerAttacker].maxHP + 1)/2); // damage = half max HP of user rounded up BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_DoRecoilNoString; effect = TRUE; - } + } gBattleScripting.moveendState++; break; case MOVEEND_EJECT_BUTTON: From 31dd3710c61b2441390ce21e5def180329c00472 Mon Sep 17 00:00:00 2001 From: kleeenexfeu Date: Fri, 3 Dec 2021 13:01:21 +0100 Subject: [PATCH 5/6] space --- 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 d9397cb4a06a..9e70d21fff7d 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3201,7 +3201,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) break; case B_TXT_BATTLER_ABILITY_NAME_WITH_PREFIX: HANDLE_NICKNAME_STRING_CASE(gBattlerAbility) - break; + break; case B_TXT_SCR_ACTIVE_NAME_WITH_PREFIX: // scripting active battlerId name with prefix HANDLE_NICKNAME_STRING_CASE(gBattleScripting.battler) break; From 0f719bd9923c23df50c27f26e018391a30300190 Mon Sep 17 00:00:00 2001 From: kleeenexfeu Date: Sat, 4 Dec 2021 14:45:20 +0100 Subject: [PATCH 6/6] mega related fixes --- asm/macros/battle_script.inc | 3 +- data/battle_scripts_1.s | 28 +++++--- include/battle.h | 10 +-- include/battle_scripts.h | 3 + src/battle_main.c | 136 +++++++++++++++++++---------------- src/battle_script_commands.c | 45 +++++------- src/battle_util.c | 18 ++++- 7 files changed, 135 insertions(+), 108 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 6ba63f916f63..6fb9b10408ec 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -669,7 +669,8 @@ .byte 0x77 .endm - .macro faintifabilitynotdamp +@ previously faintifabilitynotdamp + .macro canpursuitusermegaevolve .byte 0x78 .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 710829752b0c..647ca4114895 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6068,6 +6068,7 @@ BattleScript_DoSwitchOut2: BattleScript_PursuitDmgOnSwitchOut:: pause B_WAIT_TIME_SHORT + canpursuitusermegaevolve attackstring ppreduce critcalc @@ -7066,20 +7067,25 @@ BattleScript_FocusPunchSetUp:: BattleScript_MegaEvolution:: printstring STRINGID_MEGAEVOREACTING - waitmessage B_WAIT_TIME_LONG - setbyte gIsCriticalHit, 0 - handlemegaevo BS_ATTACKER, 0 - handlemegaevo BS_ATTACKER, 1 - playanimation BS_ATTACKER, B_ANIM_MEGA_EVOLUTION, NULL - waitanimation - handlemegaevo BS_ATTACKER, 2 - printstring STRINGID_MEGAEVOEVOLVED - waitmessage B_WAIT_TIME_LONG - switchinabilities BS_ATTACKER + call BattleScript_MegaEvolutionExecution end2 BattleScript_WishMegaEvolution:: printstring STRINGID_FERVENTWISHREACHED + call BattleScript_MegaEvolutionExecution + end2 + +BattleScript_MegaEvolutionPursuit:: + printstring STRINGID_MEGAEVOREACTING + call BattleScript_MegaEvolutionExecution + return + +BattleScript_WishMegaEvolutionPursuit:: + printstring STRINGID_FERVENTWISHREACHED + call BattleScript_MegaEvolutionExecution + return + +BattleScript_MegaEvolutionExecution:: @ for pursuit and it's better to have only one anyway waitmessage B_WAIT_TIME_LONG setbyte gIsCriticalHit, 0 handlemegaevo BS_ATTACKER, 0 @@ -7090,7 +7096,7 @@ BattleScript_WishMegaEvolution:: printstring STRINGID_MEGAEVOEVOLVED waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER - end2 + return BattleScript_PrimalReversion:: printstring STRINGID_EMPTYSTRING3 diff --git a/include/battle.h b/include/battle.h index 5658f603cf4b..e8f60fdd8479 100644 --- a/include/battle.h +++ b/include/battle.h @@ -17,7 +17,7 @@ #define GET_BATTLER_POSITION(battler) (gBattlerPositions[battler]) #define GET_BATTLER_SIDE(battler) (GetBattlerPosition(battler) & BIT_SIDE) #define GET_BATTLER_SIDE2(battler) (GET_BATTLER_POSITION(battler) & BIT_SIDE) - + // Battle Actions // These determine what each battler will do in a turn #define B_ACTION_USE_MOVE 0 @@ -474,8 +474,10 @@ struct MegaEvolutionData u8 battlerId; bool8 playerSelect; u8 triggerSpriteId; - bool8 isWishMegaEvo; + bool8 isWishMegaEvo[4]; // array to check whether the mega to be executed will be wish. bool8 isPrimalReversion; + u8 megaEvoWasDone; // used after executing the battlescript for mega evolution, to set gCurrentActionFuncId back to B_ACTION_USE_MOVE and not bypass the + // fastest mon's turn. Mega evolutions are done one by one, so a single byte (even bit) is enough }; struct Illusion @@ -792,8 +794,8 @@ struct MonSpritesGfx void* firstDecompressed; // ptr to the decompressed sprite of the first pokemon union { - void* ptr[4]; - u8* byte[4]; + void* ptr[4]; + u8* byte[4]; } sprites; struct SpriteTemplate templates[4]; struct SpriteFrameImage field_74[4][4]; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index fec525a67231..b31e469b9b41 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -287,7 +287,10 @@ extern const u8 BattleScript_ToxicSpikesFree[]; extern const u8 BattleScript_StickyWebFree[]; extern const u8 BattleScript_StealthRockFree[]; extern const u8 BattleScript_MegaEvolution[]; +extern const u8 BattleScript_MegaEvolutionPursuit[]; extern const u8 BattleScript_WishMegaEvolution[]; +extern const u8 BattleScript_WishMegaEvolutionPursuit[]; +extern const u8 BattleScript_MegaEvolutionExecution[]; extern const u8 BattleScript_MoveEffectRecoilWithStatus[]; extern const u8 BattleScript_EffectWithChance[]; extern const u8 BattleScript_MoveEffectClearSmog[]; diff --git a/src/battle_main.c b/src/battle_main.c index 766736048895..6f7c5e88eca5 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -104,10 +104,10 @@ static void RunTurnActionsFunctions(void); static void SetActionsAndBattlersTurnOrder(void); static void sub_803CDF8(void); static bool8 AllAtActionConfirmed(void); -static void TryChangeTurnOrder(void); static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void); -static void CheckMegaEvolutionBeforeTurn(void); static void CheckQuickClaw_CustapBerryActivation(void); +static u32 CheckMegaEvolutionBeforeMoves(void); +static void TryChangeTurnOrder(void); static void FreeResetData_ReturnToOvOrDoEvolutions(void); static void ReturnFromBattleToOverworld(void); static void TryEvolvePokemon(void); @@ -2896,8 +2896,8 @@ static void BattleStartClearSetData(void) } gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; - gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6); - gBattleScripting.monCaught = FALSE; + gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6); + gBattleScripting.monCaught = FALSE; gMultiHitCounter = 0; gBattleOutcome = 0; @@ -4572,8 +4572,8 @@ static void SetActionsAndBattlersTurnOrder(void) turnOrderId++; } } - gBattleMainFunc = CheckMegaEvolutionBeforeTurn; - gBattleStruct->mega.battlerId = 0; + gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; + gBattleStruct->focusPunchBattlerId = 0; return; } else @@ -4620,8 +4620,8 @@ static void SetActionsAndBattlersTurnOrder(void) } } } - gBattleMainFunc = CheckMegaEvolutionBeforeTurn; - gBattleStruct->mega.battlerId = 0; + gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; + gBattleStruct->focusPunchBattlerId = 0; } static void TurnValuesCleanUp(bool8 var0) @@ -4667,59 +4667,6 @@ void SpecialStatusesClear(void) memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); } -static void CheckMegaEvolutionBeforeTurn(void) -{ - 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)) - { - gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]); - gLastUsedItem = gBattleMons[gActiveBattler].item; - if (gBattleStruct->mega.isWishMegaEvo == TRUE) - BattleScriptExecute(BattleScript_WishMegaEvolution); - else - BattleScriptExecute(BattleScript_MegaEvolution); - return; - } - } - } - - #if B_MEGA_EVO_TURN_ORDER <= GEN_6 - gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; - #else - gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved - #endif -} - -// In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves -static void TryChangeTurnOrder(void) -{ - s32 i, j; - for (i = 0; i < gBattlersCount - 1; i++) - { - for (j = i + 1; j < gBattlersCount; j++) - { - u8 battler1 = gBattlerByTurnOrder[i]; - u8 battler2 = gBattlerByTurnOrder[j]; - if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE - && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE) - { - if (GetWhoStrikesFirst(battler1, battler2, FALSE)) - SwapTurnOrder(i, j); - } - } - } - gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; - gBattleStruct->focusPunchBattlerId = 0; -} - - static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) { u32 i; @@ -4809,10 +4756,77 @@ static void CheckQuickClaw_CustapBerryActivation(void) gBattleResources->battleScriptsStack->size = 0; } +static void TryChangeTurnOrder(void) +{ + s32 i, j; + for (i = 0; i < gBattlersCount - 1; i++) + { + for (j = i + 1; j < gBattlersCount; j++) + { + u8 battler1 = gBattlerByTurnOrder[i]; + u8 battler2 = gBattlerByTurnOrder[j]; + if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE + && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE) + { + if (GetWhoStrikesFirst(battler1, battler2, FALSE)) + SwapTurnOrder(i, j); + } + } + } +} + +static u32 CheckMegaEvolutionBeforeMoves(void) +{ + u32 val = 0; + if (gCurrentActionFuncId == B_ACTION_USE_MOVE) // mega evolution has priority only on moves + { + if (!(gHitMarker & HITMARKER_RUN)) + { + u8 remainingBattlersCount = gBattlersCount - gCurrentTurnActionNumber; + while (gBattleStruct->mega.battlerId < gBattlersCount && remainingBattlersCount > 0) + { + gBattleStruct->mega.battlerId = gBattlerByTurnOrder[gBattlersCount - remainingBattlersCount]; + gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId; + remainingBattlersCount--; + if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler] + && !(gProtectStructs[gActiveBattler].noValidMoves)) + { + gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]); + gLastUsedItem = gBattleMons[gActiveBattler].item; + if (gBattleStruct->mega.isWishMegaEvo[gActiveBattler] == TRUE) + BattleScriptExecute(BattleScript_WishMegaEvolution); + else + BattleScriptExecute(BattleScript_MegaEvolution); + val = 1; + return val; + } + } + } + } + return val; +} + static void RunTurnActionsFunctions(void) { if (gBattleOutcome != 0) gCurrentActionFuncId = B_ACTION_FINISHED; + else + { + if (gBattleStruct->mega.megaEvoWasDone) + { + gCurrentActionFuncId = B_ACTION_USE_MOVE; // restores gCurrentActionFuncId after mega battlescript was executed, thus allowing the mon to use move + gBattleStruct->mega.megaEvoWasDone = 0; + #if B_MEGA_EVO_TURN_ORDER > GEN_6 + TryChangeTurnOrder(); + #endif + } + + if (CheckMegaEvolutionBeforeMoves()) + { + gBattleStruct->mega.megaEvoWasDone = 1; + return; + } + } *(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber; sTurnActionsFuncsTable[gCurrentActionFuncId](); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0f40be32b9ae..12db6cac0cca 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -426,7 +426,7 @@ static void Cmd_hpthresholds2(void); static void Cmd_useitemonopponent(void); static void Cmd_various(void); static void Cmd_setprotectlike(void); -static void Cmd_faintifabilitynotdamp(void); +static void Cmd_canpursuitusermegaevolve(void); // previously faintifabilitynotdamp static void Cmd_setatkhptozero(void); static void Cmd_jumpifnexttargetvalid(void); static void Cmd_tryhealhalfhealth(void); @@ -685,7 +685,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_useitemonopponent, //0x75 Cmd_various, //0x76 Cmd_setprotectlike, //0x77 - Cmd_faintifabilitynotdamp, //0x78 + Cmd_canpursuitusermegaevolve, //0x78 previously faintifabilitynotdamp Cmd_setatkhptozero, //0x79 Cmd_jumpifnexttargetvalid, //0x7A Cmd_tryhealhalfhealth, //0x7B @@ -9430,32 +9430,21 @@ static void Cmd_setprotectlike(void) gBattlescriptCurrInstr++; } -static void Cmd_faintifabilitynotdamp(void) -{ - if (gBattleControllerExecFlags) - return; - - if ((gBattlerTarget = IsAbilityOnField(ABILITY_DAMP))) - { - gLastUsedAbility = ABILITY_DAMP; - RecordAbilityBattle(--gBattlerTarget, ABILITY_DAMP); - gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; - return; - } - - gActiveBattler = gBattlerAttacker; - gBattleMoveDamage = gBattleMons[gActiveBattler].hp; - BtlController_EmitHealthBarUpdate(0, INSTANT_HP_BAR_DROP); - MarkBattlerForControllerExec(gActiveBattler); - gBattlescriptCurrInstr++; - - for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) - { - if (gBattlerTarget == gBattlerAttacker) - continue; - if (IsBattlerAlive(gBattlerTarget)) - break; - } +static void Cmd_canpursuitusermegaevolve(void) // previously faintifabilitynotdamp +{ + gBattlescriptCurrInstr++; + gBattleStruct->mega.battlerId = gActiveBattler = gBattlerAttacker; + if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler] + && !(gProtectStructs[gActiveBattler].noValidMoves)) + { + gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]); + gLastUsedItem = gBattleMons[gActiveBattler].item; + BattleScriptPushCursor(); + if (gBattleStruct->mega.isWishMegaEvo[gActiveBattler] == TRUE) + BattleScriptExecute(BattleScript_WishMegaEvolutionPursuit); + else + BattleScriptExecute(BattleScript_MegaEvolutionPursuit); + } } static void Cmd_setatkhptozero(void) diff --git a/src/battle_util.c b/src/battle_util.c index 7e2320500c4a..56aaa8c95df3 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9105,6 +9105,11 @@ u16 GetWishMegaEvolutionSpecies(u16 preEvoSpecies, u16 moveId1, u16 moveId2, u16 return SPECIES_NONE; } +// This function should be tweaked in my opinion, here are my suggestions (kleenexfeu) in comment. +// Note that these suggestions fixed the AI not mega evolving first chance before ghoulash did the commit 14f974c9a95fe9801a744955973303b242036a11 +// After several playthrough with it, including watching battle records when I mega evolve, I couldn't find an issue with the new code. +// So even though the issue was solved with the revert, I still believe this is legacy code that should be changed. Please contact me if you think I'm +// making a mistake! bool32 CanMegaEvolve(u8 battlerId) { u32 itemId, holdEffect, species; @@ -9112,6 +9117,8 @@ bool32 CanMegaEvolve(u8 battlerId) u8 battlerPosition = GetBattlerPosition(battlerId); u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId)); struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]))->mega); + // The line above should be entirely removed. I couldn't find any other instance in the battle engine where the mega structure in accessed in + // such a way. #ifdef ITEM_EXPANSION // Check if Player has a Mega Ring @@ -9122,11 +9129,16 @@ bool32 CanMegaEvolve(u8 battlerId) // Check if trainer already mega evolved a pokemon. if (mega->alreadyEvolved[battlerPosition]) + // should be: if (gBattleStruct->mega.alreadyEvolved[battlerPosition]) + return FALSE; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (IsPartnerMonFromSameTrainer(battlerId) + && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) + // should be: && (gBattleStruct->mega.alreadyEvolved[partnerPosition] || (gBattleStruct->mega.toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) + return FALSE; } @@ -9152,14 +9164,14 @@ bool32 CanMegaEvolve(u8 battlerId) // Can Mega Evolve via Mega Stone. if (holdEffect == HOLD_EFFECT_MEGA_STONE) { - gBattleStruct->mega.isWishMegaEvo = FALSE; + gBattleStruct->mega.isWishMegaEvo[battlerId] = FALSE; return TRUE; } // Can undergo Primal Reversion. if (holdEffect == HOLD_EFFECT_PRIMAL_ORB) { - gBattleStruct->mega.isWishMegaEvo = FALSE; + gBattleStruct->mega.isWishMegaEvo[battlerId] = FALSE; gBattleStruct->mega.isPrimalReversion = TRUE; return TRUE; } @@ -9168,7 +9180,7 @@ bool32 CanMegaEvolve(u8 battlerId) // Check if there is an entry in the evolution table for Wish Mega Evolution. if (GetWishMegaEvolutionSpecies(species, GetMonData(mon, MON_DATA_MOVE1), GetMonData(mon, MON_DATA_MOVE2), GetMonData(mon, MON_DATA_MOVE3), GetMonData(mon, MON_DATA_MOVE4))) { - gBattleStruct->mega.isWishMegaEvo = TRUE; + gBattleStruct->mega.isWishMegaEvo[battlerId] = TRUE; return TRUE; }