Fixes activation order for a couple abilities (#7732)

This commit is contained in:
Alex 2025-09-27 18:47:10 +02:00 committed by GitHub
parent f9a8a0251a
commit 3c9f05b367
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 464 additions and 219 deletions

View File

@ -1011,8 +1011,9 @@
.4byte \jumpInstr
.endm
.macro tryrestorehpberry
.macro tryrestorehpberry battler:req
.byte 0xbb
.byte \battler
.endm
.macro halvehp failInstr:req

View File

@ -4966,7 +4966,7 @@ BattleScript_MagicRoomEnds::
setbyte gBattlerTarget, 0
BattleScript_MagicRoomHealingItemsLoop:
copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattlerTarget, 1
tryrestorehpberry
tryrestorehpberry BS_ATTACKER
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_MagicRoomHealingItemsLoop
end2
@ -5404,27 +5404,36 @@ BattleScript_SeedSowerActivates::
call BattleScript_ActivateTerrainEffects
return
BattleScript_AngerShellActivates::
BattleScript_BerserkActivates::
saveattacker
copybyte gBattlerAttacker, gBattlerTarget
copybyte gBattlerAttacker, gEffectBattler
call BattleScript_AbilityPopUp
jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_SPATK, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_SPEED, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_DEF, MIN_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPDEF, MIN_STAT_STAGE, BattleScript_RestoreAttackerButItFailed
BattleScript_AngerShellTryDef::
modifybattlerstatstage BS_ATTACKER, STAT_DEF, DECREASE, 1, BattleScript_AngerShellTrySpDef, ANIM_ON
BattleScript_AngerShellTrySpDef:
modifybattlerstatstage BS_ATTACKER, STAT_SPDEF, DECREASE, 1, BattleScript_AngerShellTryAttack, ANIM_ON
BattleScript_AngerShellTryAttack:
modifybattlerstatstage BS_ATTACKER, STAT_ATK, INCREASE, 1, BattleScript_AngerShellTrySpAtk, ANIM_ON
BattleScript_AngerShellTrySpAtk:
modifybattlerstatstage BS_ATTACKER, STAT_SPATK, INCREASE, 1, BattleScript_AngerShellTrySpeed, ANIM_ON
BattleScript_AngerShellTrySpeed:
modifybattlerstatstage BS_ATTACKER, STAT_SPEED, INCREASE, 1, BattleScript_AngerShellRet, ANIM_ON
BattleScript_AngerShellRet:
statbuffchange BS_EFFECT_BATTLER, STAT_CHANGE_CERTAIN, BattleScript_BerserkActivatesTryBerry
call BattleScript_StatUp
BattleScript_BerserkActivatesTryBerry:
restoreattacker
tryrestorehpberry BS_EFFECT_BATTLER
return
BattleScript_AngerShellActivates::
call BattleScript_AbilityPopUp
jumpifstat BS_EFFECT_BATTLER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_EFFECT_BATTLER, CMP_LESS_THAN, STAT_SPATK, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_EFFECT_BATTLER, CMP_LESS_THAN, STAT_SPEED, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_EFFECT_BATTLER, CMP_GREATER_THAN, STAT_DEF, MIN_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_EFFECT_BATTLER, CMP_EQUAL, STAT_SPDEF, MIN_STAT_STAGE, BattleScript_RestoreAttackerButItFailed
BattleScript_AngerShellTryDef::
modifybattlerstatstage BS_EFFECT_BATTLER, STAT_DEF, DECREASE, 1, BattleScript_AngerShellTrySpDef, ANIM_ON
BattleScript_AngerShellTrySpDef:
modifybattlerstatstage BS_EFFECT_BATTLER, STAT_SPDEF, DECREASE, 1, BattleScript_AngerShellTryAttack, ANIM_ON
BattleScript_AngerShellTryAttack:
modifybattlerstatstage BS_EFFECT_BATTLER, STAT_ATK, INCREASE, 1, BattleScript_AngerShellTrySpAtk, ANIM_ON
BattleScript_AngerShellTrySpAtk:
modifybattlerstatstage BS_EFFECT_BATTLER, STAT_SPATK, INCREASE, 1, BattleScript_AngerShellTrySpeed, ANIM_ON
BattleScript_AngerShellTrySpeed:
modifybattlerstatstage BS_EFFECT_BATTLER, STAT_SPEED, INCREASE, 1, BattleScript_AngerShellRet, ANIM_ON
BattleScript_AngerShellRet:
tryrestorehpberry BS_EFFECT_BATTLER
return
BattleScript_WindPowerActivates::
@ -5557,8 +5566,10 @@ BattleScript_DoFutureAttackResult:
tryfaintmon BS_TARGET
checkteamslost BattleScript_FutureAttackEnd
BattleScript_FutureAttackEnd::
moveendcase MOVEEND_SET_VALUES
moveendcase MOVEEND_RAGE
moveendcase MOVEEND_ABILITIES
moveendcase MOVEEND_COLOR_CHANGE
moveendfromto MOVEEND_ITEM_EFFECTS_ALL, MOVEEND_UPDATE_LAST_MOVES
setmoveresultflags 0
end2
@ -6142,7 +6153,7 @@ BattleScript_DoTurnDmg:
datahpupdate BS_ATTACKER
tryfaintmon BS_ATTACKER
checkteamslost BattleScript_DoTurnDmgEnd
tryrestorehpberry
tryrestorehpberry BS_ATTACKER
BattleScript_DoTurnDmgEnd:
end2
@ -6384,7 +6395,7 @@ BattleScript_YawnEnd:
BattleScript_EmbargoEndTurn::
printstring STRINGID_EMBARGOENDS
waitmessage B_WAIT_TIME_LONG
tryrestorehpberry
tryrestorehpberry BS_ATTACKER
end2
BattleScript_TelekinesisEndTurn::
@ -6674,7 +6685,7 @@ BattleScript_PickupActivates::
call BattleScript_AbilityPopUp
printstring STRINGID_XFOUNDONEY
waitmessage B_WAIT_TIME_LONG
tryrestorehpberry
tryrestorehpberry BS_ATTACKER
BattleScript_PickupActivatesEnd:
end2
@ -6684,7 +6695,7 @@ BattleScript_HarvestActivates::
call BattleScript_AbilityPopUp
printstring STRINGID_HARVESTBERRY
waitmessage B_WAIT_TIME_LONG
tryrestorehpberry
tryrestorehpberry BS_ATTACKER
BattleScript_HarvestActivatesEnd:
end2
@ -8174,6 +8185,10 @@ BattleScript_OpportunistStartCopyStats:
goto BattleScript_OpportunistStartCopyStats
BattleScript_OpportunistCopyStatChangeEnd:
setbyte sSTAT_ANIM_PLAYED, FALSE
return
BattleScript_OpportunistCopyStatChangeEnd3::
call BattleScript_OpportunistCopyStatChange
end3
BattleScript_TotemVar::
@ -8593,14 +8608,13 @@ BattleScript_SymbiosisActivates::
return
BattleScript_TargetAbilityStatRaiseRet::
copybyte sSAVED_BATTLER, gBattlerAttacker
copybyte gBattlerAbility, gEffectBattler
copybyte gBattlerAttacker, gBattlerTarget
saveattacker
copybyte gBattlerAttacker, gEffectBattler
call BattleScript_AbilityPopUp
statbuffchange BS_ATTACKER, STAT_CHANGE_CERTAIN, BattleScript_TargetAbilityStatRaiseRet_End
call BattleScript_StatUp
BattleScript_TargetAbilityStatRaiseRet_End:
copybyte gBattlerAttacker, sSAVED_BATTLER
restoreattacker
return
@@@ MAX MOVES @@@

View File

@ -607,8 +607,9 @@ struct BattleStruct
struct BattlerState battlerState[MAX_BATTLERS_COUNT];
struct PartyState partyState[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 eventBlockCounter;
u8 turnEffectsBattlerId;
u8 endTurnEventsCounter;
u8 turnEffectsBattlerId:4;
u8 moveEndBattlerId:4;
u16 wrappedMove[MAX_BATTLERS_COUNT];
u16 moveTarget[MAX_BATTLERS_COUNT];
u32 expShareExpValue;
@ -779,7 +780,8 @@ struct BattleStruct
u8 numHazards[NUM_BATTLE_SIDES];
u8 hazardsCounter:4; // Counter for applying hazard on switch in
enum SubmoveState submoveAnnouncement:2;
u8 padding2:2;
u8 toxicChainPriority:1; // If Toxic Chain will trigger on target, all other non volatiles will be blocked
u8 padding2:1;
};
struct AiBattleData
@ -901,7 +903,7 @@ struct BattleScripting
s32 savedDmg;
u16 savedMoveEffect; // For moves hitting multiple targets.
u16 moveEffect;
u16 multihitMoveEffect;
u16 unused_0x30;
u8 illusionNickHack; // To properly display nick in STRINGID_ENEMYABOUTTOSWITCHPKMN.
bool8 fixedPopup; // Force ability popup to stick until manually called back
u16 abilityPopupOverwrite;

View File

@ -57,9 +57,9 @@ enum FirstTurnEventsStates
FIRST_TURN_EVENTS_TOTEM_BOOST,
FIRST_TURN_EVENTS_NEUTRALIZING_GAS,
FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES,
FIRST_TURN_EVENTS_OPPORTUNIST_1,
FIRST_TURN_EVENTS_ITEM_EFFECTS,
FIRST_TURN_EVENTS_OPPORTUNIST_2,
FIRST_TURN_EVENTS_OPPORTUNIST,
FIRST_TURN_EVENTS_MIRROR_HERB,
FIRST_TURN_EVENTS_EJECT_PACK,
FIRST_TURN_EVENTS_END,
};

