Skip to content

Commit

Permalink
Fixes Spectral Thief stealing boost at the wrong time (#6197)
Browse files Browse the repository at this point in the history
Co-authored-by: Pawkkie <[email protected]>
  • Loading branch information
AlexOn1ine and Pawkkie authored Feb 7, 2025
1 parent 82b0eff commit de9e9af
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 119 deletions.
14 changes: 10 additions & 4 deletions asm/macros/battle_script.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,16 @@
callnative BS_RemoveTerrain
.endm

.macro tryspectralthiefsteal jumpInstr:req
callnative BS_TrySpectralThiefSteal
.4byte \jumpInstr
.endm

.macro spectralthiefprintstats
callnative BS_SpectralThiefPrintStats
.endm


@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
Expand Down Expand Up @@ -2045,10 +2055,6 @@
various \battler, VARIOUS_SET_POWDER
.endm

.macro spectralthiefprintstats
various BS_ATTACKER, VARIOUS_SPECTRAL_THIEF
.endm

.macro bringdownairbornebattler battler:req
various \battler, VARIOUS_GRAVITY_ON_AIRBORNE_MONS
.endm
Expand Down
18 changes: 17 additions & 1 deletion data/battle_scripts_1.s
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,23 @@ BattleScript_SpectralThiefSteal::
setbyte sB_ANIM_ARG2, 0
playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
spectralthiefprintstats
return
goto BattleScript_EffectSpectralThiefFromDamage

BattleScript_EffectSpectralThief::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
ppreduce
typecalc
tryspectralthiefsteal BattleScript_SpectralThiefSteal
BattleScript_EffectSpectralThiefFromDamage:
critcalc
damagecalc
adjustdamage
call BattleScript_Hit_RetFromAtkAnimation
tryfaintmon BS_TARGET
moveendall
end

BattleScript_EffectPartingShot::
attackcanceler
Expand Down
2 changes: 1 addition & 1 deletion include/battle_scripts.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ extern const u8 BattleScript_PsychicSurgeActivates[];
extern const u8 BattleScript_GrassySurgeActivates[];
extern const u8 BattleScript_MistySurgeActivates[];
extern const u8 BattleScript_ElectricSurgeActivates[];
extern const u8 BattleScript_SpectralThiefSteal[];
extern const u8 BattleScript_EffectSpectralThief[];
extern const u8 BattleScript_StatUpMsg[];
extern const u8 BattleScript_AbilityRaisesDefenderStat[];
extern const u8 BattleScript_PowderMoveNoEffect[];
Expand Down
59 changes: 29 additions & 30 deletions include/constants/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,36 +388,35 @@
#define MOVE_EFFECT_SMACK_DOWN 57
#define MOVE_EFFECT_FLAME_BURST 58
#define MOVE_EFFECT_FEINT 59
#define MOVE_EFFECT_SPECTRAL_THIEF 60
#define MOVE_EFFECT_V_CREATE 61
#define MOVE_EFFECT_HAPPY_HOUR 62
#define MOVE_EFFECT_CORE_ENFORCER 63
#define MOVE_EFFECT_THROAT_CHOP 64
#define MOVE_EFFECT_INCINERATE 65
#define MOVE_EFFECT_BUG_BITE 66
#define MOVE_EFFECT_RECOIL_HP_25 67
#define MOVE_EFFECT_TRAP_BOTH 68
#define MOVE_EFFECT_ROUND 69
#define MOVE_EFFECT_STOCKPILE_WORE_OFF 70
#define MOVE_EFFECT_DIRE_CLAW 71
#define MOVE_EFFECT_STEALTH_ROCK 72
#define MOVE_EFFECT_SPIKES 73
#define MOVE_EFFECT_SYRUP_BOMB 74
#define MOVE_EFFECT_FLORAL_HEALING 75
#define MOVE_EFFECT_SECRET_POWER 76
#define MOVE_EFFECT_PSYCHIC_NOISE 77
#define MOVE_EFFECT_TERA_BLAST 78
#define MOVE_EFFECT_ORDER_UP 79
#define MOVE_EFFECT_ION_DELUGE 80
#define MOVE_EFFECT_AROMATHERAPY 81 // No functionality yet
#define MOVE_EFFECT_HAZE 82
#define MOVE_EFFECT_LEECH_SEED 83
#define MOVE_EFFECT_REFLECT 84
#define MOVE_EFFECT_LIGHT_SCREEN 85
#define MOVE_EFFECT_SALT_CURE 86
#define MOVE_EFFECT_EERIE_SPELL 87

#define NUM_MOVE_EFFECTS 88
#define MOVE_EFFECT_V_CREATE 60
#define MOVE_EFFECT_HAPPY_HOUR 61
#define MOVE_EFFECT_CORE_ENFORCER 62
#define MOVE_EFFECT_THROAT_CHOP 63
#define MOVE_EFFECT_INCINERATE 64
#define MOVE_EFFECT_BUG_BITE 65
#define MOVE_EFFECT_RECOIL_HP_25 66
#define MOVE_EFFECT_TRAP_BOTH 67
#define MOVE_EFFECT_ROUND 68
#define MOVE_EFFECT_STOCKPILE_WORE_OFF 69
#define MOVE_EFFECT_DIRE_CLAW 70
#define MOVE_EFFECT_STEALTH_ROCK 71
#define MOVE_EFFECT_SPIKES 72
#define MOVE_EFFECT_SYRUP_BOMB 73
#define MOVE_EFFECT_FLORAL_HEALING 74
#define MOVE_EFFECT_SECRET_POWER 75
#define MOVE_EFFECT_PSYCHIC_NOISE 76
#define MOVE_EFFECT_TERA_BLAST 77
#define MOVE_EFFECT_ORDER_UP 78
#define MOVE_EFFECT_ION_DELUGE 79
#define MOVE_EFFECT_AROMATHERAPY 80 // No functionality yet
#define MOVE_EFFECT_HAZE 81
#define MOVE_EFFECT_LEECH_SEED 82
#define MOVE_EFFECT_REFLECT 83
#define MOVE_EFFECT_LIGHT_SCREEN 84
#define MOVE_EFFECT_SALT_CURE 85
#define MOVE_EFFECT_EERIE_SPELL 86

#define NUM_MOVE_EFFECTS 87

#define MOVE_EFFECT_AFFECTS_USER 0x2000
#define MOVE_EFFECT_CERTAIN 0x4000
Expand Down
1 change: 1 addition & 0 deletions include/constants/battle_move_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ enum {
EFFECT_SHELL_SIDE_ARM,
EFFECT_ORDER_UP,
EFFECT_RAPID_SPIN,
EFFECT_SPECTRAL_THIEF,
NUM_BATTLE_MOVE_EFFECTS,
};

Expand Down
1 change: 0 additions & 1 deletion include/constants/battle_script_commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ enum CmdVarious
VARIOUS_TRY_THIRD_TYPE,
VARIOUS_ACUPRESSURE,
VARIOUS_SET_POWDER,
VARIOUS_SPECTRAL_THIEF,
VARIOUS_GRAVITY_ON_AIRBORNE_MONS,
VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS,
VARIOUS_JUMP_IF_ROAR_FAILS,
Expand Down
8 changes: 3 additions & 5 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3814,7 +3814,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_FLATTER:
if (HasMoveEffect(battlerAtk, EFFECT_FOUL_PLAY)
|| HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP)
|| HasMoveWithAdditionalEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF))
|| HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF))
ADJUST_SCORE(DECENT_EFFECT);
if (aiData->abilities[battlerDef] == ABILITY_CONTRARY)
ADJUST_SCORE(GOOD_EFFECT);
Expand Down Expand Up @@ -4422,8 +4422,9 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0)
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
ADJUST_SCORE(GOOD_EFFECT);
case EFFECT_SPECTRAL_THIEF:
ADJUST_SCORE(AI_ShouldCopyStatChanges(battlerAtk, battlerDef));
break;

} // move effect checks

