Move adjustdamage to the damage calc (#8948)

This commit is contained in:
Alex 2026-01-20 12:30:00 +01:00 committed by GitHub
parent 7b4eb6ce40
commit 922db5ce4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 156 additions and 158 deletions

View File

@ -30,10 +30,6 @@
.byte B_SCR_OP_TYPECALC
.endm
.macro adjustdamage
.byte B_SCR_OP_ADJUSTDAMAGE
.endm
.macro multihitresultmessage
.byte B_SCR_OP_MULTIHITRESULTMESSAGE
.endm
@ -1339,6 +1335,10 @@
.4byte \failInstr
.endm
.macro unused_1
.byte B_SCR_UNUSED_1
.endm
.macro callnative func:req
.byte B_SCR_OP_CALLNATIVE
.4byte \func

View File

@ -563,7 +563,6 @@ BattleScript_EffectFling::
printstring STRINGID_PKMNFLUNG
waitmessage B_WAIT_TIME_SHORT
damagecalc
adjustdamage
removeitem BS_ATTACKER
attackanimation
waitanimation
@ -2230,7 +2229,6 @@ BattleScript_HitFromAccCheck::
setpreattackadditionaleffect
BattleScript_HitFromDamageCalc::
damagecalc
adjustdamage
BattleScript_HitFromAtkAnimation::
call BattleScript_Hit_RetFromAtkAnimation
BattleScript_MoveEnd::
@ -2242,7 +2240,6 @@ BattleScript_EffectHit_Ret::
BattleScript_EffectHit_RetFromAccCheck::
accuracycheck BattleScript_MoveMissedPause
damagecalc
adjustdamage
BattleScript_Hit_RetFromAtkAnimation::
attackanimation
waitanimation
@ -3588,7 +3585,6 @@ BattleScript_EffectSpitUp::
jumpifbyte CMP_EQUAL, cMISS_TYPE, B_MSG_PROTECTED, BattleScript_SpitUpFailProtect
accuracycheck BattleScript_MoveMissedPause
damagecalc
adjustdamage
stockpiletobasedamage
call BattleScript_Hit_RetFromAtkAnimation
removestockpilecounters
@ -4671,7 +4667,6 @@ BattleScript_BideAttack::
typecalc
clearmoveresultflags MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
copybidedmg
adjustdamage
setbyte sB_ANIM_TURN, 1
attackanimation
waitanimation
@ -5095,7 +5090,6 @@ BattleScript_MonTookFutureAttack::
jumpifmovehadnoeffect BattleScript_FutureAttackEnd
accuracycheck BattleScript_MoveMissedPause
damagecalc
adjustdamage
jumpifmovehadnoeffect BattleScript_DoFutureAttackResult
jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, B_MSG_FUTURE_SIGHT, BattleScript_FutureHitAnimDoomDesire
playanimation BS_ATTACKER, B_ANIM_FUTURE_SIGHT_HIT
@ -5809,7 +5803,6 @@ BattleScript_MoveUsedIsConfused::
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, FALSE, BattleScript_MoveUsedIsConfusedRet
BattleScript_DoSelfConfusionDmg::
cancelmultiturnmoves
adjustdamage
printstring STRINGID_ITHURTCONFUSION
waitmessage B_WAIT_TIME_LONG
effectivenesssound

View File

@ -10,7 +10,6 @@ enum BattleScriptOpcode
B_SCR_OP_CRITCALC,
B_SCR_OP_DAMAGECALC,
B_SCR_OP_TYPECALC,
B_SCR_OP_ADJUSTDAMAGE,
B_SCR_OP_MULTIHITRESULTMESSAGE,
B_SCR_OP_ATTACKANIMATION,
B_SCR_OP_WAITANIMATION,
@ -258,6 +257,11 @@ enum BattleScriptOpcode
B_SCR_OP_JUMPIFCAPTIVATEAFFECTED,
B_SCR_OP_SETNONVOLATILESTATUS,
B_SCR_OP_TRYOVERWRITEABILITY,
// Expansion users, please don't use any of the unused commands.
// They are reserved for expansion usage.
// Use callnatives instead.
B_SCR_OP_UNUSED_1,
B_SCR_OP_CALLNATIVE,
};