View File

@ -3,6 +3,7 @@
extern const u8 BattleScript_SupersweetSyrupActivates[];
extern const u8 BattleScript_OpportunistCopyStatChange[];
extern const u8 BattleScript_OpportunistCopyStatChangeEnd3[];
extern const u8 BattleScript_MirrorHerbCopyStatChange[];
extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[];
extern const u8 BattleScript_NotAffected[];
@ -461,6 +462,7 @@ extern const u8 BattleScript_RemoveFireType[];
extern const u8 BattleScript_TargetAbilityStatRaiseRet[];
extern const u8 BattleScript_RemoveElectricType[];
extern const u8 BattleScript_SeedSowerActivates[];
extern const u8 BattleScript_BerserkActivates[];
extern const u8 BattleScript_AngerShellActivates[];
extern const u8 BattleScript_WellBakedBodyActivates[];
extern const u8 BattleScript_WindRiderActivatesMoveEnd[];

View File

@ -43,6 +43,7 @@ enum {
ABILITYEFFECT_ON_SWITCHIN,
ABILITYEFFECT_ENDTURN,
ABILITYEFFECT_MOVE_END_ATTACKER,
ABILITYEFFECT_COLOR_CHANGE, // Color Change, Berserk, Anger Shell
ABILITYEFFECT_MOVE_END,
ABILITYEFFECT_IMMUNITY,
ABILITYEFFECT_SYNCHRONIZE,
@ -55,6 +56,7 @@ enum {
ABILITYEFFECT_SWITCH_IN_TERRAIN,
ABILITYEFFECT_SWITCH_IN_WEATHER,
ABILITYEFFECT_OPPORTUNIST,
ABILITYEFFECT_OPPORTUNIST_FIRST_TURN,
ABILITYEFFECT_SWITCH_IN_STATUSES,
};
@ -64,6 +66,7 @@ enum ItemCaseId
ITEMEFFECT_NONE,
ITEMEFFECT_ON_SWITCH_IN,
ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN,
ITEMEFFECT_LEFTOVERS, // Leftovers, Black Sludge
ITEMEFFECT_NORMAL,
ITEMEFFECT_TRY_HEALING,
ITEMEFFECT_MOVE_END,
@ -74,6 +77,8 @@ enum ItemCaseId
ITEMEFFECT_USE_LAST_ITEM, // move end effects for just the battler, not whole field
ITEMEFFECT_WHITE_HERB,
ITEMEFFECT_WHITE_HERB_ENDTURN,
ITEMEFFECT_MIRROR_HERB,
ITEMEFFECT_MIRROR_HERB_FIRST_TURN,
};
enum ItemEffect

