General MoveEnd clean up and Fell Stinger adjustments (#6674)

This commit is contained in:
Alex 2025-05-07 00:30:43 +02:00 committed by GitHub
parent 1871d16ffa
commit 6fe5f6530a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 216 additions and 209 deletions

View File

@ -1954,10 +1954,6 @@
various BS_ATTACKER, VARIOUS_TRY_ACTIVATE_SOULHEART
.endm
.macro tryactivatefellstinger battler:req
various \battler, VARIOUS_TRY_ACTIVATE_FELL_STINGER
.endm
.macro playmoveanimation battler:req, move:req
various \battler, VARIOUS_PLAY_MOVE_ANIMATION
.2byte \move

View File

@ -5233,7 +5233,6 @@ BattleScript_FaintTarget::
dofaintanimation BS_TARGET
printstring STRINGID_TARGETFAINTED
cleareffectsonfaint BS_TARGET
tryactivatefellstinger BS_ATTACKER
tryactivatesoulheart
tryactivatereceiver BS_TARGET
trytrainerslidefirstdownmsg BS_TARGET

View File

@ -121,7 +121,6 @@ enum CmdVarious
VARIOUS_INSTANT_HP_DROP,
VARIOUS_CLEAR_STATUS,
VARIOUS_RESTORE_PP,
VARIOUS_TRY_ACTIVATE_FELL_STINGER,
VARIOUS_PLAY_MOVE_ANIMATION,
VARIOUS_SET_LUCKY_CHANT,
VARIOUS_SUCKER_PUNCH_CHECK,
@ -262,7 +261,7 @@ enum MoveEndEffects
MOVEEND_ATTACKER_VISIBLE,
MOVEEND_TARGET_VISIBLE,
MOVEEND_ITEM_EFFECTS_TARGET,
MOVEEND_MOVE_EFFECTS2,
MOVEEND_FIRST_MOVE_BLOCK,
MOVEEND_ITEM_EFFECTS_ALL,
MOVEEND_SYMBIOSIS,
MOVEEND_HIT_SWITCH_TARGET,
@ -271,14 +270,13 @@ enum MoveEndEffects
MOVEEND_SKY_DROP_CONFUSE,
MOVEEND_UPDATE_LAST_MOVES,
MOVEEND_MIRROR_MOVE,
MOVEEND_NEXT_TARGET, // Everything up until here is handled for each strike of a multi-hit move
MOVEEND_NEXT_TARGET, // Everything up until here is handled for each strike of a spread move
MOVEEND_MULTIHIT_MOVE,
MOVEEND_DEFROST,
MOVEEND_RECOIL,
MOVEEND_RAPID_SPIN,
MOVEEND_SECOND_MOVE_BLOCK,
MOVEEND_ITEM_EFFECTS_ATTACKER,
MOVEEND_ABILITY_BLOCK,
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Eject Pack
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist
MOVEEND_RED_CARD, // Red Card triggers before Eject Pack
MOVEEND_EJECT_BUTTON,
MOVEEND_LIFEORB_SHELLBELL, // Includes shell bell, throat spray, etc

View File

@ -15,6 +15,7 @@ enum GenConfigTag
GEN_CONFIG_MOODY_STATS,
GEN_CONFIG_BATTLE_BOND,
GEN_CONFIG_ATE_MULTIPLIER,
GEN_CONFIG_FELL_STINGER_STAT_RAISE,
GEN_CONFIG_COUNT
};

View File

@ -17,6 +17,7 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_TELEPORT_BEHAVIOR] = B_TELEPORT_BEHAVIOR,
[GEN_CONFIG_MOODY_STATS] = B_MOODY_ACC_EVASION,
[GEN_CONFIG_BATTLE_BOND] = B_BATTLE_BOND,
[GEN_CONFIG_FELL_STINGER_STAT_RAISE] = B_FELL_STINGER_STAT_RAISE,
[GEN_CONFIG_ATE_MULTIPLIER] = B_ATE_MULTIPLIER,
};

View File