View File

@ -340,7 +340,6 @@ static void Cmd_printselectionstringfromtable(void);
static void Cmd_critcalc(void);
static void Cmd_damagecalc(void);
static void Cmd_typecalc(void);
static void Cmd_adjustdamage(void);
static void Cmd_multihitresultmessage(void);
static void Cmd_attackanimation(void);
static void Cmd_waitanimation(void);
@ -588,6 +587,7 @@ static void Cmd_averagestats(void);
static void Cmd_jumpifcaptivateaffected(void);
static void Cmd_setnonvolatilestatus(void);
static void Cmd_tryoverwriteability(void);
static void Cmd_unused_1(void);
static void Cmd_callnative(void);
void (*const gBattleScriptingCommandsTable[])(void) =
@ -599,7 +599,6 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_CRITCALC] = Cmd_critcalc,
[B_SCR_OP_DAMAGECALC] = Cmd_damagecalc,
[B_SCR_OP_TYPECALC] = Cmd_typecalc,
[B_SCR_OP_ADJUSTDAMAGE] = Cmd_adjustdamage,
[B_SCR_OP_MULTIHITRESULTMESSAGE] = Cmd_multihitresultmessage,
[B_SCR_OP_ATTACKANIMATION] = Cmd_attackanimation,
[B_SCR_OP_WAITANIMATION] = Cmd_waitanimation,
@ -847,6 +846,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_JUMPIFCAPTIVATEAFFECTED] = Cmd_jumpifcaptivateaffected,
[B_SCR_OP_SETNONVOLATILESTATUS] = Cmd_setnonvolatilestatus,
[B_SCR_OP_TRYOVERWRITEABILITY] = Cmd_tryoverwriteability,
[B_SCR_OP_UNUSED_1] = Cmd_unused_1,
[B_SCR_OP_CALLNATIVE] = Cmd_callnative,
};
@ -1277,7 +1277,8 @@ static void Cmd_damagecalc(void)
{
CMD_ARGS();
if (gBattleStruct->calculatedDamageDone)
// Test if unableToUseMove check is needed
if (gBattleStruct->calculatedDamageDone || gBattleStruct->unableToUseMove)
{
gBattlescriptCurrInstr = cmd->nextInstr;
return;
@ -1295,8 +1296,7 @@ static void Cmd_damagecalc(void)
if (IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove)))
{
u32 battlerDef;
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{
if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef))
continue;
@ -1304,6 +1304,7 @@ static void Cmd_damagecalc(void)
ctx.battlerDef = battlerDef;
CalculateAndSetMoveDamage(&ctx);
}
gBattleStruct->calculatedDamageDone = TRUE;
}
else
{
@ -1312,6 +1313,14 @@ static void Cmd_damagecalc(void)
}
gBattlescriptCurrInstr = cmd->nextInstr;
if (gSpecialStatuses[gBattlerAttacker].gemBoost
&& gBattleMons[gBattlerAttacker].item
&& IsAnyTargetAffected())
{
BattleScriptCall(BattleScript_GemActivates);
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
}
}
static void Cmd_typecalc(void)
@ -1334,120 +1343,6 @@ static void Cmd_typecalc(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_adjustdamage(void)
{
CMD_ARGS();
enum HoldEffect holdEffect;
u8 param;
u32 battlerDef;
u32 rand = Random() % 100;
u32 affectionScore;
enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove);
bool32 calcSpreadMoveDamage = IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove));
u32 enduredHit = 0;
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{
if (gBattleStruct->calculatedDamageDone)
break;
if (!calcSpreadMoveDamage && battlerDef != gBattlerTarget)
continue;
if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef))
continue;
if (DoesSubstituteBlockMove(gBattlerAttacker, battlerDef, gCurrentMove))
continue;
if (DoesDisguiseBlockMove(battlerDef, gCurrentMove))
continue;
if (GetBattlerAbility(battlerDef) == ABILITY_ICE_FACE && IsBattleMovePhysical(gCurrentMove) && gBattleMons[battlerDef].species == SPECIES_EISCUE)
{
// Damage deals typeless 0 HP.
gBattleStruct->moveResultFlags[battlerDef] &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE);
gBattleStruct->moveDamage[battlerDef] = 0;
RecordAbilityBattle(battlerDef, ABILITY_ICE_FACE);
gBattleMons[battlerDef].volatiles.triggerIceFace = TRUE;
// Form change will be done after attack animation in Cmd_resultmessage.
continue;
}
if (gBattleMons[battlerDef].hp > gBattleStruct->moveDamage[battlerDef])
continue;
holdEffect = GetBattlerHoldEffect(battlerDef);
param = GetBattlerHoldEffectParam(battlerDef);
affectionScore = GetBattlerAffectionHearts(battlerDef);
gPotentialItemEffectBattler = battlerDef;
if (moveEffect == EFFECT_FALSE_SWIPE)
{
enduredHit |= 1u << battlerDef;
}
else if (gBattleMons[battlerDef].volatiles.endured)
{
enduredHit |= 1u << battlerDef;
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_ENDURED;
}
else if (holdEffect == HOLD_EFFECT_FOCUS_BAND && rand < param)
{
enduredHit |= 1u << battlerDef;
RecordItemEffectBattle(battlerDef, holdEffect);
gLastUsedItem = gBattleMons[battlerDef].item;
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_HUNG_ON;
}
else if (GetConfig(CONFIG_STURDY) >= GEN_5 && GetBattlerAbility(battlerDef) == ABILITY_STURDY && IsBattlerAtMaxHp(battlerDef))
{
enduredHit |= 1u << battlerDef;
RecordAbilityBattle(battlerDef, ABILITY_STURDY);
gLastUsedAbility = ABILITY_STURDY;
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_STURDIED;
}
else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && IsBattlerAtMaxHp(battlerDef))
{
enduredHit |= 1u << battlerDef;
RecordItemEffectBattle(battlerDef, holdEffect);
gLastUsedItem = gBattleMons[battlerDef].item;
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_HUNG_ON;
}
else if (B_AFFECTION_MECHANICS == TRUE && IsOnPlayerSide(battlerDef) && affectionScore >= AFFECTION_THREE_HEARTS)
{
if ((affectionScore == AFFECTION_FIVE_HEARTS && rand < 20)
|| (affectionScore == AFFECTION_FOUR_HEARTS && rand < 15)
|| (affectionScore == AFFECTION_THREE_HEARTS && rand < 10))
{
enduredHit |= 1u << battlerDef;
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_ENDURED_AFFECTION;
}
}
// Handle reducing the dmg to 1 hp.
if (enduredHit & 1u << battlerDef)
{
gBattleStruct->moveDamage[battlerDef] = gBattleMons[battlerDef].hp - 1;
gProtectStructs[battlerDef].assuranceDoubled = TRUE;
}
}
if (calcSpreadMoveDamage)
gBattleStruct->calculatedDamageDone = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
if (gSpecialStatuses[gBattlerAttacker].gemBoost
&& !IsBattlerUnaffectedByMove(gBattlerTarget)
&& !gBattleStruct->unableToUseMove
&& gBattleMons[gBattlerAttacker].item)
{
BattleScriptCall(BattleScript_GemActivates);
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
}
}
static void Cmd_multihitresultmessage(void)
{
CMD_ARGS();
@ -1731,9 +1626,7 @@ static void DoublesHPBarReduction(void)
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{
if (IsBattlerUnaffectedByMove(battlerDef)
|| gBattleStruct->moveDamage[battlerDef] == 0
|| DoesSubstituteBlockMove(gBattlerAttacker, battlerDef, gCurrentMove)
|| DoesDisguiseBlockMove(battlerDef, gCurrentMove))
|| gBattleStruct->moveDamage[battlerDef] == 0)
continue;
s32 dmgUpdate = min(gBattleStruct->moveDamage[battlerDef], 10000);
@ -11833,6 +11726,10 @@ static void Cmd_tryoverwriteability(void)
}
}
static void Cmd_unused_1(void)
{
}
static void Cmd_callnative(void)
{
CMD_ARGS(void (*func)(void));

View File

@ -2381,6 +2381,7 @@ static enum MoveCanceler CancelerConfused(struct BattleContext *ctx)
dmgCtx.updateFlags = TRUE;
dmgCtx.isSelfInflicted = TRUE;
dmgCtx.fixedBasePower = 40;
gBattlerTarget = gBattlerAttacker;
gBattleStruct->passiveHpUpdate[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused;
return MOVE_STEP_FAILURE;
@ -9737,20 +9738,15 @@ s32 CalcCritChanceStageGen1(struct BattleContext *ctx)
if (critChance > 255)
critChance = 255;
// Prevented crits
if (gSideStatuses[ctx->battlerDef] & SIDE_STATUS_LUCKY_CHANT)
critChance = CRITICAL_HIT_BLOCKED;
else if (ctx->abilityDef == ABILITY_BATTLE_ARMOR || ctx->abilityDef == ABILITY_SHELL_ARMOR)
if (ctx->abilityDef == ABILITY_BATTLE_ARMOR || ctx->abilityDef == ABILITY_SHELL_ARMOR)
{
if (ctx->updateFlags)
RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef);
critChance = CRITICAL_HIT_BLOCKED;
}
// Guaranteed crits
else if (gBattleMons[ctx->battlerAtk].volatiles.laserFocus
|| MoveAlwaysCrits(ctx->move)
|| (ctx->abilityAtk == ABILITY_MERCILESS && gBattleMons[ctx->battlerDef].status1 & STATUS1_PSN_ANY))
|| MoveAlwaysCrits(ctx->move)
|| (ctx->abilityAtk == ABILITY_MERCILESS && gBattleMons[ctx->battlerDef].status1 & STATUS1_PSN_ANY))
{
critChance = CRITICAL_HIT_ALWAYS;
}
@ -9760,6 +9756,14 @@ s32 CalcCritChanceStageGen1(struct BattleContext *ctx)
static bool32 IsCriticalHit(struct BattleContext *ctx)
{
if (gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE))
return FALSE;
if (ctx->isSelfInflicted)
return FALSE;
if (gSideStatuses[ctx->battlerDef] & SIDE_STATUS_LUCKY_CHANT)
return FALSE;
bool32 isCrit = FALSE;
s32 critChance = 0;
@ -9768,21 +9772,16 @@ static bool32 IsCriticalHit(struct BattleContext *ctx)
else
critChance = CalcCritChanceStage(ctx);
if (gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE))
if (critChance == CRITICAL_HIT_BLOCKED)
isCrit = FALSE;
else if (critChance == -1)
isCrit = FALSE;
else if (critChance == -2)
else if (critChance == CRITICAL_HIT_ALWAYS)
isCrit = TRUE;
else if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_1)
isCrit = RandomChance(RNG_CRITICAL_HIT, critChance, 256);
else if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_2)
isCrit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(critChance), 256);
else
{
if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_1)
isCrit = RandomChance(RNG_CRITICAL_HIT, critChance, 256);
else if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_2)
isCrit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(critChance), 256);
else
isCrit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(critChance));
}
isCrit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(critChance));
// Counter for IF_CRITICAL_HITS_GE evolution condition.
if (isCrit && IsOnPlayerSide(ctx->battlerAtk)
@ -9793,21 +9792,95 @@ static bool32 IsCriticalHit(struct BattleContext *ctx)
return isCrit;
}
s32 GetAdjustedDamage(struct BattleContext *ctx, s32 damage)
{
if (DoesSubstituteBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->move)
|| DoesDisguiseBlockMove(ctx->battlerDef, ctx->move))
return damage; // No damage will be dealt
if (ctx->abilityDef == ABILITY_ICE_FACE && IsBattleMovePhysical(ctx->move) && gBattleMons[ctx->battlerDef].species == SPECIES_EISCUE)
{
gBattleStruct->moveResultFlags[ctx->battlerDef] &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE);
RecordAbilityBattle(ctx->battlerDef, ABILITY_ICE_FACE);
gBattleMons[ctx->battlerDef].volatiles.triggerIceFace = TRUE; // Form change will be done after attack animation in Cmd_resultmessage.
return 0; // Typeless damage 0 HP.
}
if (gBattleMons[ctx->battlerDef].hp > damage)
return damage;
bool32 enduredHit = FALSE;
u32 rand = Random() % 100;
u32 affectionScore = GetBattlerAffectionHearts(ctx->battlerDef);
if (GetMoveEffect(ctx->move) == EFFECT_FALSE_SWIPE)
{
enduredHit = TRUE;
}
else if (gBattleMons[ctx->battlerDef].volatiles.endured)
{
enduredHit = TRUE;
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_ENDURED;
}
else if (ctx->holdEffectDef == HOLD_EFFECT_FOCUS_BAND && rand < GetBattlerHoldEffectParam(ctx->battlerDef))
{
enduredHit = TRUE;
RecordItemEffectBattle(ctx->battlerDef, ctx->holdEffectDef);
gLastUsedItem = gBattleMons[ctx->battlerDef].item;
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_HUNG_ON;
}
else if (GetConfig(CONFIG_STURDY) >= GEN_5 && ctx->abilityDef == ABILITY_STURDY && IsBattlerAtMaxHp(ctx->battlerDef))
{
enduredHit = TRUE;
RecordAbilityBattle(ctx->battlerDef, ABILITY_STURDY);
gLastUsedAbility = ABILITY_STURDY;
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_STURDIED;
}
else if (ctx->holdEffectDef == HOLD_EFFECT_FOCUS_SASH && IsBattlerAtMaxHp(ctx->battlerDef))
{
enduredHit = TRUE;
RecordItemEffectBattle(ctx->battlerDef, ctx->holdEffectDef);
gLastUsedItem = gBattleMons[ctx->battlerDef].item;
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_HUNG_ON;
}
else if (B_AFFECTION_MECHANICS == TRUE && IsOnPlayerSide(ctx->battlerDef) && affectionScore >= AFFECTION_THREE_HEARTS)
{
if ((affectionScore == AFFECTION_FIVE_HEARTS && rand < 20)
|| (affectionScore == AFFECTION_FOUR_HEARTS && rand < 15)
|| (affectionScore == AFFECTION_THREE_HEARTS && rand < 10))
{
enduredHit = TRUE;
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_ENDURED_AFFECTION;
}
}
if (enduredHit)
{
damage = gBattleMons[ctx->battlerDef].hp - 1; // Reduce damage to 1 hp.
gProtectStructs[ctx->battlerDef].assuranceDoubled = TRUE;
}
return damage;
}
s32 CalculateMoveDamage(struct BattleContext *ctx)
{
s32 damage = 0;
ctx->abilityAtk = GetBattlerAbility(ctx->battlerAtk);
ctx->abilityDef = GetBattlerAbility(ctx->battlerDef);
ctx->holdEffectAtk = GetBattlerHoldEffect(ctx->battlerAtk);
ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef);
ctx->typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(ctx);
if (!ctx->isSelfInflicted)
ctx->isCrit = IsCriticalHit(ctx);
ctx->isCrit = IsCriticalHit(ctx);
if (IsFutureSightAttackerInParty(ctx->battlerAtk, ctx->battlerDef, ctx->move))
return DoFutureSightAttackDamageCalc(ctx);
damage = DoFutureSightAttackDamageCalc(ctx);
else
damage = DoMoveDamageCalc(ctx);
return DoMoveDamageCalc(ctx);
return GetAdjustedDamage(ctx, damage);
}
// for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once

View File

@ -52,3 +52,34 @@ SINGLE_BATTLE_TEST("Confusion self hit does not consume Gems")
MESSAGE("It hurt itself in its confusion!");
}
}
SINGLE_BATTLE_TEST("Confusion damage activates Focus Sash")
{
GIVEN {
ASSUME(GetItemHoldEffect(ITEM_FOCUS_SASH) == HOLD_EFFECT_FOCUS_SASH);
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(1); Item(ITEM_FOCUS_SASH); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_POUND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent);
HP_BAR(player); // Confusion damage
MESSAGE("Wobbuffet hung on using its Focus Sash!");
}
}
SINGLE_BATTLE_TEST("Confusion damage Breaks Ice Face")
{
GIVEN {
PLAYER(SPECIES_EISCUE) { Ability(ABILITY_ICE_FACE); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_FAIRY_WIND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent);
HP_BAR(player); // Confusion damage
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
} THEN {
EXPECT_EQ(player->species, SPECIES_EISCUE_NOICE);
}
}