View File

@ -33,7 +33,7 @@
#define sSAVED_DMG (gBattleScripting + 0x28) // savedDmg
#define sSAVED_MOVE_EFFECT (gBattleScripting + 0x2C) // savedMoveEffect
#define sMOVE_EFFECT (gBattleScripting + 0x2E) // moveEffect
#define sMULTIHIT_EFFECT (gBattleScripting + 0x30) // multihitMoveEffect
#define sUNUSED_0x30 (gBattleScripting + 0x30) // unused_0x30
#define sILLUSION_NICK_HACK (gBattleScripting + 0x32) // illusionNickHack
#define sFIXED_ABILITY_POPUP (gBattleScripting + 0x33) // fixedPopup
#define sABILITY_OVERWRITE (gBattleScripting + 0x34) // abilityPopupOverwrite
@ -128,7 +128,7 @@ enum CmdVarious
// cases for Cmd_moveend - Order matters!
enum MoveEndEffects
{
MOVEEND_SUM_DAMAGE,
MOVEEND_SET_VALUES,
MOVEEND_PROTECT_LIKE_EFFECT,
MOVEEND_ABSORB,
MOVEEND_RAGE,
@ -155,14 +155,16 @@ enum MoveEndEffects
MOVEEND_ITEM_EFFECTS_ATTACKER,
MOVEEND_ABILITY_BLOCK,
MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist
MOVEEND_COLOR_CHANGE, // Color Change / Berserk / Anger Shell
MOVEEND_RED_CARD, // Red Card triggers before Eject Pack
MOVEEND_EJECT_BUTTON,
MOVEEND_LIFEORB_SHELLBELL, // Includes shell bell, throat spray, etc
MOVEEND_LIFEORB_SHELLBELL, // Shell Bell / Life Orb / Throat Spray
MOVEEND_FORM_CHANGE,
MOVEEND_EMERGENCY_EXIT,
MOVEEND_EJECT_PACK,
MOVEEND_HIT_ESCAPE,
MOVEEND_OPPORTUNIST, // Occurs after other stat change items/abilities to try and copy the boosts
MOVEEND_MIRROR_HERB,
MOVEEND_PICKPOCKET,
MOVEEND_WHITE_HERB,
MOVEEND_THIRD_MOVE_BLOCK,

View File

@ -425,17 +425,22 @@ static bool32 HandleEndTurnWish(u32 battler)
static bool32 HandleEndTurnFirstEventBlock(u32 battler)
{
bool32 effect = FALSE;
u32 side;
if (!IsBattlerAlive(battler))
{
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId++;
return effect;
}
switch (gBattleStruct->eventBlockCounter)
{
case FIRST_EVENT_BLOCK_GMAX_MOVE_RESIDUAL: // TODO: Has to be split into 3 statuses and needs a queue
side = GetBattlerSide(battler);
if (gSideStatuses[side] & SIDE_STATUS_DAMAGE_NON_TYPES)
{
if (IsBattlerAlive(battler)
&& !IS_BATTLER_OF_TYPE(battler, gSideTimers[side].damageNonTypesType)
if (!IS_BATTLER_OF_TYPE(battler, gSideTimers[side].damageNonTypesType)
&& !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD))
{
gBattlerAttacker = battler;
@ -448,7 +453,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_SEA_OF_FIRE_DAMAGE:
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SEA_OF_FIRE && IsBattlerAlive(battler))
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SEA_OF_FIRE)
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
@ -484,7 +489,6 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
break;
case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL:
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN
&& IsBattlerAlive(battler)
&& !IsBattlerAtMaxHp(battler)
&& !gBattleMons[battler].volatiles.healBlock
&& !IsSemiInvulnerable(battler, CHECK_ALL)
@ -515,23 +519,12 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
break;
}
case FIRST_EVENT_BLOCK_HEAL_ITEMS:
{
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, TRUE);
switch (holdEffect)
{
case HOLD_EFFECT_LEFTOVERS:
case HOLD_EFFECT_BLACK_SLUDGE:
if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler))
effect = TRUE;
break;
default:
break;
}
if (ItemBattleEffects(ITEMEFFECT_LEFTOVERS, battler))
effect = TRUE;
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId++;
break;
}
}
return effect;
}