// check move additional effects that are likely to happen
Expand Down Expand Up @@ -4547,9 +4548,6 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case MOVE_EFFECT_CLEAR_SMOG:
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
break;
case MOVE_EFFECT_SPECTRAL_THIEF:
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
break;
case MOVE_EFFECT_BUG_BITE: // And pluck
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
Expand Down
143 changes: 77 additions & 66 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -3662,52 +3662,6 @@ void SetMoveEffect(bool32 primary, bool32 certain)
gBattlescriptCurrInstr = BattleScript_MoveEffectFeint;
}
break;
case MOVE_EFFECT_SPECTRAL_THIEF:
if (!NoAliveMonsForEitherParty())
{
bool32 contrary = (GetBattlerAbility(gBattlerAttacker) == ABILITY_CONTRARY);
gBattleStruct->stolenStats[0] = 0; // Stats to steal.
gBattleScripting.animArg1 = 0;
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
{
if (gBattleMons[gBattlerTarget].statStages[i] > DEFAULT_STAT_STAGE && gBattleMons[gBattlerAttacker].statStages[i] != MAX_STAT_STAGE)
{
bool32 byTwo = FALSE;

gBattleStruct->stolenStats[0] |= (1 << (i));
// Store by how many stages to raise the stat.
gBattleStruct->stolenStats[i] = gBattleMons[gBattlerTarget].statStages[i] - DEFAULT_STAT_STAGE;
while (gBattleMons[gBattlerAttacker].statStages[i] + gBattleStruct->stolenStats[i] > MAX_STAT_STAGE)
gBattleStruct->stolenStats[i]--;
gBattleMons[gBattlerTarget].statStages[i] = DEFAULT_STAT_STAGE;

if (gBattleStruct->stolenStats[i] >= 2)
byTwo++;

if (gBattleScripting.animArg1 == 0)
{
if (byTwo)
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + i;
else
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + i;
}
else
{
if (byTwo)
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2);
else
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1);
}
}
}