@ -1540,8 +1540,18 @@ static bool32 AccuracyCalcHelper(u32 move, u32 battler)
gBattleStruct->moveResultFlags[battler] |= MOVE_RESULT_MISSED;
effect = TRUE;
}
else if (B_MINIMIZE_DMG_ACC >= GEN_6
&& (gStatuses3[battler] & STATUS3_MINIMIZED)
&& MoveIncreasesPowerToMinimizedTargets(move))
{
effect = TRUE;
}
else if (GetMoveAccuracy(move) == 0)
{
effect = TRUE;
}
if (HasWeatherEffect())
if (!effect && HasWeatherEffect())
{
if ((moveEffect == EFFECT_THUNDER || moveEffect == EFFECT_RAIN_ALWAYS_HIT)
&& IsBattlerWeatherAffected(battler, B_WEATHER_RAIN))
@ -1553,17 +1563,6 @@ static bool32 AccuracyCalcHelper(u32 move, u32 battler)
return effect;
}
if (B_MINIMIZE_DMG_ACC >= GEN_6
&& (gStatuses3[battler] & STATUS3_MINIMIZED)
&& MoveIncreasesPowerToMinimizedTargets(move))
{
effect = TRUE;
}
else if (GetMoveAccuracy(move) == 0)
{
effect = TRUE;
}
if (ability != ABILITY_NONE)
RecordAbilityBattle(gBattlerAttacker, ABILITY_NO_GUARD);
@ -6210,7 +6209,6 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move
if (gBattleStruct->battleBondBoost[side] & (1u << gBattlerPartyIndexes[battlerAtk]))
break;
if (GetGenConfig(GEN_CONFIG_BATTLE_BOND) < GEN_9 && gBattleMons[battlerAtk].species == SPECIES_GRENINJA_BATTLE_BOND)
{
// TODO: Convert this to a proper FORM_CHANGE type.
@ -6381,13 +6379,7 @@ static void Cmd_moveend(void)
effect = 1;
}
break;
case PROTECT_NONE:
case PROTECT_NORMAL:
case PROTECT_MAX_GUARD:
case PROTECT_WIDE_GUARD:
case PROTECT_QUICK_GUARD:
case PROTECT_CRAFTY_SHIELD:
case PROTECT_MAT_BLOCK:
default:
break;
}
@ -6410,33 +6402,27 @@ static void Cmd_moveend(void)
case MOVEEND_ABSORB:
if (moveEffect == EFFECT_ABSORB
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& !(gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK)
&& IsBattlerAlive(gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget))
{
if (gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK && IsHealingMove(gCurrentMove))
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, (gBattleStruct->moveDamage[gBattlerTarget] * GetMoveAbsorbPercentage(gCurrentMove) / 100));
gBattleStruct->moveDamage[gBattlerAttacker] = GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->moveDamage[gBattlerAttacker]);
gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE;
effect = TRUE;
if (GetBattlerAbility(gBattlerTarget) == ABILITY_LIQUID_OOZE)
{
gBattleScripting.moveendState++;
break;
gBattleStruct->moveDamage[gBattlerAttacker] *= -1;
gHitMarker |= HITMARKER_PASSIVE_DAMAGE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABSORB_OOZE;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EffectAbsorbLiquidOoze;
}
else if (IsBattlerAlive(gBattlerAttacker) && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
else
{
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, (gBattleStruct->moveDamage[gBattlerTarget] * GetMoveAbsorbPercentage(gCurrentMove) / 100));
gBattleStruct->moveDamage[gBattlerAttacker] = GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->moveDamage[gBattlerAttacker]);
gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE;
effect = TRUE;
if (GetBattlerAbility(gBattlerTarget) == ABILITY_LIQUID_OOZE)
{
gBattleStruct->moveDamage[gBattlerAttacker] *= -1;
gHitMarker |= HITMARKER_PASSIVE_DAMAGE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABSORB_OOZE;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EffectAbsorbLiquidOoze;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABSORB;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EffectAbsorb;
}
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABSORB;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EffectAbsorb;
}
}
gBattleScripting.moveendState++;
@ -6458,109 +6444,6 @@ static void Cmd_moveend(void)
}
gBattleScripting.moveendState++;
break;
case MOVEEND_DEFROST: // defrosting check
if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& gBattlerAttacker != gBattlerTarget
&& (moveType == TYPE_FIRE || CanBurnHitThaw(gCurrentMove))
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
{
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE;
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
MarkBattlerForControllerExec(gBattlerTarget);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove;
effect = TRUE;
}
if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE
&& IsBattlerAlive(gBattlerTarget)
&& gBattlerAttacker != gBattlerTarget
&& MoveThawsUser(originallyUsedMove)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
{
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FROSTBITE;
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
MarkBattlerForControllerExec(gBattlerTarget);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_FrostbiteHealedViaFireMove;
effect = TRUE;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_RECOIL:
{
u32 moveRecoil = GetMoveRecoil(gCurrentMove);
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
{
gBattleScripting.moveendState++;
break;
}
else if (GetMoveEffect(gCurrentMove) == EFFECT_RECOIL_IF_MISS
&& (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !gBattleStruct->noTargetPresent
&& IsBattlerAlive(gBattlerAttacker))
{
if (B_RECOIL_IF_MISS_DMG >= GEN_5 || (B_CRASH_IF_TARGET_IMMUNE == GEN_4 && gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_DOESNT_AFFECT_FOE))
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
else if (B_RECOIL_IF_MISS_DMG == GEN_4 && (GetNonDynamaxMaxHP(gBattlerTarget) / 2) < gBattleStruct->moveDamage[gBattlerTarget])
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
else // Fallback if B_RECOIL_IF_MISS_DMG is set to gen3 or lower.
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_RecoilIfMiss;
effect = TRUE;
}
else if (moveEffect == EFFECT_RECOIL
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& IsBattlerAlive(gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget))
{
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleStruct->moveDamage[gBattlerTarget] * max(1, moveRecoil) / 100);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
effect = TRUE;
}
else if (moveEffect == EFFECT_EXPLOSION && !IsAbilityOnField(ABILITY_DAMP))
{
gBattleStruct->moveDamage[gBattlerAttacker] = 0;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_FaintAttackerForExplosion;
effect = TRUE;
}
else if ((moveEffect == EFFECT_MAX_HP_50_RECOIL || moveEffect == EFFECT_MIND_BLOWN)
&& IsBattlerAlive(gBattlerAttacker)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MaxHp50Recoil;
effect = TRUE;
}
gBattleScripting.moveendState++;
break;
}
case MOVEEND_RAPID_SPIN:
if (GetMoveEffect(gCurrentMove) == EFFECT_RAPID_SPIN
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& IsBattlerTurnDamaged(gBattlerTarget))
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_RapidSpinAway;
effect = TRUE;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_ITEM_EFFECTS_ATTACKER:
if (ItemBattleEffects(ITEMEFFECT_MOVE_END, gBattlerAttacker, FALSE))
effect = TRUE;
gBattleScripting.moveendState++;
break;
case MOVEEND_SYNCHRONIZE_TARGET: // target synchronize
if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, 0))
effect = TRUE;
@ -6576,12 +6459,6 @@ static void Cmd_moveend(void)
effect = TRUE;
gBattleScripting.moveendState++;
break;
case MOVEEND_OPPORTUNIST:
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
else
gBattleScripting.moveendState++;
break;
case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities
if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0))
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
@ -6622,23 +6499,12 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
}
case MOVEEND_CHANGED_ITEMS: // changed held items
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleStruct->changedItems[i] != ITEM_NONE)
{
gBattleMons[i].item = gBattleStruct->changedItems[i];
gBattleStruct->changedItems[i] = ITEM_NONE;
}
}
gBattleScripting.moveendState++;
break;
case MOVEEND_ITEM_EFFECTS_TARGET:
if (ItemBattleEffects(ITEMEFFECT_TARGET, gBattlerTarget, FALSE))
effect = TRUE;
gBattleScripting.moveendState++;
break;
case MOVEEND_MOVE_EFFECTS2: // For effects which should happen after target items, for example Knock Off after damage from Rocky Helmet.
case MOVEEND_FIRST_MOVE_BLOCK: // For effects which should happen after target items, for example Knock Off after damage from Rocky Helmet.
{
switch (gBattleStruct->moveEffect2)
{
@ -6803,7 +6669,7 @@ static void Cmd_moveend(void)
}
gBattleScripting.moveendState++;
break;
case MOVEEND_SUBSTITUTE: // update substitute
case MOVEEND_SUBSTITUTE:
for (i = 0; i < gBattlersCount; i++)
{
if (gDisableStructs[i].substituteHP == 0)
@ -7062,6 +6928,126 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
}
case MOVEEND_DEFROST:
if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& gBattlerAttacker != gBattlerTarget
&& (moveType == TYPE_FIRE || CanBurnHitThaw(gCurrentMove))
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
{
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE;
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
MarkBattlerForControllerExec(gBattlerTarget);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove;
effect = TRUE;
}
if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE
&& IsBattlerAlive(gBattlerTarget)
&& gBattlerAttacker != gBattlerTarget
&& MoveThawsUser(originallyUsedMove)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
{
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FROSTBITE;
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
MarkBattlerForControllerExec(gBattlerTarget);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_FrostbiteHealedViaFireMove;
effect = TRUE;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_SECOND_MOVE_BLOCK:
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
{
gBattleScripting.moveendState++;
break;
}
switch (moveEffect)
{
case EFFECT_RECOIL_IF_MISS:
if (IsBattlerAlive(gBattlerAttacker)
&& (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !gBattleStruct->noTargetPresent)
{
if (B_RECOIL_IF_MISS_DMG >= GEN_5 || (B_CRASH_IF_TARGET_IMMUNE == GEN_4 && gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_DOESNT_AFFECT_FOE))
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
else if (B_RECOIL_IF_MISS_DMG == GEN_4 && (GetNonDynamaxMaxHP(gBattlerTarget) / 2) < gBattleStruct->moveDamage[gBattlerTarget])
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
else // Fallback if B_RECOIL_IF_MISS_DMG is set to gen3 or lower.
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_RecoilIfMiss;
effect = TRUE;
}
break;
case EFFECT_RECOIL:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleStruct->moveDamage[gBattlerTarget] * max(1, GetMoveRecoil(gCurrentMove)) / 100);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
effect = TRUE;
}
break;
case EFFECT_EXPLOSION:
if (!IsAbilityOnField(ABILITY_DAMP))
{
gBattleStruct->moveDamage[gBattlerAttacker] = 0;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_FaintAttackerForExplosion;
effect = TRUE;
}
break;
case EFFECT_MAX_HP_50_RECOIL:
case EFFECT_MIND_BLOWN:
if (IsBattlerAlive(gBattlerAttacker)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MaxHp50Recoil;
effect = TRUE;
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_RapidSpinAway;
effect = TRUE;
}
break;
case EFFECT_FELL_STINGER:
if (IsBattlerAlive(gBattlerAttacker)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_FellStingerRaisesStat;
effect = TRUE;
}
default:
break;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_ITEM_EFFECTS_ATTACKER:
if (ItemBattleEffects(ITEMEFFECT_MOVE_END, gBattlerAttacker, FALSE))
effect = TRUE;
gBattleScripting.moveendState++;
break;
case MOVEEND_ABILITY_BLOCK:
effect = HandleMoveEndAbilityBlock(gBattlerAttacker, gBattlerTarget, gCurrentMove);
gBattleScripting.moveendState++;
@ -7316,9 +7302,6 @@ static void Cmd_moveend(void)
if (!(emergencyExitBattlers & 1u << battler))
continue;
if (moveEffect == EFFECT_HIT_ESCAPE)
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
effect = TRUE;
gBattleScripting.battler = battler;
BattleScriptPushCursor();
@ -7349,6 +7332,12 @@ static void Cmd_moveend(void)
}
gBattleScripting.moveendState++;
break;
case MOVEEND_OPPORTUNIST:
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
else
gBattleScripting.moveendState++;
break;
case MOVEEND_SYMBIOSIS:
for (i = 0; i < gBattlersCount; i++)
{
@ -7376,6 +7365,17 @@ static void Cmd_moveend(void)
gBattleStruct->sameMoveTurns[gBattlerAttacker]++;
gBattleScripting.moveendState++;
break;
case MOVEEND_CHANGED_ITEMS:
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleStruct->changedItems[i] != ITEM_NONE)
{
gBattleMons[i].item = gBattleStruct->changedItems[i];
gBattleStruct->changedItems[i] = ITEM_NONE;
}
}
gBattleScripting.moveendState++;
break;
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)
gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
@ -9587,20 +9587,6 @@ static void Cmd_useitemonopponent(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static bool32 HasAttackerFaintedTarget(void)
{
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !IsBattleMoveStatus(gCurrentMove)
&& (gLastHitBy[gBattlerTarget] == 0xFF || gLastHitBy[gBattlerTarget] == gBattlerAttacker)
&& gBattleStruct->moveTarget[gBattlerAttacker] == gBattlerTarget
&& gBattlerTarget != gBattlerAttacker
&& gCurrentTurnActionNumber == GetBattlerTurnOrderNum(gBattlerAttacker)
&& (gChosenMove == gChosenMoveByBattler[gBattlerAttacker] || gChosenMove == gBattleMons[gBattlerAttacker].moves[gChosenMovePos] || gChosenMove == GetMaxMove(gBattlerAttacker, gChosenMoveByBattler[gBattlerAttacker])))
return TRUE;
else
return FALSE;
}
bool32 CanUseLastResort(u8 battler)
{
u32 i;
@ -10615,23 +10601,6 @@ static void Cmd_various(void)
gBattleStruct->soulheartBattlerId = 0;
break;
}
case VARIOUS_TRY_ACTIVATE_FELL_STINGER:
{
VARIOUS_ARGS();
if (GetMoveEffect(gCurrentMove) == EFFECT_FELL_STINGER
&& IsBattlerAlive(gBattlerAttacker)
&& HasAttackerFaintedTarget()
&& !NoAliveMonsForEitherParty()
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
SET_STATCHANGER(STAT_ATK, (B_FELL_STINGER_STAT_RAISE >= GEN_7 ? 3 : 2), FALSE);
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_FellStingerRaisesStat;
return;
}
break;
}
case VARIOUS_PLAY_MOVE_ANIMATION:
{
VARIOUS_ARGS(u16 move);

View File

@ -0,0 +1,43 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Fell Stinger raises user's Attack by 3 stages if it faints target")
{
u32 config;
PARAMETRIZE { config = GEN_7; }
PARAMETRIZE { config = GEN_6; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_FELL_STINGER_STAT_RAISE, config);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FELL_STINGER); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FELL_STINGER); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
if (config == GEN_7)
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3);
else
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("Fell Stinger doesn't raise user's Attack if it doesn't faint target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FELL_STINGER); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FELL_STINGER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, player);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
}
}