View File

@ -3864,23 +3864,18 @@ static void TryDoEventsBeforeFirstTurn(void)
case FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES:
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
{
i = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
if (TryPrimalReversion(i))
if (TryPrimalReversion(battler))
return;
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0) != 0)
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0))
return;
if (TryClearIllusion(i, ABILITYEFFECT_ON_SWITCHIN))
if (TryClearIllusion(battler, ABILITYEFFECT_ON_SWITCHIN))
return;
}
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++;
break;
case FIRST_TURN_EVENTS_OPPORTUNIST_1:
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
return;
gBattleStruct->eventsBeforeFirstTurnState++;
break;
case FIRST_TURN_EVENTS_ITEM_EFFECTS:
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
{
@ -3890,9 +3885,23 @@ static void TryDoEventsBeforeFirstTurn(void)
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++;
break;
case FIRST_TURN_EVENTS_OPPORTUNIST_2:
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
return;
case FIRST_TURN_EVENTS_OPPORTUNIST:
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
{
u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST_FIRST_TURN, battler, GetBattlerAbility(battler), 0, 0))
return;
}
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++;
break;
case FIRST_TURN_EVENTS_MIRROR_HERB:
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
{
if (ItemBattleEffects(ITEMEFFECT_MIRROR_HERB_FIRST_TURN, gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]))
return;
}
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventsBeforeFirstTurnState++;
break;
case FIRST_TURN_EVENTS_EJECT_PACK:
@ -3925,8 +3934,8 @@ static void TryDoEventsBeforeFirstTurn(void)
gBattleStruct->appearedInBattle |= 1u << gBattlerPartyIndexes[i];
}
*(&gBattleStruct->eventBlockCounter) = 0;
*(&gBattleStruct->turnEffectsBattlerId) = 0;
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId = 0;
gBattleScripting.moveendState = 0;
gBattleStruct->faintedActionsState = 0;
gBattleStruct->endTurnEventsCounter = 0;
@ -5354,7 +5363,6 @@ static void CheckChangingTurnOrderEffects(void)
gBattleMainFunc = RunTurnActionsFunctions;
gBattleCommunication[3] = 0;
gBattleCommunication[4] = 0;
gBattleScripting.multihitMoveEffect = 0;
gBattleResources->battleScriptsStack->size = 0;
}

View File

@ -352,7 +352,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_PKMNPROTECTEDBY] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} was protected by {B_DEF_ABILITY}!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSUSAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents {B_ATK_NAME_WITH_PREFIX2} from using {B_CURRENT_MOVE}!"), //I don't see this in SV text
[STRINGID_PKMNRESTOREDHPUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} restored HP using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup
[STRINGID_PKMNCHANGEDTYPEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} made it the {B_BUFF1} type!"), //not in gen 5+, ability popup
[STRINGID_PKMNCHANGEDTYPEWITH] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX}'s {B_EFF_ABILITY} made it the {B_BUFF1} type!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSROMANCEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents romance!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSCONFUSIONWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents confusion!"), //not in gen 5+, ability popup
[STRINGID_PKMNRAISEDFIREPOWERWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} raised the power of Fire-type moves!"), //not in gen 5+, ability popup

View File