if (gBattleStruct->stolenStats[0] != 0)
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_SpectralThiefSteal;
}
}
break;
case MOVE_EFFECT_V_CREATE:
if (!NoAliveMonsForEitherParty())
{
Expand Down Expand Up @@ -9543,26 +9497,6 @@ static void Cmd_various(void)
gStatuses3[battler] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR | STATUS3_SKY_DROPPED);
break;
}
case VARIOUS_SPECTRAL_THIEF:
{
VARIOUS_ARGS();
// Raise stats
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
{
if (gBattleStruct->stolenStats[0] & (1u << i))
{
gBattleStruct->stolenStats[0] &= ~(1u << i);
SET_STATCHANGER(i, gBattleStruct->stolenStats[i], FALSE);
if (ChangeStatBuffs(GET_STAT_BUFF_VALUE_WITH_SIGN(gBattleScripting.statChanger), i, MOVE_EFFECT_CERTAIN | MOVE_EFFECT_AFFECTS_USER, NULL) == STAT_CHANGE_WORKED)
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_StatUpMsg;
return;
}
}
}
break;
}
case VARIOUS_SET_POWDER:
{
VARIOUS_ARGS();
Expand Down Expand Up @@ -17646,3 +17580,80 @@ void BS_RemoveTerrain(void)
RemoveAllTerrains();
gBattlescriptCurrInstr = cmd->nextInstr;
}