@ -962,6 +962,26 @@ static const struct PickupItem sPickupTable[] =
#undef _
static void ValidateSavedBattlerCounts(void)
{
if (gBattleStruct->savedAttackerCount > 0)
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #endif
}
if (gBattleStruct->savedTargetCount > 0)
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #endif
}
}
static bool32 NoTargetPresent(u8 battler, u32 move)
{
if (!IsBattlerAlive(gBattlerTarget))
@ -4005,6 +4025,10 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
static bool32 CanApplyAdditionalEffect(const struct AdditionalEffect *additionalEffect)
{
// If Toxic Chain will activate it blocks all other non volatile effects
if (gBattleStruct->toxicChainPriority && additionalEffect->moveEffect <= MOVE_EFFECT_FROSTBITE)
return FALSE;
if (additionalEffect->self
&& NumAffectedSpreadMoveTargets() > 1
&& GetNextTarget(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove), TRUE) != MAX_BATTLERS_COUNT)
@ -4021,6 +4045,20 @@ static bool32 CanApplyAdditionalEffect(const struct AdditionalEffect *additional
return TRUE;
}
static void SetToxicChainPriority(void)
{
if (gBattleStruct->toxicChainPriority)
return;
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
if (abilityAtk == ABILITY_TOXIC_CHAIN
&& IsBattlerAlive(gBattlerTarget)
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, abilityAtk, GetBattlerAbility(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget)
&& RandomWeighted(RNG_TOXIC_CHAIN, 7, 3))
gBattleStruct->toxicChainPriority = TRUE;
}
static void Cmd_setadditionaleffects(void)
{
CMD_ARGS();
@ -4028,6 +4066,7 @@ static void Cmd_setadditionaleffects(void)
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
{
u32 numAdditionalEffects = GetMoveAdditionalEffectCount(gCurrentMove);
SetToxicChainPriority();
if (numAdditionalEffects > gBattleStruct->additionalEffectsCounter)
{
u32 percentChance;
@ -4076,8 +4115,6 @@ static void Cmd_setadditionaleffects(void)
gBattleScripting.moveEffect = 0;
gBattlescriptCurrInstr = cmd->nextInstr;
}
gBattleScripting.multihitMoveEffect = 0;
}
static void Cmd_seteffectprimary(void)
@ -4111,7 +4148,6 @@ static void Cmd_clearvolatile(void)
gProtectStructs[battler].chargingTurn = FALSE;
gBattlescriptCurrInstr = cmd->nextInstr;
gBattleScripting.multihitMoveEffect = 0;
}
static void Cmd_tryfaintmon(void)
@ -5849,8 +5885,9 @@ static void Cmd_moveend(void)
{
switch (gBattleScripting.moveendState)
{
case MOVEEND_SUM_DAMAGE: // Sum and store damage dealt for multi strike recoil
case MOVEEND_SET_VALUES:
gBattleScripting.savedDmg += gBattleStruct->moveDamage[gBattlerTarget];
gBattleStruct->moveEndBattlerId = 0;
gBattleScripting.moveendState++;
break;
case MOVEEND_PROTECT_LIKE_EFFECT:
@ -6417,6 +6454,18 @@ static void Cmd_moveend(void)
else
gBattleScripting.moveendState++;
break;
case MOVEEND_COLOR_CHANGE:
while (gBattleStruct->moveEndBattlerId < gBattlersCount)
{
u32 battler = gBattleStruct->moveEndBattlerId++;
if (battler == gBattlerAttacker)
continue;
if (AbilityBattleEffects(ABILITYEFFECT_COLOR_CHANGE, battler, GetBattlerAbility(battler), 0, 0))
return;
}
gBattleStruct->moveEndBattlerId = 0;
gBattleScripting.moveendState++;
break;
case MOVEEND_RED_CARD:
{
u32 redCardBattlers = 0, i;
@ -6648,10 +6697,24 @@ 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++;
while (gBattleStruct->moveEndBattlerId < gBattlersCount)
{
u32 battler = gBattleStruct->moveEndBattlerId++;
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, GetBattlerAbility(battler), 0, 0))
return;
}
gBattleStruct->moveEndBattlerId = 0;
gBattleScripting.moveendState++;
break;
case MOVEEND_MIRROR_HERB:
while (gBattleStruct->moveEndBattlerId < gBattlersCount)
{
u32 battler = gBattleStruct->moveEndBattlerId++;
if (ItemBattleEffects(ITEMEFFECT_MIRROR_HERB, battler))
return;
}
gBattleStruct->moveEndBattlerId = 0;
gBattleScripting.moveendState++;
break;
case MOVEEND_PICKPOCKET:
if (IsBattlerAlive(gBattlerAttacker)
@ -6782,25 +6845,7 @@ static void Cmd_moveend(void)
&& gBattleMons[gBattlerAttacker].volatiles.lockConfusionTurns != 1) // And won't end this turn
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE); // Cancel it
if (gBattleStruct->savedAttackerCount > 0)
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #endif
}
if (gBattleStruct->savedTargetCount > 0)
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #endif
}
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
ValidateSavedBattlerCounts();
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
gBattleStruct->battlerState[gBattlerAttacker].ateBoost = FALSE;
gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE;
@ -6814,12 +6859,15 @@ static void Cmd_moveend(void)
gBattleStruct->fickleBeamBoosted = FALSE;
gBattleStruct->battlerState[gBattlerAttacker].usedMicleBerry = FALSE;
gBattleStruct->noTargetPresent = FALSE;
gBattleStruct->toxicChainPriority = FALSE;
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
gBattleStruct->pledgeMove = FALSE;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
if (B_CHARGE >= GEN_9 && moveType == TYPE_ELECTRIC && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
gBattleMons[gBattlerAttacker].volatiles.charge = FALSE;
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
// check if Stellar type boost should be used up
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
@ -7732,25 +7780,24 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler))
return TRUE;
else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0))
return TRUE;
for (i = 0; i < gBattlersCount; i++)
{
if (i == battler)
continue;
switch (GetBattlerAbility(i))
u32 ability = GetBattlerAbility(i);
switch (ability)
{
case ABILITY_TRACE:
case ABILITY_COMMANDER:
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0))
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, ability, 0, 0))
return TRUE;
break;
case ABILITY_FORECAST:
case ABILITY_FLOWER_GIFT:
case ABILITY_PROTOSYNTHESIS:
if (AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, i, 0, 0, 0))
if (AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, i, ability, 0, 0))
return TRUE;
break;
}
@ -7758,6 +7805,17 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
return TRUE;
}
for (i = 0; i < gBattlersCount; i++)
{
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, i, GetBattlerAbility(i), 0, 0))
return TRUE;
}
for (i = 0; i < gBattlersCount; i++)
{
if (ItemBattleEffects(ITEMEFFECT_MIRROR_HERB, battler))
return TRUE;
}
for (i = 0; i < gBattlersCount; i++)
{
if (gBattlerByTurnOrder[i] == battler)
@ -11988,8 +12046,9 @@ static void Cmd_jumpifnopursuitswitchdmg(void)
static void Cmd_tryrestorehpberry(void)
{
CMD_ARGS();
if (TryRestoreHPBerries(gBattlerAttacker, ITEMEFFECT_TRY_HEALING))
CMD_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (TryRestoreHPBerries(battler, ITEMEFFECT_TRY_HEALING))
return;
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -910,7 +910,6 @@ void HandleAction_ActionFinished(void)
gBattleScripting.moveendState = 0;
gBattleCommunication[3] = 0;
gBattleCommunication[4] = 0;
gBattleScripting.multihitMoveEffect = 0;
gBattleResources->battleScriptsStack->size = 0;
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
@ -4727,6 +4726,48 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
}
break;
case ABILITYEFFECT_COLOR_CHANGE:
switch (gLastUsedAbility)
{
case ABILITY_COLOR_CHANGE:
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& !IS_BATTLER_OF_TYPE(battler, moveType)
&& move != MOVE_STRUGGLE
&& moveType != TYPE_STELLAR
&& moveType != TYPE_MYSTERY)
{
gEffectBattler = gBattlerAbility = battler;
SET_BATTLER_TYPE(battler, moveType);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
BattleScriptCall(BattleScript_ColorChangeActivates);
effect++;
}
break;
case ABILITY_BERSERK:
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
BattleScriptCall(BattleScript_BerserkActivates);
effect++;
}
break;
case ABILITY_ANGER_SHELL:
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& HadMoreThanHalfHpNowDoesnt(battler))
{
gEffectBattler = gBattlerAbility = battler;
BattleScriptCall(BattleScript_AngerShellActivates);
effect++;
}
break;
}
break;
case ABILITYEFFECT_MOVE_END: // Think contact abilities.
switch (gLastUsedAbility)
{
@ -4746,7 +4787,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& moveType == TYPE_DARK
&& CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
gEffectBattler = battler;
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
@ -4758,7 +4799,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST)
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
{
gEffectBattler = battler;
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_SPEED, 1, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
@ -4770,7 +4811,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& moveType == TYPE_WATER
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
{
gEffectBattler = battler;
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_DEF, 2, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
@ -4782,26 +4823,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerAlive(battler)
&& CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
{
gEffectBattler = battler;
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_DEF, 1, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
}
break;
case ABILITY_BERSERK:
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
{
gEffectBattler = battler;
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
}
break;
case ABILITY_WEAK_ARMOR:
if (IsBattlerTurnDamaged(battler)
&& IsBattlerAlive(battler)
@ -4894,21 +4921,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect++;
}
break;
case ABILITY_COLOR_CHANGE:
if (move != MOVE_STRUGGLE
&& !IsBattleMoveStatus(move)
&& IsBattlerTurnDamaged(battler)
&& !IS_BATTLER_OF_TYPE(battler, moveType)
&& moveType != TYPE_STELLAR
&& moveType != TYPE_MYSTERY
&& IsBattlerAlive(battler))
{
SET_BATTLER_TYPE(battler, moveType);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
BattleScriptCall(BattleScript_ColorChangeActivates);
effect++;
}
break;
case ABILITY_GOOEY:
case ABILITY_TANGLING_HAIR:
if (IsBattlerAlive(gBattlerAttacker)
@ -5123,7 +5135,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)
&& (moveType == TYPE_FIRE || moveType == TYPE_WATER))
{
gEffectBattler = battler;
gEffectBattler = gBattlerAbility = battler;
SET_STATCHANGER(STAT_SPEED, 6, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
@ -5209,24 +5221,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& moveType == TYPE_FIRE)
{
gEffectBattler = gBattlerTarget;
gEffectBattler = gBattlerAbility = gBattlerTarget;
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptCall(BattleScript_TargetAbilityStatRaiseRet);
effect++;
}
break;
case ABILITY_ANGER_SHELL:
if (!gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1) // Activates after all hits from a multi-hit move.
&& IsBattlerAlive(gBattlerTarget)
&& HadMoreThanHalfHpNowDoesnt(gBattlerTarget)
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)))
{
BattleScriptCall(BattleScript_AngerShellActivates);
effect++;
}
break;
case ABILITY_WIND_POWER:
if (!IsWindMove(gCurrentMove))
break;
@ -5274,13 +5274,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_TOXIC_CHAIN:
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& IsBattlerAlive(gBattlerTarget)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, gLastUsedAbility, GetBattlerAbility(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget) // Need to actually hit the target
&& RandomWeighted(RNG_TOXIC_CHAIN, 7, 3))
if (gBattleStruct->toxicChainPriority)
{
gBattleStruct->toxicChainPriority = FALSE;
gEffectBattler = gBattlerTarget;
gBattleScripting.battler = gBattlerAttacker;
gBattleScripting.moveEffect = MOVE_EFFECT_TOXIC;
@ -5356,25 +5352,22 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITYEFFECT_OPPORTUNIST:
/* Similar to ABILITYEFFECT_IMMUNITY in that it loops through all battlers.
* Is called after ABILITYEFFECT_ON_SWITCHIN to copy any boosts
* from switch in abilities e.g. intrepid sword, as
*/
for (battler = 0; battler < gBattlersCount; battler++)
case ABILITYEFFECT_OPPORTUNIST_FIRST_TURN:
switch (ability)
{
switch (GetBattlerAbility(battler))
case ABILITY_OPPORTUNIST:
if (gProtectStructs[battler].activateOpportunist == 2)
{
case ABILITY_OPPORTUNIST:
if (gProtectStructs[battler].activateOpportunist == 2)
{
gBattleScripting.battler = battler;
gProtectStructs[battler].activateOpportunist--;
ChooseStatBoostAnimation(battler);
BattleScriptPushCursorAndCallback(BattleScript_OpportunistCopyStatChange);
effect = 1;
}
break;
gBattleScripting.battler = battler;
gProtectStructs[battler].activateOpportunist--;
ChooseStatBoostAnimation(battler);
if (caseID == ABILITYEFFECT_OPPORTUNIST_FIRST_TURN)
BattleScriptPushCursorAndCallback(BattleScript_OpportunistCopyStatChangeEnd3);
else
BattleScriptCall(BattleScript_OpportunistCopyStatChange);
effect = 1;
}
break;
}
break;
case ABILITYEFFECT_IMMUNITY:
@ -6527,7 +6520,7 @@ static u32 TryConsumeMirrorHerb(u32 battler, enum ItemCaseId caseID)
gBattleScripting.battler = battler;
gProtectStructs[battler].eatMirrorHerb = 0;
ChooseStatBoostAnimation(battler);
if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL)
if (caseID == ITEMEFFECT_MIRROR_HERB_FIRST_TURN)
BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2);
else
BattleScriptCall(BattleScript_MirrorHerbCopyStatChange);
@ -6767,9 +6760,6 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
case HOLD_EFFECT_BERSERK_GENE:
effect = ConsumeBerserkGene(battler, ITEMEFFECT_NONE);
break;
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, ITEMEFFECT_NONE);
break;
default:
break;
}
@ -6832,6 +6822,43 @@ static inline bool32 TryCureStatus(u32 battler, enum ItemCaseId caseId)
return effect;
}
// This is hacky and could potentially be improved because ideally this function wouldn't exist but there is some inconsitency with timings.
static bool32 ShouldWaitWithActivation(u32 battler, enum ItemHoldEffect holdEffect)
{
if (holdEffect != HOLD_EFFECT_RESTORE_HP
&& holdEffect != HOLD_EFFECT_RESTORE_PCT_HP
&& holdEffect != HOLD_EFFECT_RESTORE_PP
&& holdEffect != HOLD_EFFECT_CONFUSE_SPICY
&& holdEffect != HOLD_EFFECT_CONFUSE_DRY
&& holdEffect != HOLD_EFFECT_CONFUSE_SWEET
&& holdEffect != HOLD_EFFECT_CONFUSE_BITTER
&& holdEffect != HOLD_EFFECT_CONFUSE_SOUR)
return FALSE;
// Restore HP berries can be knocked off
if (gBattleStruct->battlerState[battler].itemCanBeKnockedOff)
return TRUE;
if (IsBattlerTurnDamaged(battler)
&& gMultiHitCounter == 0 // Non multi hit moves don't wait for Berserk / Anger Shell activation
&& IsBattlerAlive(battler)
&& !TestIfSheerForceAffected(battler, gCurrentMove)
&& HadMoreThanHalfHpNowDoesnt(battler))
{
switch (GetBattlerAbility(battler))
{
case ABILITY_BERSERK:
return CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN);
case ABILITY_ANGER_SHELL:
return TRUE;
default:
return FALSE;
}
}
return FALSE;
}
u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
{
u32 moveType = 0;
@ -7037,9 +7064,6 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
case HOLD_EFFECT_BERSERK_GENE:
effect = ConsumeBerserkGene(battler, caseID);
break;
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, caseID);
break;
case HOLD_EFFECT_BOOSTER_ENERGY:
effect = TryBoosterEnergy(battler, GetBattlerAbility(battler), caseID);
break;
@ -7058,9 +7082,48 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
}
}
break;
case ITEMEFFECT_LEFTOVERS:
switch (battlerHoldEffect)
{
case HOLD_EFFECT_BLACK_SLUDGE:
if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON))
{
goto LEFTOVERS;
}
else if (!IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD))
{
gBattleScripting.battler = battler;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_ItemHurtEnd2);
effect = ITEM_HP_CHANGE;
RecordItemEffectBattle(battler, battlerHoldEffect);
PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem);
}
break;
case HOLD_EFFECT_LEFTOVERS:
LEFTOVERS:
if (gBattleMons[battler].hp < gBattleMons[battler].maxHP
&& (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock))
{
gBattleScripting.battler = battler;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
gBattleStruct->moveDamage[battler] *= -1;
BattleScriptExecute(BattleScript_ItemHealHP_End2);
effect = ITEM_HP_CHANGE;
RecordItemEffectBattle(battler, battlerHoldEffect);
}
break;
default:
break;
}
break;
case ITEMEFFECT_NORMAL:
case ITEMEFFECT_TRY_HEALING:
if (gBattleMons[battler].hp)
if (IsBattlerAlive(battler))
{
switch (battlerHoldEffect)
{
@ -7073,35 +7136,10 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
case HOLD_EFFECT_RESTORE_PP:
effect = ItemRestorePp(battler, gLastUsedItem, caseID);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON))
{
goto LEFTOVERS;
}
else if (!IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_ItemHurtEnd2);
effect = ITEM_HP_CHANGE;
RecordItemEffectBattle(battler, battlerHoldEffect);
PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem);
}
break;
case HOLD_EFFECT_LEFTOVERS:
LEFTOVERS:
if (gBattleMons[battler].hp < gBattleMons[battler].maxHP
&& (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
gBattleStruct->moveDamage[battler] *= -1;
BattleScriptExecute(BattleScript_ItemHealHP_End2);
effect = ITEM_HP_CHANGE;
RecordItemEffectBattle(battler, battlerHoldEffect);
}
case HOLD_EFFECT_WHITE_HERB:
effect = RestoreWhiteHerbStats(battler);
if (effect != 0)
BattleScriptExecute(BattleScript_WhiteHerbEnd2);
break;
case HOLD_EFFECT_CONFUSE_SPICY:
effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SPICY, caseID);
@ -7220,9 +7258,6 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
case HOLD_EFFECT_BERSERK_GENE:
effect = ConsumeBerserkGene(battler, caseID);
break;
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, caseID);
break;
default:
break;
}
@ -7255,14 +7290,12 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
case ITEMEFFECT_MOVE_END:
for (battler = 0; battler < gBattlersCount; battler++)
{
// If item can be knocked off berry activation will be blocked, but not other items
if (gBattleStruct->battlerState[battler].itemCanBeKnockedOff
&& (GetItemPocket(gBattleMons[battler].item) == POCKET_BERRIES
|| GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RESTORE_HP))
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, TRUE);
if (ShouldWaitWithActivation(battler, holdEffect))
continue;
gLastUsedItem = gBattleMons[battler].item;
effect = ItemEffectMoveEnd(battler, GetBattlerHoldEffect(battler, TRUE));
effect = ItemEffectMoveEnd(battler, holdEffect);
if (effect)
{
gPotentialItemEffectBattler = gBattleScripting.battler = battler;
@ -7594,6 +7627,17 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
break;
}
break;
case ITEMEFFECT_MIRROR_HERB:
case ITEMEFFECT_MIRROR_HERB_FIRST_TURN:
switch (battlerHoldEffect)
{
case HOLD_EFFECT_MIRROR_HERB:
effect = TryConsumeMirrorHerb(battler, caseID);
break;
default:
break;
}
break;
}
// Berry was successfully used on a Pokemon.

View File

@ -93,3 +93,23 @@ SINGLE_BATTLE_TEST("Anger Shell activates after all hits from a multi-hit move")
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Anger Shell does not activate if move is boosted by Sheer Force")
{
u16 maxHp = 500;
GIVEN {
PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); }
OPPONENT(SPECIES_NIDOKING) { Ability(ABILITY_SHEER_FORCE); }
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
}
}

View File

@ -73,3 +73,64 @@ SINGLE_BATTLE_TEST("Berserk activates after all hits from a multi-hit move")
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Berserk does not activate if move is boosted by Sheer Force")
{
u16 maxHp = 500;
GIVEN {
PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); }
OPPONENT(SPECIES_NIDOKING) { Ability(ABILITY_SHEER_FORCE); }
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
NOT ABILITY_POPUP(player, ABILITY_BERSERK);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Berserk will not activate if the last multi hit move activates a restore berry")
{
u32 j;
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_SLAP) == EFFECT_MULTI_HIT);
PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); Item(ITEM_SITRUS_BERRY); MaxHP(100); HP(90); }
OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times.
} WHEN {
TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); }
} SCENE {
for (j = 0; j < 4; j++) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent);
NOT ABILITY_POPUP(player, ABILITY_BERSERK);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
NOT ABILITY_POPUP(player, ABILITY_BERSERK);
} THEN {
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Berserk activates before the hp can be restored on non multi hit moves")
{
u16 maxHp = 500;
GIVEN {
ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH));
PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); Item(ITEM_SITRUS_BERRY); MaxHP(maxHp); HP(maxHp / 2 + 1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
ABILITY_POPUP(player, ABILITY_BERSERK);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
}
}

View File

@ -153,3 +153,16 @@ SINGLE_BATTLE_TEST("Color Change changes the type to Normal when a Pokemon is hi
MESSAGE("The opposing Kecleon's Color Change made it the Normal type!");
}
}
SINGLE_BATTLE_TEST("Color Change does not activate if move is boosted by Sheer Force")
{
GIVEN {
PLAYER(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); }
OPPONENT(SPECIES_NIDOKING) { Ability(ABILITY_SHEER_FORCE); }
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
NOT ABILITY_POPUP(player, ABILITY_COLOR_CHANGE);
}
}

View File

@ -294,3 +294,24 @@ SINGLE_BATTLE_TEST("Opportunist and Mirror Herb stack stat increases")
EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
}
}
DOUBLE_BATTLE_TEST("Opportunist and Mirror Herb resolve correctly")
{
GIVEN {
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_MEOWSCARADA) { Item(ITEM_MIRROR_HERB); }
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); Item(ITEM_MIRROR_HERB); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_INTREPID_SWORD);
ABILITY_POPUP(opponentRight, ABILITY_OPPORTUNIST);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}