void BS_TrySpectralThiefSteal(void)
{
NATIVE_ARGS(const u8 *jumpInstr);

if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
{
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}

bool32 contrary = GetBattlerAbility(gBattlerAttacker) == ABILITY_CONTRARY;
gBattleStruct->stolenStats[0] = 0; // Stats to steal.
gBattleScripting.animArg1 = 0;
for (u32 stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++)
{
if (gBattleMons[gBattlerTarget].statStages[stat] > DEFAULT_STAT_STAGE && gBattleMons[gBattlerAttacker].statStages[stat] != MAX_STAT_STAGE)
{
bool32 byTwo = FALSE;

gBattleStruct->stolenStats[0] |= (1 << (stat));
// Store by how many stages to raise the stat.
gBattleStruct->stolenStats[stat] = gBattleMons[gBattlerTarget].statStages[stat] - DEFAULT_STAT_STAGE;

while (gBattleMons[gBattlerAttacker].statStages[stat] + gBattleStruct->stolenStats[stat] > MAX_STAT_STAGE)
gBattleStruct->stolenStats[stat]--;

gBattleMons[gBattlerTarget].statStages[stat] = DEFAULT_STAT_STAGE;

if (gBattleStruct->stolenStats[stat] >= 2)
byTwo++;

if (gBattleScripting.animArg1 == 0)
{
if (byTwo)
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + stat;
else
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + stat;
}
else
{
if (byTwo)
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2);
else
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1);
}
}
}

if (gBattleStruct->stolenStats[0] != 0)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}

void BS_SpectralThiefPrintStats(void)
{
NATIVE_ARGS();

for (u32 stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++)
{
if (gBattleStruct->stolenStats[0] & (1u << stat))
{
gBattleStruct->stolenStats[0] &= ~(1u << stat);
SET_STATCHANGER(stat, gBattleStruct->stolenStats[stat], FALSE);
if (ChangeStatBuffs(GET_STAT_BUFF_VALUE_WITH_SIGN(gBattleScripting.statChanger),
stat,
MOVE_EFFECT_CERTAIN | MOVE_EFFECT_AFFECTS_USER, NULL) == STAT_CHANGE_WORKED)
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_StatUpMsg;
return;
}
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
6 changes: 6 additions & 0 deletions src/data/battle_move_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -2225,4 +2225,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},

[EFFECT_SPECTRAL_THIEF] =
{
.battleScript = BattleScript_EffectSpectralThief,
.battleTvScore = 0, // TODO: Assign points
},
};
5 changes: 1 addition & 4 deletions src/data/moves_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -16562,7 +16562,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"Steals the target's stat\n"
"boosts, then attacks."),
.effect = EFFECT_HIT,
.effect = EFFECT_SPECTRAL_THIEF,
.power = 90,
.type = TYPE_GHOST,
.accuracy = 100,
Expand All @@ -16573,9 +16573,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.ignoresSubstitute = TRUE,
.makesContact = TRUE,
.metronomeBanned = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_SPECTRAL_THIEF,
}),
.contestEffect = CONTEST_EFFECT_APPEAL_AS_GOOD_AS_PREV_ONES,
.contestCategory = CONTEST_CATEGORY_SMART,
.contestComboStarterId = 0,
Expand Down
4 changes: 2 additions & 2 deletions test/battle/ability/big_pecks.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ SINGLE_BATTLE_TEST("Big Pecks doesn't prevent Spectral Thief from resetting posi
{
GIVEN {
ASSUME(gMovesInfo[MOVE_HARDEN].effect == EFFECT_DEFENSE_UP);
ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF));
ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF);
ASSUME(gMovesInfo[MOVE_SOAK].effect == EFFECT_SOAK);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
Expand All @@ -86,8 +86,8 @@ SINGLE_BATTLE_TEST("Big Pecks doesn't prevent Spectral Thief from resetting posi
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, opponent);
MESSAGE("The opposing Pidgey's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player);
MESSAGE("Wobbuffet stole the target's boosted stats!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
Expand Down
2 changes: 1 addition & 1 deletion test/battle/ability/clear_body.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent S
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }

GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF) == TRUE);
ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF);
ASSUME(gMovesInfo[MOVE_AGILITY].effect == EFFECT_SPEED_UP_2);
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(species) { Speed(5); Ability(ability); }
Expand Down
Loading

0 comments on commit de9e9af

Please sign in to comment.