Replaces STATUS2 usage with volatiles in code (#7262)

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
Nephrite 2025-07-11 23:53:49 +03:00 committed by GitHub
parent 9228826ae1
commit a1e67572b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 583 additions and 598 deletions

View File

@ -596,27 +596,15 @@
.byte \notChosenMove
.endm
.macro statusanimation battler:req
.macro statusanimation battler:req, status=0, isVolatile=FALSE
.byte 0x64
.byte \battler
.endm
.macro status2animation battler:req, status2:req
.byte 0x65
.byte \battler
.4byte \status2
.endm
.macro setmoveeffect effect:req
sethword sMOVE_EFFECT, \effect
sethword sSAVED_MOVE_EFFECT, \effect
.endm
.macro chosenstatusanimation battler:req, isStatus2:req, status:req
.byte 0x66
.byte \battler
.byte \isStatus2
.4byte \status
.byte \isVolatile
.endm
.macro volatileanimation battler:req, volatile:req
statusanimation \battler, \volatile, TRUE
.endm
.macro yesnobox
@ -979,8 +967,11 @@
.4byte \failInstr
.endm
.macro setforesight
.macro setvolatile battler:req, volatile:req, value=TRUE
.byte 0xb1
.byte \battler
.byte \volatile
.byte \value
.endm
.macro trysetperishsong failInstr:req
@ -1041,10 +1032,6 @@
.byte 0xbe
.endm
.macro setdefensecurlbit
.byte 0xbf
.endm
.macro recoverbasedonsunlight failInstr:req
.byte 0xc0
.4byte \failInstr
@ -2094,10 +2081,6 @@
.4byte \failInstr
.endm
.macro setpowder battler:req
various \battler, VARIOUS_SET_POWDER
.endm
.macro bringdownairbornebattler battler:req
various \battler, VARIOUS_GRAVITY_ON_AIRBORNE_MONS
.endm
@ -2353,12 +2336,9 @@
setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7
.endm
.macro chosenstatus1animation battler:req, status:req
chosenstatusanimation \battler, 0x0, \status
.endm
.macro chosenstatus2animation battler:req, status:req
chosenstatusanimation \battler, 0x1, \status
.macro setmoveeffect effect:req
sethword sMOVE_EFFECT, \effect
sethword sSAVED_MOVE_EFFECT, \effect
.endm
.macro sethword dst:req, value:req

View File

@ -1329,7 +1329,7 @@ BattleScript_EffectPowder::
attackstring
ppreduce
jumpifvolatile BS_TARGET, VOLATILE_POWDER, BattleScript_ButItFailed
setpowder BS_TARGET
setvolatile BS_TARGET, VOLATILE_POWDER
attackanimation
waitanimation
printstring STRINGID_COVEREDINPOWDER
@ -3929,7 +3929,7 @@ BattleScript_EffectForesight::
ppreduce
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed
setforesight
setvolatile BS_TARGET, VOLATILE_FORESIGHT
BattleScript_IdentifiedFoe:
attackanimation
waitanimation
@ -4026,7 +4026,7 @@ BattleScript_TryDestinyKnotTarget:
infatuatewithbattler BS_TARGET, BS_ATTACKER
playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT
waitanimation
status2animation BS_TARGET, STATUS2_INFATUATION
volatileanimation BS_TARGET, VOLATILE_INFATUATION
waitanimation
printstring STRINGID_DESTINYKNOTACTIVATES
waitmessage B_WAIT_TIME_LONG
@ -4038,7 +4038,7 @@ BattleScript_TryDestinyKnotAttacker:
infatuatewithbattler BS_ATTACKER, BS_TARGET
playanimation BS_TARGET, B_ANIM_HELD_ITEM_EFFECT
waitanimation
status2animation BS_ATTACKER, STATUS2_INFATUATION
volatileanimation BS_ATTACKER, VOLATILE_INFATUATION
waitanimation
printstring STRINGID_DESTINYKNOTACTIVATES
waitmessage B_WAIT_TIME_LONG
@ -4332,7 +4332,7 @@ BattleScript_EffectDefenseCurl::
attackcanceler
attackstring
ppreduce
setdefensecurlbit
setvolatile BS_TARGET, VOLATILE_DEFENSE_CURL
setstatchanger STAT_DEF, 1, FALSE
statbuffchange BS_ATTACKER, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_DefenseCurlDoStatUpAnim
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_StatUpPrintString
@ -6866,7 +6866,7 @@ BattleScript_PrintUproarOverTurns::
end2
BattleScript_ThrashConfuses::
chosenstatus2animation BS_ATTACKER, STATUS2_CONFUSION
volatileanimation BS_ATTACKER, VOLATILE_CONFUSION
printstring STRINGID_PKMNFATIGUECONFUSION
waitmessage B_WAIT_TIME_LONG
end2
@ -6874,7 +6874,7 @@ BattleScript_ThrashConfuses::
BattleScript_MoveUsedIsConfused::
printstring STRINGID_PKMNISCONFUSED
waitmessage B_WAIT_TIME_LONG
status2animation BS_ATTACKER, STATUS2_CONFUSION
volatileanimation BS_ATTACKER, VOLATILE_CONFUSION
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, FALSE, BattleScript_MoveUsedIsConfusedRet
BattleScript_DoSelfConfusionDmg::
cancelmultiturnmoves BS_ATTACKER
@ -6900,7 +6900,7 @@ BattleScript_MoveUsedPowder::
ppreduce
pause B_WAIT_TIME_SHORT
cancelmultiturnmoves BS_ATTACKER
status2animation BS_ATTACKER, STATUS2_POWDER
volatileanimation BS_ATTACKER, VOLATILE_POWDER
waitanimation
effectivenesssound
hitanimation BS_ATTACKER
@ -6938,7 +6938,7 @@ BattleScript_WrapEnds::
BattleScript_MoveUsedIsInLove::
printstring STRINGID_PKMNINLOVE
waitmessage B_WAIT_TIME_LONG
status2animation BS_ATTACKER, STATUS2_INFATUATION
volatileanimation BS_ATTACKER, VOLATILE_INFATUATION
return
BattleScript_MoveUsedIsInLoveCantAttack::
@ -6949,13 +6949,13 @@ BattleScript_MoveUsedIsInLoveCantAttack::
BattleScript_NightmareTurnDmg::
printstring STRINGID_PKMNLOCKEDINNIGHTMARE
waitmessage B_WAIT_TIME_LONG
status2animation BS_ATTACKER, STATUS2_NIGHTMARE
volatileanimation BS_ATTACKER, VOLATILE_NIGHTMARE
goto BattleScript_DoTurnDmg
BattleScript_CurseTurnDmg::
printstring STRINGID_PKMNAFFLICTEDBYCURSE
waitmessage B_WAIT_TIME_LONG
status2animation BS_ATTACKER, STATUS2_CURSED
volatileanimation BS_ATTACKER, VOLATILE_CURSED
goto BattleScript_DoTurnDmg
BattleScript_TargetPRLZHeal::
@ -7085,7 +7085,7 @@ BattleScript_MoveEffectWrap::
return
BattleScript_MoveEffectConfusion::
chosenstatus2animation BS_EFFECT_BATTLER, STATUS2_CONFUSION
volatileanimation BS_EFFECT_BATTLER, VOLATILE_CONFUSION
printstring STRINGID_PKMNWASCONFUSED
waitmessage B_WAIT_TIME_LONG
return
@ -8195,7 +8195,7 @@ BattleScript_BanefulBunkerEffect::
BattleScript_CuteCharmActivates::
call BattleScript_AbilityPopUp
status2animation BS_ATTACKER, STATUS2_INFATUATION
volatileanimation BS_ATTACKER, VOLATILE_INFATUATION
printstring STRINGID_PKMNSXINFATUATEDY
waitmessage B_WAIT_TIME_LONG
call BattleScript_TryDestinyKnotTarget
@ -9455,9 +9455,9 @@ BattleScript_EffectConfuseSide::
BattleScript_ConfuseSideLoop:
jumpifabsent BS_TARGET, BattleScript_ConfuseSideIncrement
trysetconfusion BattleScript_ConfuseSideIncrement
status2animation BS_EFFECT_BATTLER, STATUS2_CONFUSION
volatileanimation BS_EFFECT_BATTLER, VOLATILE_CONFUSION
BattleScript_ConfuseSidePrintMessage:
printfromtable gStatus2StringIds
printstring STRINGID_PKMNWASCONFUSED
waitmessage B_WAIT_TIME_LONG
BattleScript_ConfuseSideIncrement:
jumpifbytenotequal gBattlerTarget, sBATTLER, BattleScript_ConfuseSideEnd
@ -9477,9 +9477,9 @@ BattleScript_EffectInfatuateSide::
BattleScript_InfatuateSideLoop:
jumpifabsent BS_TARGET, BattleScript_InfatuateSideIncrement
trysetinfatuation BattleScript_InfatuateSideIncrement
status2animation BS_EFFECT_BATTLER, STATUS2_INFATUATION
volatileanimation BS_EFFECT_BATTLER, VOLATILE_INFATUATION
BattleScript_InfatuateSidePrintMessage:
printfromtable gStatus2StringIds
printstring STRINGID_PKMNFELLINLOVE
waitmessage B_WAIT_TIME_LONG
BattleScript_InfatuateSideIncrement:
jumpifbytenotequal gBattlerTarget, sBATTLER, BattleScript_InfatuateSideEnd
@ -9495,7 +9495,7 @@ BattleScript_TormentSideLoop:
jumpifabsent BS_TARGET, BattleScript_TormentSideIncrement
trysettorment BattleScript_TormentSideIncrement
BattleScript_TormentSidePrintMessage:
printfromtable gStatus2StringIds
printstring STRINGID_PKMNSUBJECTEDTOTORMENT
waitmessage B_WAIT_TIME_LONG
BattleScript_TormentSideIncrement:
jumpifbytenotequal gBattlerTarget, sBATTLER, BattleScript_TormentSideEnd
@ -9516,7 +9516,7 @@ BattleScript_MeanLookSideLoop:
jumpifabsent BS_TARGET, BattleScript_MeanLookSideIncrement
trysetescapeprevention BattleScript_MeanLookSideIncrement
BattleScript_MeanLookSidePrintMessage:
printfromtable gStatus2StringIds
printstring STRINGID_TARGETCANTESCAPENOW
waitmessage B_WAIT_TIME_LONG
BattleScript_MeanLookSideIncrement:
jumpifbytenotequal gBattlerTarget, sBATTLER, BattleScript_MeanLookSideEnd

View File

@ -35,7 +35,7 @@ void BS_TrySetOctolock(void)
else
{
gDisableStructs[battler].octolock = TRUE;
gBattleMons[battler].status2 |= STATUS2_ESCAPE_PREVENTION;
gBattleMons[battler].volatiles.escapePrevention = TRUE;
gDisableStructs[battler].battlerPreventingEscape = gBattlerAttacker;
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -44,7 +44,7 @@
// Used to exclude moves learned temporarily by Transform or Mimic
#define MOVE_IS_PERMANENT(battler, moveSlot) \
(!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) \
(!(gBattleMons[battler].volatiles.transformed) \
&& !(gDisableStructs[battler].mimickedMoves & (1u << moveSlot)))
// Battle Actions

View File

@ -301,8 +301,8 @@ void BtlController_EmitChooseItem(u32 battler, u32 bufferId, u8 *battlePartyOrde
void BtlController_EmitChoosePokemon(u32 battler, u32 bufferId, u8 caseId, u8 slotId, u16 abilityId, u8 battlerPreventingSwitchout, u8 *data);
void BtlController_EmitHealthBarUpdate(u32 battler, u32 bufferId, u16 hpValue);
void BtlController_EmitExpUpdate(u32 battler, u32 bufferId, u8 partyId, s32 expPoints);
void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status1, u32 status2);
void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 status2, u32 status);
void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status);
void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 isVolatile, u32 status);
void BtlController_EmitDataTransfer(u32 battler, u32 bufferId, u16 size, void *data);
void BtlController_EmitTwoReturnValues(u32 battler, u32 bufferId, u8 ret8, u32 ret32);
void BtlController_EmitChosenMonReturnValue(u32 battler, u32 bufferId, u8 partyId, u8 *battlePartyOrder);

View File

@ -7,7 +7,7 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler);
void SpriteCB_WaitForBattlerBallReleaseAnim(struct Sprite *sprite);
void SpriteCB_TrainerSlideIn(struct Sprite *sprite);
void SpriteCB_TrainerSpawn(struct Sprite *sprite);
void InitAndLaunchChosenStatusAnimation(u32 battler, bool32 isStatus2, u32 status);
void InitAndLaunchChosenStatusAnimation(u32 battler, bool32 isVolatile, u32 status);
bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattlerId, u8 attacker, u8 target, u8 tableId, u16 argument);
void InitAndLaunchSpecialAnimation(u8 activeBattlerId, u8 attacker, u8 target, u8 tableId);
bool8 IsBattleSEPlaying(u8 battler);

View File

@ -89,7 +89,7 @@ void SpriteCB_TrainerThrowObject(struct Sprite *sprite);
void AnimSetCenterToCornerVecX(struct Sprite *sprite);
void BeginBattleIntroDummy(void);
void BeginBattleIntro(void);
void SwitchInClearSetData(u32 battler);
void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy);
const u8* FaintClearSetData(u32 battler);
void BattleTurnPassed(void);
u8 IsRunningFromBattleImpossible(u32 battler);

View File

@ -392,9 +392,10 @@ bool32 HadMoreThanHalfHpNowDoesnt(u32 battler);
void UpdateStallMons(void);
bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId);
bool32 TrySwitchInEjectPack(enum ItemCaseId caseID);
u32 GetMonVolatile(u32 battler, enum Volatile volatile);
void SetMonVolatile(u32 battler, enum Volatile volatile, u32 newValue);
u32 GetMonVolatile(u32 battler, enum Volatile _volatile);
void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue);
u32 TryBoosterEnergy(u32 battler, u32 ability, enum ItemCaseId caseID);
bool32 ItemHealMonVolatile(u32 battler, u16 itemId);
void PushHazardTypeToQueue(u32 side, enum Hazards hazardType);
bool32 IsHazardOnSide(u32 side, enum Hazards hazardType);
bool32 AreAnyHazardsOnSide(u32 side);

View File

@ -142,23 +142,21 @@ enum VolatileFlags
V_BATON_PASSABLE = (1 << 0),
};
// Volatile status ailments
// These are removed after exiting the battle or switching
/* Definitions with names e.g. "Confusion" are accessible in the debug menu
* Enum, Type, (Field name, (optional)bitSize), Flags, (optional)(Debug menu header, (optional)max. value)
*/
/* Volatile status ailments
* These are removed after exiting the battle or switching
* Enum, Type Type, max value, flags */
#define VOLATILE_DEFINITIONS(F) \
F(VOLATILE_CONFUSION, confusionTurns, (u32, 3), V_BATON_PASSABLE) \
F(VOLATILE_CONFUSION, confusionTurns, (u32, 6), V_BATON_PASSABLE) \
F(VOLATILE_FLINCHED, flinched, (u32, 1)) \
F(VOLATILE_UPROAR, uproarTurns, (u32, 3)) \
F(VOLATILE_UPROAR, uproarTurns, (u32, 5)) \
F(VOLATILE_TORMENT, torment, (u32, 1)) \
F(VOLATILE_BIDE, bideTurns, (u32, 2)) \
F(VOLATILE_LOCK_CONFUSE, lockConfusionTurns, (u32, 2)) \
F(VOLATILE_BIDE, bideTurns, (u32, 3)) \
F(VOLATILE_LOCK_CONFUSE, lockConfusionTurns, (u32, 3)) \
F(VOLATILE_MULTIPLETURNS, multipleTurns, (u32, 1)) \
F(VOLATILE_WRAPPED, wrapped, (u32, 1)) \
F(VOLATILE_POWDER, powder, (u32, 1)) \
F(VOLATILE_UNUSED, padding, (u32, 1)) \
F(VOLATILE_INFATUATION, infatuation, (u32, 4)) \
F(VOLATILE_INFATUATION, infatuation, (enum BattlerId, MAX_BITS(4))) \
F(VOLATILE_DEFENSE_CURL, defenseCurl, (u32, 1)) \
F(VOLATILE_TRANSFORMED, transformed, (u32, 1)) \
F(VOLATILE_RECHARGE, recharge, (u32, 1)) \
@ -174,18 +172,22 @@ enum VolatileFlags
F(VOLATILE_MUD_SPORT, mudSport, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_WATER_SPORT, waterSport, (u32, 1), V_BATON_PASSABLE)
/* Use within a macro to get the maximum allowed value for a volatile. Requires _typeBitSize and debug parameters as input. */
#define GET_VOLATILE_MAXIMUM(_typeBitSize, ...) INVOKE_WITH_B(GET_VOLATILE_MAXIMUM_, _typeBitSize)
#define GET_VOLATILE_MAXIMUM_(_type, ...) FIRST(__VA_OPT__(MAX_BITS(FIRST(__VA_ARGS__)),) MAX_BITS((sizeof(_type) * 8)))
/* Use within a macro to get the maximum allowed value for a volatile. Requires _typeMaxValue as input. */
#define GET_VOLATILE_MAXIMUM(_typeMaxValue, ...) INVOKE_WITH_B(GET_VOLATILE_MAXIMUM_, _typeMaxValue)
#define GET_VOLATILE_MAXIMUM_(_type, ...) FIRST(__VA_OPT__(FIRST(__VA_ARGS__),) MAX_BITS((sizeof(_type) * 8)))
#define UNPACK_VOLATILE_ENUMS(_enum, ...) _enum,
enum Volatile
{
VOLATILE_NONE,
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_ENUMS)
/* Expands to VOLATILE_CONFUSION, VOLATILE_FLINCHED, etc. */
};
// Helper macros
#define INFATUATED_WITH(battler) (battler + 1)
// Old flags
#define STATUS2_CONFUSION (1 << 0 | 1 << 1 | 1 << 2)
#define STATUS2_CONFUSION_TURN(num) ((num) << 0)

View File

@ -149,7 +149,6 @@ enum CmdVarious
VARIOUS_SET_AURORA_VEIL,
VARIOUS_TRY_THIRD_TYPE,
VARIOUS_ACUPRESSURE,
VARIOUS_SET_POWDER,
VARIOUS_GRAVITY_ON_AIRBORNE_MONS,
VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS,
VARIOUS_JUMP_IF_ROAR_FAILS,

View File

@ -2,6 +2,7 @@
#define GUARD_ITEM_H
#include "constants/item.h"
#include "constants/item_effects.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/tms_hms.h"
@ -187,7 +188,7 @@ u8 GetItemBattleUsage(u16 itemId);
u32 GetItemSecondaryId(u32 itemId);
u32 GetItemFlingPower(u32 itemId);
u32 GetItemStatus1Mask(u16 itemId);
u32 GetItemStatus2Mask(u16 itemId);
bool32 ItemHasVolatileFlag(u16 itemId, enum Volatile volatile);
u32 GetItemSellPrice(u32 itemId);
#endif // GUARD_ITEM_H

View File

@ -120,42 +120,51 @@
#define DEFAULT_3(_default, ...) DEFAULT(_default __VA_OPT__(, THIRD(__VA_ARGS__)))
#define DEFAULT_4(_default, ...) DEFAULT(_default __VA_OPT__(, FOURTH(__VA_ARGS__)))
/* Simply a lists numbers 0-31, allows for word-spanning macros */
#define BITS_32(F, ...) \
F(0, __VA_ARGS__) \
F(1, __VA_ARGS__) \
F(2, __VA_ARGS__) \
F(3, __VA_ARGS__) \
F(4, __VA_ARGS__) \
F(5, __VA_ARGS__) \
F(6, __VA_ARGS__) \
F(7, __VA_ARGS__) \
F(8, __VA_ARGS__) \
F(9, __VA_ARGS__) \
F(10, __VA_ARGS__) \
F(11, __VA_ARGS__) \
F(12, __VA_ARGS__) \
F(13, __VA_ARGS__) \
F(14, __VA_ARGS__) \
F(15, __VA_ARGS__) \
F(16, __VA_ARGS__) \
F(17, __VA_ARGS__) \
F(18, __VA_ARGS__) \
F(19, __VA_ARGS__) \
F(20, __VA_ARGS__) \
F(21, __VA_ARGS__) \
F(22, __VA_ARGS__) \
F(23, __VA_ARGS__) \
F(24, __VA_ARGS__) \
F(25, __VA_ARGS__) \
F(26, __VA_ARGS__) \
F(27, __VA_ARGS__) \
F(28, __VA_ARGS__) \
F(29, __VA_ARGS__) \
F(30, __VA_ARGS__) \
F(31, __VA_ARGS__)
/* Compares _n to 1 shifted by _b by _operation (==, <, > etc) */
#define OP_BIT_SHIFT(_b, _n, _operation) (_n) _operation (1 << _b) ? _b :
/* (Credit to MGriffin) A rather monstrous way of finding the set bit in a word.
Invalid input causes a compiler error. Sample: https://cexplore.karathan.at/z/x1hm7B */
#define BIT_INDEX(n) \
(n) == (1 << 0) ? 0 : \
(n) == (1 << 1) ? 1 : \
(n) == (1 << 2) ? 2 : \
(n) == (1 << 3) ? 3 : \
(n) == (1 << 4) ? 4 : \
(n) == (1 << 5) ? 5 : \
(n) == (1 << 6) ? 6 : \
(n) == (1 << 7) ? 7 : \
(n) == (1 << 8) ? 8 : \
(n) == (1 << 9) ? 9 : \
(n) == (1 << 10) ? 10 : \
(n) == (1 << 11) ? 11 : \
(n) == (1 << 12) ? 12 : \
(n) == (1 << 13) ? 13 : \
(n) == (1 << 14) ? 14 : \
(n) == (1 << 15) ? 15 : \
(n) == (1 << 16) ? 16 : \
(n) == (1 << 17) ? 17 : \
(n) == (1 << 18) ? 18 : \
(n) == (1 << 19) ? 19 : \
(n) == (1 << 20) ? 20 : \
(n) == (1 << 21) ? 21 : \
(n) == (1 << 22) ? 22 : \
(n) == (1 << 23) ? 23 : \
(n) == (1 << 24) ? 24 : \
(n) == (1 << 25) ? 25 : \
(n) == (1 << 26) ? 26 : \
(n) == (1 << 27) ? 27 : \
(n) == (1 << 28) ? 28 : \
(n) == (1 << 29) ? 29 : \
(n) == (1 << 30) ? 30 : \
(n) == (1 << 31) ? 31 : \
*(u32 *)NULL
#define BIT_INDEX(_n) BITS_32(OP_BIT_SHIFT, _n, ==) *(u32 *)NULL
/* (Credit to MGriffin) A way to find the minimum required number of bits to
store a number (max: 32). Sample: https://godbolt.org/z/xb4KdPMhT */
#define BIT_SIZE(_n) (BITS_32(OP_BIT_SHIFT, _n, <) 32)
#define COMPRESS_BITS_0 0, 1
#define COMPRESS_BITS_1 1, 1
@ -167,11 +176,10 @@ Invalid input causes a compiler error. Sample: https://cexplore.karathan.at/z/x1
#define COMPRESS_BITS_7 7, 1
/* Will try and compress a set bit (or up to three sequential bits) into a single byte
Input must be of the form (upper << lower) where upper can be up to 3, lower up to 31 */
Input must be of the form (upper << lower) where upper can be up to 7, lower up to 31 */
#define COMPRESS_BITS(_val) COMPRESS_BITS_STEP_2 _val
#define COMPRESS_BITS_STEP_2(_unpacked) COMPRESS_BITS_STEP_3(COMPRESS_BITS_## _unpacked)
#define COMPRESS_BITS_STEP_3(...) COMPRESS_BITS_STEP_4(__VA_ARGS__)
#define COMPRESS_BITS_STEP_4(upper, lower) (((upper % 8) << 5) + (BIT_INDEX(lower)))
#define COMPRESS_BITS_STEP_2(_unpacked) INVOKE(COMPRESS_BITS_STEP_3, COMPRESS_BITS_## _unpacked)
#define COMPRESS_BITS_STEP_3(upper, lower) (((upper % 8) << 5) + (BIT_INDEX(lower)))
/* Will read a compressed bit stored by COMPRESS_BIT into a single byte */
#define UNCOMPRESS_BITS(compressed) ((compressed >> 5) << (compressed & 0x1F))

View File

@ -310,8 +310,8 @@ enum {
MON_SPR_GFX_MANAGERS_COUNT
};
#define UNPACK_VOLATILE_STRUCT(_enum, _fieldName, _typeBitSize, ...) INVOKE(UNPACK_VOLATILE_STRUCT_, _fieldName, UNPACK_B(_typeBitSize));
#define UNPACK_VOLATILE_STRUCT_(_fieldName, _type, ...) _type FIRST(__VA_OPT__(_fieldName:FIRST(__VA_ARGS__),) _fieldName)
#define UNPACK_VOLATILE_STRUCT(_enum, _fieldName, _typeMaxValue, ...) INVOKE(UNPACK_VOLATILE_STRUCT_, _fieldName, UNPACK_B(_typeMaxValue));
#define UNPACK_VOLATILE_STRUCT_(_fieldName, _type, ...) _type FIRST(__VA_OPT__(_fieldName:BIT_SIZE(FIRST(__VA_ARGS__)),) _fieldName)
struct Volatiles
{

View File

@ -1653,19 +1653,21 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// AI_CBM_HighRiskForDamage
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0))
ADJUST_SCORE(-10);
if (HasDamagingMove(battlerDef) && !((gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE)
if (HasDamagingMove(battlerDef) && !(gBattleMons[battlerAtk].volatiles.substitute
|| IsBattlerIncapacitated(battlerDef, abilityDef)
|| gBattleMons[battlerDef].volatiles.infatuation
|| gBattleMons[battlerDef].volatiles.confusionTurns))
ADJUST_SCORE(-10);
if (HasMoveWithEffect(battlerAtk, EFFECT_SUBSTITUTE) && !(gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE))
if (HasMoveWithEffect(battlerAtk, EFFECT_SUBSTITUTE) && !gBattleMons[battlerAtk].volatiles.substitute)
ADJUST_SCORE(-10);
if (HasNonVolatileMoveEffect(battlerAtk, MOVE_EFFECT_SLEEP) && ! (gBattleMons[battlerDef].status1 & STATUS1_SLEEP))
ADJUST_SCORE(-10);
break;
case EFFECT_COUNTER:
case EFFECT_MIRROR_COAT:
if (IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) || gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION))
if (IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])
|| gBattleMons[battlerDef].volatiles.infatuation
|| gBattleMons[battlerDef].volatiles.confusionTurns > 0)
ADJUST_SCORE(-1);
if ((predictedMove == MOVE_NONE || GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_STATUS
|| DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove))
@ -1717,7 +1719,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_FOCUS_ENERGY:
if (gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY_ANY)
if (gBattleMons[battlerAtk].volatiles.dragonCheer || gBattleMons[battlerAtk].volatiles.focusEnergy)
ADJUST_SCORE(-10);
break;
case EFFECT_CONFUSE:
@ -1727,7 +1729,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_SUBSTITUTE:
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR)
if (gBattleMons[battlerAtk].volatiles.substitute || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR)
ADJUST_SCORE(-8);
else if (aiData->hpPercents[battlerAtk] <= 25)
ADJUST_SCORE(-10);
@ -1737,7 +1739,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_SHED_TAIL:
if (CountUsablePartyMons(battlerAtk) == 0)
ADJUST_SCORE(-10);
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR)
if (gBattleMons[battlerAtk].volatiles.substitute || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR)
ADJUST_SCORE(-8);
else if (aiData->hpPercents[battlerAtk] <= 50)
ADJUST_SCORE(-10);
@ -1808,7 +1810,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_NIGHTMARE:
if (gBattleMons[battlerDef].status2 & STATUS2_NIGHTMARE)
if (gBattleMons[battlerDef].volatiles.nightmare)
ADJUST_SCORE(-10);
else if (!AI_IsBattlerAsleepOrComatose(battlerDef))
ADJUST_SCORE(-8);
@ -1818,7 +1820,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_CURSE:
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST))
{
if (gBattleMons[battlerDef].status2 & STATUS2_CURSED
if (gBattleMons[battlerDef].volatiles.cursed
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
else if (aiData->hpPercents[battlerAtk] <= 50)
@ -1857,7 +1859,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10); // only one mon needs to set up Sticky Web
break;
case EFFECT_FORESIGHT:
if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT)
if (gBattleMons[battlerDef].volatiles.foresight)
ADJUST_SCORE(-10);
else if (gBattleMons[battlerDef].statStages[STAT_EVASION] <= 4
|| !(IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST))
@ -1945,7 +1947,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_BATON_PASS:
if (CountUsablePartyMons(battlerAtk) == 0)
ADJUST_SCORE(-10);
else if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE
else if (gBattleMons[battlerAtk].volatiles.substitute
|| (gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_AQUA_RING | STATUS3_MAGNET_RISE | STATUS3_POWER_TRICK))
|| AnyStatIsRaised(battlerAtk))
break;
@ -2005,7 +2007,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_TORMENT:
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gBattleMons[battlerDef].status2 & STATUS2_TORMENT
else if (gBattleMons[battlerDef].volatiles.torment
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
{
ADJUST_SCORE(-10);
@ -2162,8 +2164,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-6);
break;
case EFFECT_TRANSFORM:
if (gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED
|| (gBattleMons[battlerDef].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))) //Leave out Illusion b/c AI is supposed to be fooled
if (gBattleMons[battlerAtk].volatiles.transformed
|| gBattleMons[battlerDef].volatiles.transformed
|| gBattleMons[battlerDef].volatiles.substitute) //Leave out Illusion b/c AI is supposed to be fooled
ADJUST_SCORE(-10);
break;
case EFFECT_SPITE:
@ -2206,7 +2209,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_DESTINY_BOND:
if (DoesDestinyBondFail(battlerAtk))
ADJUST_SCORE(-10);
if (gBattleMons[battlerDef].status2 & STATUS2_DESTINY_BOND)
if (gBattleMons[battlerDef].volatiles.destinyBond)
ADJUST_SCORE(-10);
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
@ -2703,7 +2706,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|| MoveHasAdditionalEffectSelf(instructedMove, MOVE_EFFECT_RECHARGE)
|| IsZMove(instructedMove)
|| (gLockedMoves[battlerDef] != 0 && gLockedMoves[battlerDef] != 0xFFFF)
|| gBattleMons[battlerDef].status2 & STATUS2_MULTIPLETURNS
|| gBattleMons[battlerDef].volatiles.multipleTurns
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
{
ADJUST_SCORE(-10);
@ -2975,7 +2978,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-7);
break;
case EFFECT_PERISH_SONG:
if (!(gBattleMons[battlerDef].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)))
if (!(gBattleMons[battlerDef].volatiles.escapePrevention || gBattleMons[battlerDef].volatiles.wrapped))
{
if (IsTrappingMove(aiData->partnerMove) || predictedMove == MOVE_INGRAIN)
ADJUST_SCORE(WEAK_EFFECT);
@ -3038,7 +3041,9 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_DRAGON_CHEER:
if (gBattleMons[battlerAtkPartner].status2 & STATUS2_FOCUS_ENERGY_ANY || !HasDamagingMove(battlerAtkPartner))
if (gBattleMons[battlerAtkPartner].volatiles.dragonCheer
|| gBattleMons[battlerAtkPartner].volatiles.focusEnergy
|| !HasDamagingMove(battlerAtkPartner))
ADJUST_SCORE(-5);
else if (atkPartnerHoldEffect == HOLD_EFFECT_SCOPE_LENS
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_DRAGON)
@ -4147,7 +4152,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_BATON_PASS:
if ((gAiLogicData->shouldSwitch & (1u << battlerAtk)) && (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE
if ((gAiLogicData->shouldSwitch & (1u << battlerAtk)) && (gBattleMons[battlerAtk].volatiles.substitute
|| (gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_AQUA_RING | STATUS3_MAGNET_RISE | STATUS3_POWER_TRICK))
|| AnyStatIsRaised(battlerAtk)))
ADJUST_SCORE(BEST_EFFECT);
@ -4411,7 +4416,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_DEFENSE_CURL:
if (HasMoveWithEffect(battlerAtk, EFFECT_ROLLOUT) && !(gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL))
if (HasMoveWithEffect(battlerAtk, EFFECT_ROLLOUT) && !gBattleMons[battlerAtk].volatiles.defenseCurl)
ADJUST_SCORE(DECENT_EFFECT);
ADJUST_SCORE(IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF));
break;
@ -4456,7 +4461,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
&& BattlerWillFaintFromSecondaryDamage(battlerDef, aiData->abilities[battlerDef]))
break; // Don't use if the attract won't have a change to activate
if (gBattleMons[battlerDef].status1 & STATUS1_ANY
|| (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| gBattleMons[battlerDef].volatiles.confusionTurns > 0
|| (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)))
ADJUST_SCORE(GOOD_EFFECT);
else
@ -5049,7 +5054,7 @@ case EFFECT_GUARD_SPLIT:
break;
case EFFECT_RAPID_SPIN:
if ((AreAnyHazardsOnSide(GetBattlerSide(battlerAtk)) && CountUsablePartyMons(battlerAtk) != 0)
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].volatiles.wrapped))
ADJUST_SCORE(GOOD_EFFECT);
case EFFECT_SPECTRAL_THIEF:
ADJUST_SCORE(AI_ShouldCopyStatChanges(battlerAtk, battlerDef));
@ -5204,13 +5209,13 @@ case EFFECT_GUARD_SPLIT:
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
break;
case MOVE_EFFECT_BUG_BITE: // And pluck
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
if (gBattleMons[battlerDef].volatiles.substitute || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
else if (GetItemPocket(aiData->items[battlerDef]) == POCKET_BERRIES)
ADJUST_SCORE(DECENT_EFFECT);
break;
case MOVE_EFFECT_INCINERATE:
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
if (gBattleMons[battlerDef].volatiles.substitute || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
else if (GetItemPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS)
ADJUST_SCORE(DECENT_EFFECT);
@ -5949,9 +5954,10 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0))
ADJUST_SCORE(10);
if (HasDamagingMove(battlerDef) && !((gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE)
if (HasDamagingMove(battlerDef) && !(gBattleMons[battlerAtk].volatiles.substitute
|| IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])
|| gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)))
|| gBattleMons[battlerDef].volatiles.infatuation
|| gBattleMons[battlerDef].volatiles.confusionTurns > 0))
ADJUST_SCORE(10);
}
break;

View File

@ -692,7 +692,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
&& gAiLogicData->abilities[opposingBattler] != ABILITY_KEEN_EYE
&& gAiLogicData->abilities[opposingBattler] != ABILITY_MINDS_EYE
&& (B_ILLUMINATE_EFFECT >= GEN_9 && gAiLogicData->abilities[opposingBattler] != ABILITY_ILLUMINATE)
&& !(gBattleMons[battler].status2 & STATUS2_FORESIGHT)
&& !gBattleMons[battler].volatiles.foresight
&& !(gStatuses3[battler] & STATUS3_MIRACLE_EYED))
switchMon = FALSE;
@ -713,12 +713,12 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
//Cursed
if (gBattleMons[battler].status2 & STATUS2_CURSED
if (gBattleMons[battler].volatiles.cursed
&& (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_CURSED, GetSwitchChance(SHOULD_SWITCH_CURSED_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_CURSED, GetSwitchChance(SHOULD_SWITCH_CURSED))))
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
//Nightmare
if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE
if (gBattleMons[battler].volatiles.nightmare
&& (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, GetSwitchChance(SHOULD_SWITCH_NIGHTMARE_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, GetSwitchChance(SHOULD_SWITCH_NIGHTMARE))))
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
@ -729,7 +729,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
}
// Infatuation
if (gBattleMons[battler].status2 & STATUS2_INFATUATION
if (gBattleMons[battler].volatiles.infatuation
&& !AiExpectsToFaintPlayer(battler)
&& gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE
&& RandomPercentage(RNG_AI_SWITCH_INFATUATION, GetSwitchChance(SHOULD_SWITCH_INFATUATION)))
@ -1079,7 +1079,9 @@ bool32 ShouldSwitch(u32 battler)
s32 i;
s32 availableToSwitch;
if (gBattleMons[battler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
if (gBattleMons[battler].volatiles.wrapped)
return FALSE;
if (gBattleMons[battler].volatiles.escapePrevention)
return FALSE;
if (gStatuses3[battler] & STATUS3_ROOTED)
return FALSE;
@ -1228,7 +1230,9 @@ void ModifySwitchAfterMoveScoring(u32 battler)
s32 i;
s32 availableToSwitch;
if (gBattleMons[battler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
if (gBattleMons[battler].volatiles.wrapped)
return;
if (gBattleMons[battler].volatiles.escapePrevention)
return;
if (gStatuses3[battler] & STATUS3_ROOTED)
return;
@ -2405,7 +2409,7 @@ static bool32 ShouldUseItem(u32 battler)
shouldUse = TRUE;
if (itemEffects[3] & ITEM3_PARALYSIS && gBattleMons[battler].status1 & STATUS1_PARALYSIS)
shouldUse = TRUE;
if (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[battler].status2 & STATUS2_CONFUSION)
if (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[battler].volatiles.confusionTurns > 0)
shouldUse = TRUE;
break;
case EFFECT_ITEM_INCREASE_STAT:
@ -2417,7 +2421,8 @@ static bool32 ShouldUseItem(u32 battler)
break;
case EFFECT_ITEM_SET_FOCUS_ENERGY:
if (!gDisableStructs[battler].isFirstTurn
|| gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY
|| gBattleMons[battler].volatiles.dragonCheer
|| gBattleMons[battler].volatiles.focusEnergy
|| AI_OpponentCanFaintAiWithMod(battler, 0))
break;
shouldUse = TRUE;

View File

@ -373,7 +373,9 @@ bool32 AI_CanBattlerEscape(u32 battler)
bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef)
{
if (gBattleMons[battlerDef].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED))
if (gBattleMons[battlerDef].volatiles.wrapped)
return TRUE;
if (gBattleMons[battlerDef].volatiles.escapePrevention)
return TRUE;
if (gStatuses3[battlerDef] & (STATUS3_ROOTED | STATUS3_SKY_DROPPED))
return TRUE;
@ -1880,7 +1882,9 @@ bool32 ShouldSetSnow(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
bool32 IsBattlerDamagedByStatus(u32 battler)
{
return gBattleMons[battler].status1 & STATUS1_DAMAGING
|| gBattleMons[battler].status2 & (STATUS2_WRAPPED | STATUS2_NIGHTMARE | STATUS2_CURSED)
|| gBattleMons[battler].volatiles.wrapped
|| gBattleMons[battler].volatiles.nightmare
|| gBattleMons[battler].volatiles.cursed
|| gStatuses3[battler] & (STATUS3_PERISH_SONG | STATUS3_LEECHSEED)
|| gStatuses4[battler] & (STATUS4_SALT_CURE)
|| gSideStatuses[GetBattlerSide(battler)] & (SIDE_STATUS_SEA_OF_FIRE | SIDE_STATUS_DAMAGE_NON_TYPES);
@ -2028,7 +2032,7 @@ u32 IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, u32 stat)
tempScore += WEAK_EFFECT;
if (gStatuses3[battlerDef] & STATUS3_ROOTED)
tempScore += WEAK_EFFECT;
if (gBattleMons[battlerDef].status2 & STATUS2_CURSED)
if (gBattleMons[battlerDef].volatiles.cursed)
tempScore += WEAK_EFFECT;
break;
case STAT_EVASION:
@ -2038,7 +2042,7 @@ u32 IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, u32 stat)
tempScore += WEAK_EFFECT;
if (gStatuses3[battlerDef] & STATUS3_ROOTED)
tempScore += WEAK_EFFECT;
if (gBattleMons[battlerDef].status2 & STATUS2_CURSED)
if (gBattleMons[battlerDef].volatiles.cursed)
tempScore += WEAK_EFFECT;
break;
}
@ -2801,7 +2805,7 @@ static u32 GetLeechSeedDamage(u32 battlerId)
static u32 GetNightmareDamage(u32 battlerId)
{
u32 damage = 0;
if ((gBattleMons[battlerId].status2 & STATUS2_NIGHTMARE) && gBattleMons[battlerId].status1 & STATUS1_SLEEP)
if (gBattleMons[battlerId].volatiles.nightmare && gBattleMons[battlerId].status1 & STATUS1_SLEEP)
{
damage = GetNonDynamaxMaxHP(battlerId) / 4;
if (damage == 0)
@ -2813,7 +2817,7 @@ static u32 GetNightmareDamage(u32 battlerId)
static u32 GetCurseDamage(u32 battlerId)
{
u32 damage = 0;
if (gBattleMons[battlerId].status2 & STATUS2_CURSED)
if (gBattleMons[battlerId].volatiles.cursed)
{
damage = GetNonDynamaxMaxHP(battlerId) / 4;
if (damage == 0)
@ -2827,7 +2831,7 @@ static u32 GetTrapDamage(u32 battlerId)
// ai has no knowledge about turns remaining
u32 damage = 0;
enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[gBattleStruct->wrappedBy[battlerId]];
if (gBattleMons[battlerId].status2 & STATUS2_WRAPPED)
if (gBattleMons[battlerId].volatiles.wrapped)
{
if (holdEffect == HOLD_EFFECT_BINDING_BAND)
damage = GetNonDynamaxMaxHP(battlerId) / (B_BINDING_DAMAGE >= GEN_6 ? 6 : 8);
@ -3233,7 +3237,7 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability)
if (gBattleMons[battler].status1 & STATUS1_SLEEP && !HasMoveWithEffect(battler, EFFECT_SLEEP_TALK))
return TRUE;
if (gBattleMons[battler].status2 & STATUS2_RECHARGE || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0))
if (gBattleMons[battler].volatiles.recharge || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0))
return TRUE;
return FALSE;
@ -3372,7 +3376,7 @@ bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move,
bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability)
{
if ((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
if (gBattleMons[battlerDef].volatiles.confusionTurns > 0
|| (ability == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(battlerAtk, gAiLogicData->abilities[battlerAtk], move))
|| IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
@ -3421,7 +3425,7 @@ bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 b
bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if ((gBattleMons[battlerDef].status2 & STATUS2_INFATUATION)
if (gBattleMons[battlerDef].volatiles.infatuation
|| gAiLogicData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] == UQ_4_12(0.0)
|| defAbility == ABILITY_OBLIVIOUS
|| !AreBattlersOfOppositeGender(battlerAtk, battlerDef)
@ -3441,8 +3445,8 @@ u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbi
}
else if ((atkAbility == ABILITY_SERENE_GRACE
|| gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| gBattleMons[battlerDef].volatiles.infatuation
|| gBattleMons[battlerDef].volatiles.confusionTurns > 0)
|| ((AI_IsFaster(battlerAtk, battlerDef, move)) && CanTargetFaintAi(battlerDef, battlerAtk)))
{
return 2; // good idea to flinch
@ -4357,8 +4361,8 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
if ((defSpeed >= atkSpeed && defSpeed / 2 < atkSpeed) // You'll go first after paralyzing foe
|| IsPowerBasedOnStatus(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_PARALYSIS)
|| (HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FIRST_TURN_ONLY)) // filter out Fake Out
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| gBattleMons[battlerDef].volatiles.infatuation
|| gBattleMons[battlerDef].volatiles.confusionTurns > 0)
ADJUST_SCORE_PTR(GOOD_EFFECT);
else
ADJUST_SCORE_PTR(DECENT_EFFECT);
@ -4396,7 +4400,7 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score
&& gAiLogicData->holdEffects[battlerDef] != HOLD_EFFECT_CURE_STATUS)
{
if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| gBattleMons[battlerDef].volatiles.infatuation
|| (gAiLogicData->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FIRST_TURN_ONLY)))
ADJUST_SCORE_PTR(GOOD_EFFECT);
else
@ -4801,9 +4805,9 @@ void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
if (AreAnyHazardsOnSide(GetBattlerSide(battlerDef)) && CountUsablePartyMons(battlerDef) != 0)
ADJUST_SCORE_PTR(-2);
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE && AI_IsFaster(battlerAtk, battlerDef, move))
if (gBattleMons[battlerAtk].volatiles.substitute && AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE_PTR(-10);
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE)
if (gBattleMons[battlerDef].volatiles.substitute)
ADJUST_SCORE_PTR(GOOD_EFFECT);
if (gStatuses3[battlerAtk] & STATUS3_LEECHSEED)

View File

@ -1025,7 +1025,7 @@ void HandleMoveSwitching(u32 battler)
gBattleMons[battler].pp[i] = moveInfo->currentPp[i];
}
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
if (!(gBattleMons[battler].volatiles.transformed))
{
for (i = 0; i < MAX_MON_MOVES; i++)
{

View File

@ -1043,24 +1043,20 @@ void BtlController_EmitExpUpdate(u32 battler, u32 bufferId, u8 partyId, s32 expP
PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6);
}
void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status1, u32 status2)
void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status)
{
gBattleResources->transferBuffer[0] = CONTROLLER_STATUSICONUPDATE;
gBattleResources->transferBuffer[1] = status1;
gBattleResources->transferBuffer[2] = (status1 & 0x0000FF00) >> 8;
gBattleResources->transferBuffer[3] = (status1 & 0x00FF0000) >> 16;
gBattleResources->transferBuffer[4] = (status1 & 0xFF000000) >> 24;
gBattleResources->transferBuffer[5] = status2;
gBattleResources->transferBuffer[6] = (status2 & 0x0000FF00) >> 8;
gBattleResources->transferBuffer[7] = (status2 & 0x00FF0000) >> 16;
gBattleResources->transferBuffer[8] = (status2 & 0xFF000000) >> 24;
PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 9);
gBattleResources->transferBuffer[1] = status;
gBattleResources->transferBuffer[2] = (status & 0x0000FF00) >> 8;
gBattleResources->transferBuffer[3] = (status & 0x00FF0000) >> 16;
gBattleResources->transferBuffer[4] = (status & 0xFF000000) >> 24;
PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 5);
}
void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 status2, u32 status)
void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 isVolatile, u32 status)
{
gBattleResources->transferBuffer[0] = CONTROLLER_STATUSANIMATION;
gBattleResources->transferBuffer[1] = status2;
gBattleResources->transferBuffer[1] = isVolatile;
gBattleResources->transferBuffer[2] = status;
gBattleResources->transferBuffer[3] = (status & 0x0000FF00) >> 8;
gBattleResources->transferBuffer[4] = (status & 0x00FF0000) >> 16;

View File

@ -1668,13 +1668,13 @@ static void UpdateBattlerValue(struct BattleDebugMenu *data)
if (data->modifyArrows.currValue)
{
if (IsBattlerAlive(BATTLE_OPPOSITE(data->battlerId)))
gBattleMons[data->battlerId].status2 |= STATUS2_INFATUATED_WITH(BATTLE_OPPOSITE(data->battlerId));
gBattleMons[data->battlerId].volatiles.infatuation = INFATUATED_WITH(BATTLE_OPPOSITE(data->battlerId));
else
gBattleMons[data->battlerId].status2 |= STATUS2_INFATUATED_WITH(BATTLE_PARTNER(BATTLE_OPPOSITE(data->battlerId)));
gBattleMons[data->battlerId].volatiles.infatuation = INFATUATED_WITH(BATTLE_PARTNER(BATTLE_OPPOSITE(data->battlerId)));
}
else
{
gBattleMons[data->battlerId].status2 &= ~STATUS2_INFATUATION;
gBattleMons[data->battlerId].volatiles.infatuation = 0;
}
break;
}
@ -2034,7 +2034,7 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data)
data->modifyArrows.maxDigits = 1;
data->modifyArrows.modifiedValPtr = NULL;
data->modifyArrows.typeOfVal = VAR_IN_LOVE;
data->modifyArrows.currValue = (gBattleMons[data->battlerId].status2 & STATUS2_INFATUATION) != 0;
data->modifyArrows.currValue = gBattleMons[data->battlerId].volatiles.infatuation;
}
break;
case LIST_ITEM_STATUS1:
@ -2046,7 +2046,7 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data)
data->modifyArrows.currValue = GetMonVolatile(data->battlerId, data->currentSecondaryListItemId);
data->modifyArrows.typeOfVal = VAL_VOLATILE;
data->modifyArrows.minValue = 0;
#define UNPACK_VOLATILE_MAX_SIZE(_enum, _fieldName, _typeBitSize, ...) case _enum: data->modifyArrows.maxValue = min(MAX_u16, GET_VOLATILE_MAXIMUM(_typeBitSize)); break;
#define UNPACK_VOLATILE_MAX_SIZE(_enum, _fieldName, _typeMaxValue, ...) case _enum: data->modifyArrows.maxValue = min(MAX_u16, GET_VOLATILE_MAXIMUM(_typeMaxValue)); break;
switch (data->currentSecondaryListItemId)
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_MAX_SIZE)

View File

@ -181,14 +181,14 @@ void ActivateDynamax(u32 battler)
gBattleStruct->dynamax.dynamaxTurns[battler] = gBattleTurnCounter + DYNAMAX_TURNS_COUNT;
// Substitute is removed upon Dynamaxing.
gBattleMons[battler].status2 &= ~STATUS2_SUBSTITUTE;
gBattleMons[battler].volatiles.substitute = FALSE;
ClearBehindSubstituteBit(battler);
// Choiced Moves are reset upon Dynamaxing.
gBattleStruct->choicedMove[battler] = MOVE_NONE;
// Try Gigantamax form change.
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax.
if (!gBattleMons[battler].volatiles.transformed) // Ditto cannot Gigantamax.
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
BattleScriptExecute(BattleScript_DynamaxBegins);

View File

@ -458,21 +458,21 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_THRASH:
if (gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
if (gBattleMons[battler].volatiles.lockConfusionTurns && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
{
gBattleMons[battler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1);
gBattleMons[battler].volatiles.lockConfusionTurns--;
if (WasUnableToUseMove(battler))
{
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
}
else if (!(gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE) && (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS))
else if (!gBattleMons[battler].volatiles.lockConfusionTurns && gBattleMons[battler].volatiles.multipleTurns)
{
gBattleMons[battler].status2 &= ~STATUS2_MULTIPLETURNS;
if (!(gBattleMons[battler].status2 & STATUS2_CONFUSION))
gBattleMons[battler].volatiles.multipleTurns = FALSE;
if (!gBattleMons[battler].volatiles.confusionTurns)
{
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION;
SetMoveEffect(battler, battler, TRUE, FALSE);
if (gBattleMons[battler].status2 & STATUS2_CONFUSION)
if (gBattleMons[battler].volatiles.confusionTurns)
BattleScriptExecute(BattleScript_ThrashConfuses);
effect = TRUE;
}
@ -705,7 +705,7 @@ static bool32 HandleEndTurnNightmare(u32 battler)
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE
if (gBattleMons[battler].volatiles.nightmare
&& IsBattlerAlive(battler)
&& !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD))
{
@ -719,7 +719,7 @@ static bool32 HandleEndTurnNightmare(u32 battler)
}
else
{
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
}
}
@ -732,7 +732,7 @@ static bool32 HandleEndTurnCurse(u32 battler)
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status2 & STATUS2_CURSED
if (gBattleMons[battler].volatiles.cursed
&& IsBattlerAlive(battler)
&& !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD))
{
@ -752,7 +752,7 @@ static bool32 HandleEndTurnWrap(u32 battler)
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status2 & STATUS2_WRAPPED && IsBattlerAlive(battler))
if (gBattleMons[battler].volatiles.wrapped && IsBattlerAlive(battler))
{
if (--gDisableStructs[battler].wrapTurns != 0)
{
@ -773,7 +773,7 @@ static bool32 HandleEndTurnWrap(u32 battler)
}
else // broke free
{
gBattleMons[battler].status2 &= ~STATUS2_WRAPPED;
gBattleMons[battler].volatiles.wrapped = FALSE;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]);
BattleScriptExecute(BattleScript_WrapEnds);
}
@ -867,7 +867,7 @@ static bool32 HandleEndTurnTorment(u32 battler)
if (gDisableStructs[battler].tormentTimer == gBattleTurnCounter)
{
gBattleMons[battler].status2 &= ~STATUS2_TORMENT;
gBattleMons[battler].volatiles.torment = FALSE;
BattleScriptExecute(BattleScript_TormentEnds);
effect = TRUE;
}
@ -1349,7 +1349,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
switch (gBattleStruct->eventBlockCounter)
{
case THIRD_EVENT_BLOCK_UPROAR:
if (gBattleMons[battler].status2 & STATUS2_UPROAR)
if (gBattleMons[battler].volatiles.uproarTurns)
{
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
@ -1357,7 +1357,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF)
{
gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[gBattlerAttacker].volatiles.nightmare = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
BattleScriptExecute(BattleScript_MonWokeUpInUproar);
BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerAttacker].status1);
@ -1372,16 +1372,16 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler)
else
{
gBattlerAttacker = battler;
gBattleMons[battler].status2 -= STATUS2_UPROAR_TURN(1); // uproar timer goes down
gBattleMons[battler].volatiles.uproarTurns--; // uproar timer goes down
if (WasUnableToUseMove(battler))
{
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
}
else if (gBattleMons[battler].status2 & STATUS2_UPROAR)
else if (gBattleMons[battler].volatiles.uproarTurns)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_CONTINUES;
gBattleMons[battler].status2 |= STATUS2_MULTIPLETURNS;
gBattleMons[battler].volatiles.multipleTurns = TRUE;
}
else
{

View File

@ -459,10 +459,10 @@ static void SpriteCB_TrainerSlideVertical(struct Sprite *sprite)
#undef sSpeedX
void InitAndLaunchChosenStatusAnimation(u32 battler, bool32 isStatus2, u32 status)
void InitAndLaunchChosenStatusAnimation(u32 battler, bool32 isVolatile, u32 status)
{
gBattleSpritesDataPtr->healthBoxesData[battler].statusAnimActive = 1;
if (!isStatus2)
if (!isVolatile)
{
if (status == STATUS1_FREEZE || status == STATUS1_FROSTBITE)
LaunchStatusAnimation(battler, B_ANIM_STATUS_FRZ);
@ -479,13 +479,13 @@ void InitAndLaunchChosenStatusAnimation(u32 battler, bool32 isStatus2, u32 statu
}
else
{
if (status & STATUS2_INFATUATION)
if (status == VOLATILE_INFATUATION)
LaunchStatusAnimation(battler, B_ANIM_STATUS_INFATUATION);
else if (status & STATUS2_CONFUSION)
else if (status == VOLATILE_CONFUSION)
LaunchStatusAnimation(battler, B_ANIM_STATUS_CONFUSION);
else if (status & STATUS2_CURSED)
else if (status == VOLATILE_CURSED)
LaunchStatusAnimation(battler, B_ANIM_STATUS_CURSED);
else if (status & STATUS2_NIGHTMARE)
else if (status == VOLATILE_NIGHTMARE)
LaunchStatusAnimation(battler, B_ANIM_STATUS_NIGHTMARE);
else // no animation
gBattleSpritesDataPtr->healthBoxesData[battler].statusAnimActive = 0;

View File

@ -3117,14 +3117,13 @@ static void BattleStartClearSetData(void)
}
}
#define UNPACK_VOLATILE_BATON_PASSABLES(_enum, _fieldName, _typeBitSize, ...) __VA_OPT__(if ((FIRST(__VA_ARGS__)) & V_BATON_PASSABLE) gBattleMons[battler].volatiles._fieldName = volatilesCopy._fieldName;)
#define UNPACK_VOLATILE_BATON_PASSABLES(_enum, _fieldName, _typeMaxValue, ...) __VA_OPT__(if ((FIRST(__VA_ARGS__)) & V_BATON_PASSABLE) gBattleMons[battler].volatiles._fieldName = volatilesCopy->_fieldName;)
void SwitchInClearSetData(u32 battler)
void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy)
{
s32 i;
enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove);
struct DisableStruct disableStructCopy = gDisableStructs[battler];
struct Volatiles volatilesCopy = gBattleMons[battler].volatiles;
ClearIllusionMon(battler);
if (effect != EFFECT_BATON_PASS)
@ -3133,8 +3132,8 @@ void SwitchInClearSetData(u32 battler)
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
for (i = 0; i < gBattlersCount; i++)
{
if ((gBattleMons[i].status2 & STATUS2_ESCAPE_PREVENTION) && gDisableStructs[i].battlerPreventingEscape == battler)
gBattleMons[i].status2 &= ~STATUS2_ESCAPE_PREVENTION;
if (gBattleMons[i].volatiles.escapePrevention && gDisableStructs[i].battlerPreventingEscape == battler)
gBattleMons[i].volatiles.escapePrevention = FALSE;
if ((gStatuses3[i] & STATUS3_ALWAYS_HITS) && gDisableStructs[i].battlerWithSureHit == battler)
{
gStatuses3[i] &= ~STATUS3_ALWAYS_HITS;
@ -3142,15 +3141,17 @@ void SwitchInClearSetData(u32 battler)
}
}
}
// Clear volatiles - reapply some if Baton Pass was used
memset(&gBattleMons[battler].volatiles, 0, sizeof(struct Volatiles));
if (effect == EFFECT_BATON_PASS)
{
// Transfer Baton Passable volatile statuses
memset(&gBattleMons[battler].volatiles, 0, sizeof(struct Volatiles));
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_BATON_PASSABLES)
/* Expands to the following (compiler removes `if` statements):
* gBattleMons[battler].volatiles.confusionTurns = volatilesCopy.confusionTurns;
* gBattleMons[battler].volatiles.substitute = volatilesCopy.substitute;
* gBattleMons[battler].volatiles.escapePrevention = volatilesCopy.escapePrevention;
* gBattleMons[battler].volatiles.confusionTurns = volatilesCopy->confusionTurns;
* gBattleMons[battler].volatiles.substitute = volatilesCopy->substitute;
* gBattleMons[battler].volatiles.escapePrevention = volatilesCopy->escapePrevention;
* ...etc
*/
gStatuses3[battler] &= (STATUS3_LEECHSEED_BATTLER | STATUS3_LEECHSEED | STATUS3_ALWAYS_HITS | STATUS3_PERISH_SONG | STATUS3_ROOTED
@ -3172,17 +3173,16 @@ void SwitchInClearSetData(u32 battler)
}
else
{
gBattleMons[battler].status2 = 0;
gStatuses3[battler] = 0;
gStatuses4[battler] = 0;
}
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleMons[i].status2 & STATUS2_INFATUATED_WITH(battler))
gBattleMons[i].status2 &= ~STATUS2_INFATUATED_WITH(battler);
if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && gBattleStruct->wrappedBy[i] == battler)
gBattleMons[i].status2 &= ~STATUS2_WRAPPED;
if (gBattleMons[i].volatiles.infatuation == INFATUATED_WITH(battler))
gBattleMons[i].volatiles.infatuation = 0;
if (gBattleMons[i].volatiles.wrapped && gBattleStruct->wrappedBy[i] == battler)
gBattleMons[i].volatiles.wrapped = FALSE;
if ((gStatuses4[i] & STATUS4_SYRUP_BOMB) && gBattleStruct->stickySyrupdBy[i] == battler)
gStatuses4[i] &= ~STATUS4_SYRUP_BOMB;
}
@ -3203,7 +3203,7 @@ void SwitchInClearSetData(u32 battler)
}
else if (effect == EFFECT_SHED_TAIL)
{
gBattleMons[battler].status2 |= STATUS2_SUBSTITUTE;
gBattleMons[battler].volatiles.substitute = TRUE;
gDisableStructs[battler].substituteHP = disableStructCopy.substituteHP;
}
@ -3289,18 +3289,18 @@ const u8* FaintClearSetData(u32 battler)
for (i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
gBattleMons[battler].status2 = 0;
memset(&gBattleMons[battler].volatiles, 0, sizeof(struct Volatiles));
gStatuses3[battler] &= STATUS3_GASTRO_ACID; // Edge case: Keep Gastro Acid if pokemon's ability can have effect after fainting, for example Innards Out.
gStatuses4[battler] = 0;
for (i = 0; i < gBattlersCount; i++)
{
if ((gBattleMons[i].status2 & STATUS2_ESCAPE_PREVENTION) && gDisableStructs[i].battlerPreventingEscape == battler)
gBattleMons[i].status2 &= ~STATUS2_ESCAPE_PREVENTION;
if (gBattleMons[i].status2 & STATUS2_INFATUATED_WITH(battler))
gBattleMons[i].status2 &= ~STATUS2_INFATUATED_WITH(battler);
if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && gBattleStruct->wrappedBy[i] == battler)
gBattleMons[i].status2 &= ~STATUS2_WRAPPED;
if (gBattleMons[i].volatiles.escapePrevention && gDisableStructs[i].battlerPreventingEscape == battler)
gBattleMons[i].volatiles.escapePrevention = FALSE;
if (gBattleMons[i].volatiles.infatuation == INFATUATED_WITH(battler))
gBattleMons[i].volatiles.infatuation = 0;
if (gBattleMons[i].volatiles.wrapped && gBattleStruct->wrappedBy[i] == battler)
gBattleMons[i].volatiles.wrapped = FALSE;
if ((gStatuses4[i] & STATUS4_SYRUP_BOMB) && gBattleStruct->stickySyrupdBy[i] == battler)
gStatuses4[i] &= ~STATUS4_SYRUP_BOMB;
}
@ -3403,17 +3403,17 @@ const u8* FaintClearSetData(u32 battler)
// If the target was sky dropped in the middle of using Outrage/Petal Dance/Thrash,
// confuse them upon release and print "confused via fatigue" message and animation.
if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE)
if (gBattleMons[otherSkyDropper].volatiles.lockConfusionTurns)
{
gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE);
gBattleMons[otherSkyDropper].volatiles.lockConfusionTurns = 0;
// If the released mon can be confused, do so.
// Don't use CanBeConfused here, since it can cause issues in edge cases.
if (!(GetBattlerAbility(otherSkyDropper) == ABILITY_OWN_TEMPO
|| gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION
|| gBattleMons[otherSkyDropper].volatiles.confusionTurns
|| IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN)))
{
gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
gBattleMons[otherSkyDropper].volatiles.confusionTurns = ((Random()) % 4) + 2;
gBattlerAttacker = otherSkyDropper;
result = BattleScript_ThrashConfuses;
}
@ -3476,7 +3476,7 @@ static void DoBattleIntro(void)
gBattleMons[battler].types[2] = TYPE_MYSTERY;
gBattleMons[battler].ability = GetAbilityBySpecies(gBattleMons[battler].species, gBattleMons[battler].abilityNum);
gBattleStruct->hpOnSwitchout[GetBattlerSide(battler)] = gBattleMons[battler].hp;
gBattleMons[battler].status2 = 0;
memset(&gBattleMons[battler].volatiles, 0, sizeof(struct Volatiles));
for (i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
#if TESTING
@ -3905,7 +3905,7 @@ static void TryDoEventsBeforeFirstTurn(void)
for (i = 0; i < gBattlersCount; i++)
{
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
gBattleMons[i].volatiles.flinched = FALSE;
// Record party slots of player's mons that appeared in battle
if (!BattlerHasAi(i))
gBattleStruct->appearedInBattle |= 1u << gBattlerPartyIndexes[i];
@ -3945,8 +3945,8 @@ static void HandleEndTurn_ContinueBattle(void)
gBattleCommunication[i] = 0;
for (i = 0; i < gBattlersCount; i++)
{
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].status2 & STATUS2_MULTIPLETURNS))
gBattleMons[i].volatiles.flinched = FALSE;
if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].volatiles.multipleTurns))
CancelMultiTurnMoves(i, SKY_DROP_IGNORE);
}
gBattleStruct->eventBlockCounter = 0;
@ -4003,8 +4003,8 @@ void BattleTurnPassed(void)
gChosenMoveByBattler[i] = MOVE_NONE;
gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE;
gStatuses4[i] &= ~STATUS4_ELECTRIFIED;
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
gBattleMons[i].status2 &= ~STATUS2_POWDER;
gBattleMons[i].volatiles.flinched = FALSE;
gBattleMons[i].volatiles.powder = FALSE;
if (gBattleStruct->battlerState[i].stompingTantrumTimer > 0)
gBattleStruct->battlerState[i].stompingTantrumTimer--;
@ -4192,8 +4192,8 @@ static void HandleTurnActionSelectionState(void)
}
else
{
if (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS
|| gBattleMons[battler].status2 & STATUS2_RECHARGE)
if (gBattleMons[battler].volatiles.multipleTurns
|| gBattleMons[battler].volatiles.recharge)
{
gChosenActionByBattler[battler] = B_ACTION_USE_MOVE;
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
@ -4347,8 +4347,8 @@ static void HandleTurnActionSelectionState(void)
gBattleCommunication[battler] = STATE_WAIT_SET_BEFORE_ACTION;
gBattleCommunication[GetPartnerBattler(battler)] = STATE_BEFORE_ACTION_CHOSEN;
RecordedBattle_ClearBattlerAction(battler, 1);
if (gBattleMons[GetPartnerBattler(battler)].status2 & STATUS2_MULTIPLETURNS
|| gBattleMons[GetPartnerBattler(battler)].status2 & STATUS2_RECHARGE)
if (gBattleMons[GetPartnerBattler(battler)].volatiles.multipleTurns
|| gBattleMons[GetPartnerBattler(battler)].volatiles.recharge)
{
BtlController_EmitEndBounceEffect(battler, B_COMM_TO_CONTROLLER);
MarkBattlerForControllerExec(battler);
@ -4740,9 +4740,9 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
speed *= 2;
else if (ability == ABILITY_SLOW_START && gDisableStructs[battler].slowStartTimer != 0)
speed /= 2;
else if (ability == ABILITY_PROTOSYNTHESIS && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && ((gBattleWeather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battler].boosterEnergyActivated))
else if (ability == ABILITY_PROTOSYNTHESIS && !(gBattleMons[battler].volatiles.transformed) && ((gBattleWeather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battler].boosterEnergyActivated))
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
else if (ability == ABILITY_QUARK_DRIVE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battler].boosterEnergyActivated))
else if (ability == ABILITY_QUARK_DRIVE && !(gBattleMons[battler].volatiles.transformed) && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battler].boosterEnergyActivated))
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive)
speed *= 2;
@ -4762,7 +4762,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
speed /= 2;
else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF && GetActiveGimmick(battler) != GIMMICK_DYNAMAX)
speed = (speed * 150) / 100;
else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battler].species == SPECIES_DITTO && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battler].species == SPECIES_DITTO && !(gBattleMons[battler].volatiles.transformed))
speed *= 2;
// various effects
@ -5108,13 +5108,13 @@ static void TurnValuesCleanUp(bool8 var0)
{
gDisableStructs[i].rechargeTimer--;
if (gDisableStructs[i].rechargeTimer == 0)
gBattleMons[i].status2 &= ~STATUS2_RECHARGE;
gBattleMons[i].volatiles.recharge = FALSE;
}
gBattleStruct->battlerState[i].canPickupItem = FALSE;
}
if (gDisableStructs[i].substituteHP == 0)
gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE;
gBattleMons[i].volatiles.substitute = FALSE;
if (!(gStatuses3[i] & STATUS3_COMMANDER))
gBattleStruct->battlerState[i].commandingDondozo = FALSE;

View File

@ -1379,11 +1379,6 @@ const u16 gStatusConditionsStringIds[] =
STRINGID_PKMNWASPOISONED, STRINGID_PKMNBADLYPOISONED, STRINGID_PKMNWASBURNED, STRINGID_PKMNWASPARALYZED, STRINGID_PKMNFELLASLEEP, STRINGID_PKMNGOTFROSTBITE
};
const u16 gStatus2StringIds[] =
{
STRINGID_PKMNWASCONFUSED, STRINGID_PKMNFELLINLOVE, STRINGID_TARGETCANTESCAPENOW, STRINGID_PKMNSUBJECTEDTOTORMENT
};
const u16 gDamageNonTypesStartStringIds[] =
{
[B_MSG_TRAPPED_WITH_VINES] = STRINGID_TEAMTRAPPEDWITHVINES,

View File

@ -449,8 +449,8 @@ static void Cmd_drawpartystatussummary(void);
static void Cmd_hidepartystatussummary(void);
static void Cmd_jumptocalledmove(void);
static void Cmd_statusanimation(void);
static void Cmd_status2animation(void);
static void Cmd_chosenstatusanimation(void);
static void Cmd_unused_0x65(void);
static void Cmd_unused_0x66(void);
static void Cmd_yesnobox(void);
static void Cmd_cancelallactions(void);
static void Cmd_setgravity(void);
@ -525,7 +525,7 @@ static void Cmd_tryspiteppreduce(void);
static void Cmd_healpartystatus(void);
static void Cmd_cursetarget(void);
static void Cmd_trysetspikes(void);
static void Cmd_setforesight(void);
static void Cmd_setvolatile(void);
static void Cmd_trysetperishsong(void);
static void Cmd_handlerollout(void);
static void Cmd_jumpifconfusedandstatmaxed(void);
@ -539,7 +539,7 @@ static void Cmd_tryrestorehpberry(void);
static void Cmd_halvehp(void);
static void Cmd_copyfoestats(void);
static void Cmd_rapidspinfree(void);
static void Cmd_setdefensecurlbit(void);
static void Cmd_unused_0xBF(void);
static void Cmd_recoverbasedonsunlight(void);
static void Cmd_setstickyweb(void);
static void Cmd_selectfirstvalidtarget(void);
@ -708,8 +708,8 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_hidepartystatussummary, //0x62
Cmd_jumptocalledmove, //0x63
Cmd_statusanimation, //0x64
Cmd_status2animation, //0x65
Cmd_chosenstatusanimation, //0x66
Cmd_unused_0x65, //0x65
Cmd_unused_0x66, //0x66
Cmd_yesnobox, //0x67
Cmd_cancelallactions, //0x68
Cmd_setgravity, //0x69
@ -784,7 +784,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_healpartystatus, //0xAE
Cmd_cursetarget, //0xAF
Cmd_trysetspikes, //0xB0
Cmd_setforesight, //0xB1
Cmd_setvolatile, //0xB1
Cmd_trysetperishsong, //0xB2
Cmd_handlerollout, //0xB3
Cmd_jumpifconfusedandstatmaxed, //0xB4
@ -798,7 +798,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_halvehp, //0xBC
Cmd_copyfoestats, //0xBD
Cmd_rapidspinfree, //0xBE
Cmd_setdefensecurlbit, //0xBF
Cmd_unused_0xBF, //0xBF
Cmd_recoverbasedonsunlight, //0xC0
Cmd_setstickyweb, //0xC1
Cmd_selectfirstvalidtarget, //0xC2
@ -1137,9 +1137,9 @@ u32 NumAffectedSpreadMoveTargets(void)
u32 NumFaintedBattlersByAttacker(u32 battlerAtk)
{
u32 numMonsFainted = 0;
u32 battler, numMonsFainted = 0;
for (u32 battler = 0; battler < gBattlersCount; battler++)
for (battler = 0; battler < gBattlersCount; battler++)
{
if (battler == battlerAtk)
continue;
@ -1268,7 +1268,7 @@ static void Cmd_attackcanceler(void)
if (!gBattleMons[gBattlerAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE
&& !(gHitMarker & (HITMARKER_ALLOW_NO_PP | HITMARKER_NO_ATTACKSTRING | HITMARKER_NO_PPDEDUCT))
&& !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
&& !(gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
{
gBattlescriptCurrInstr = BattleScript_NoPPForMove;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
@ -1279,7 +1279,7 @@ static void Cmd_attackcanceler(void)
// Check if no available target present on the field or if Sky Battles ban the move
if ((NoTargetPresent(gBattlerAttacker, gCurrentMove)
&& (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
&& (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)))
|| (IsMoveNotAllowedInSkyBattles(gCurrentMove)))
{
gBattleStruct->noTargetPresent = TRUE;
@ -1289,7 +1289,7 @@ static void Cmd_attackcanceler(void)
else
gBattlescriptCurrInstr = BattleScript_FailedFromAtkString;
if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
return;
}
@ -1354,7 +1354,8 @@ static void Cmd_attackcanceler(void)
return;
}
for (u32 i = 0; i < gBattlersCount; i++)
u32 i;
for (i = 0; i < gBattlersCount; i++)
{
if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && MoveCanBeSnatched(gCurrentMove))
{
@ -1382,7 +1383,7 @@ static void Cmd_attackcanceler(void)
}
else if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)
&& (effect != EFFECT_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
&& (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
&& (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
&& effect != EFFECT_SUCKER_PUNCH
&& effect != EFFECT_COUNTER
&& effect != EFFECT_UPPER_HAND)
@ -1504,13 +1505,14 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
}
else
{
u32 numTargets = 0;
u32 numMisses = 0;
u32 moveType = GetBattleMoveType(move);
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u32 battlerDef,
numTargets = 0,
numMisses = 0,
moveType = GetBattleMoveType(move),
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
bool32 calcSpreadMove = IsSpreadMove(moveTarget) && !IsBattleMoveStatus(move);
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{
if (gBattleStruct->calculatedSpreadMoveAccuracy)
break;
@ -1624,7 +1626,7 @@ static void Cmd_ppreduce(void)
if (gBattleControllerExecFlags)
return;
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
if (moveTarget == MOVE_TARGET_BOTH
@ -1739,8 +1741,8 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA
}
else
{
critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0)
+ 1 * ((gBattleMons[battlerAtk].status2 & STATUS2_DRAGON_CHEER) != 0)
critChance = 2 * (gBattleMons[battlerAtk].volatiles.focusEnergy != 0)
+ 1 * (gBattleMons[battlerAtk].volatiles.dragonCheer != 0)
+ GetMoveCriticalHitStage(move)
+ GetHoldEffectCritChanceIncrease(battlerAtk, holdEffectAtk)
+ 2 * (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerAtk) == AFFECTION_FIVE_HEARTS)
@ -1789,9 +1791,9 @@ s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec
if (bonusCritStage > 0)
critChance *= bonusCritStage;
if (gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY)
if (gBattleMons[battlerAtk].volatiles.focusEnergy)
critChance *= 4;
else if (gBattleMons[battlerAtk].status2 & STATUS2_DRAGON_CHEER)
else if (gBattleMons[battlerAtk].volatiles.dragonCheer)
critChance *= 2;
if (holdEffectCritStage > 0)
@ -1836,14 +1838,15 @@ static void Cmd_critcalc(void)
{
CMD_ARGS();
u32 partySlot = gBattlerPartyIndexes[gBattlerAttacker];
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
u32 partySlot = gBattlerPartyIndexes[gBattlerAttacker],
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove),
abilityAtk = GetBattlerAbility(gBattlerAttacker),
battlerDef;
bool32 calcSpreadMoveDamage = IsSpreadMove(moveTarget) && !IsBattleMoveStatus(gCurrentMove);
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
enum ItemHoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
gPotentialItemEffectBattler = gBattlerAttacker;
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
{
if (gBattleStruct->calculatedDamageDone)
break;
@ -2120,7 +2123,7 @@ static inline bool32 DoesBattlerNegateDamage(u32 battler)
u32 species = gBattleMons[battler].species;
u32 ability = GetBattlerAbility(battler);
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (gBattleMons[battler].volatiles.transformed)
return FALSE;
if (ability == ABILITY_DISGUISE && species == SPECIES_MIMIKYU)
return TRUE;
@ -2307,7 +2310,7 @@ static void Cmd_attackanimation(void)
if (!(moveResultFlags & MOVE_RESULT_NO_EFFECT))
{
u32 multihit;
if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE)
if (gBattleMons[gBattlerTarget].volatiles.substitute)
{
multihit = gMultiHitCounter;
}
@ -3138,20 +3141,20 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
break;
case MOVE_EFFECT_CONFUSION:
if (!CanBeConfused(gEffectBattler)
|| gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION
|| gBattleMons[gEffectBattler].volatiles.confusionTurns
|| (gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_SAFEGUARD && !(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT) && !primary))
{
gBattlescriptCurrInstr++;
}
else
{
gBattleMons[gEffectBattler].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); // 2-5 turns
gBattleMons[gEffectBattler].volatiles.confusionTurns = ((Random()) % 4) + 2; // 2-5 turns
// If the confusion is activating due to being released from Sky Drop, go to "confused due to fatigue" script.
// Otherwise, do normal confusion script.
if (GetMoveEffect(gCurrentMove) == EFFECT_SKY_DROP)
{
gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE);
gBattleMons[gEffectBattler].volatiles.lockConfusionTurns = 0;
gBattlerAttacker = gEffectBattler;
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
}
@ -3179,14 +3182,14 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
gBattlescriptCurrInstr++;
}
}
else if (gBattleMons[gEffectBattler].status2 & STATUS2_FLINCHED)
else if (gBattleMons[gEffectBattler].volatiles.flinched)
{
gBattlescriptCurrInstr++;
}
else if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber
&& !(GetActiveGimmick(gEffectBattler) == GIMMICK_DYNAMAX))
{
gBattleMons[gEffectBattler].status2 |= STATUS2_FLINCHED;
gBattleMons[gEffectBattler].volatiles.flinched = TRUE;
gBattlescriptCurrInstr++;
}
else
@ -3195,11 +3198,11 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
}
break;
case MOVE_EFFECT_UPROAR:
if (!(gBattleMons[gEffectBattler].status2 & STATUS2_UPROAR))
if (!gBattleMons[gEffectBattler].volatiles.uproarTurns)
{
gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS;
gBattleMons[gEffectBattler].volatiles.multipleTurns = TRUE;
gLockedMoves[gEffectBattler] = gCurrentMove;
gBattleMons[gEffectBattler].status2 |= STATUS2_UPROAR_TURN(B_UPROAR_TURNS >= GEN_5 ? 3 : (Random() & 3) + 2);
gBattleMons[gEffectBattler].volatiles.uproarTurns = B_UPROAR_TURNS >= GEN_5 ? 3 : (Random() & 3) + 2;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_MoveEffectUproar;
@ -3260,13 +3263,13 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
}
break;
case MOVE_EFFECT_WRAP:
if (gBattleMons[gEffectBattler].status2 & STATUS2_WRAPPED)
if (gBattleMons[gEffectBattler].volatiles.wrapped)
{
gBattlescriptCurrInstr++;
}
else
{
gBattleMons[gEffectBattler].status2 |= STATUS2_WRAPPED;
gBattleMons[gEffectBattler].volatiles.wrapped = TRUE;
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_GRIP_CLAW)
gDisableStructs[gEffectBattler].wrapTurns = B_BINDING_TURNS >= GEN_5 ? 7 : 5;
else
@ -3401,13 +3404,13 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
if (B_SKIP_RECHARGE == GEN_1 && !IsBattlerAlive(gBattlerTarget)) // Skip recharge if gen 1 and foe is KO'd
break;
gBattleMons[gEffectBattler].status2 |= STATUS2_RECHARGE;
gBattleMons[gEffectBattler].volatiles.recharge = TRUE;
gDisableStructs[gEffectBattler].rechargeTimer = 2;
gLockedMoves[gEffectBattler] = gCurrentMove;
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_RAGE:
gBattleMons[gBattlerAttacker].status2 |= STATUS2_RAGE;
gBattleMons[gBattlerAttacker].volatiles.rage = TRUE;
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_STEAL_ITEM:
@ -3440,15 +3443,15 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
}
break;
case MOVE_EFFECT_PREVENT_ESCAPE:
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION))
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention)
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION;
gBattleMons[gBattlerTarget].volatiles.escapePrevention = TRUE;
gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker;
}
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_NIGHTMARE:
gBattleMons[gBattlerTarget].status2 |= STATUS2_NIGHTMARE;
gBattleMons[gBattlerTarget].volatiles.nightmare = TRUE;
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_ALL_STATS_UP:
@ -3484,15 +3487,15 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
break;
case MOVE_EFFECT_THRASH:
// Petal Dance doesn't lock mons that copy the move with Dancer
if (gSpecialStatuses[gEffectBattler].dancerUsedMove || gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE)
if (gSpecialStatuses[gEffectBattler].dancerUsedMove || gBattleMons[gEffectBattler].volatiles.lockConfusionTurns)
{
gBattlescriptCurrInstr++;
}
else
{
gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS;
gBattleMons[gEffectBattler].volatiles.multipleTurns = TRUE;
gLockedMoves[gEffectBattler] = gCurrentMove;
gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN(RandomUniform(RNG_RAMPAGE_TURNS, 2, 3));
gBattleMons[gEffectBattler].volatiles.lockConfusionTurns = RandomUniform(RNG_RAMPAGE_TURNS, 2, 3);
}
break;
case MOVE_EFFECT_CLEAR_SMOG:
@ -3597,19 +3600,19 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
}
break;
case MOVE_EFFECT_TRAP_BOTH:
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_ESCAPE_PREVENTION))
if (!(gBattleMons[gBattlerTarget].volatiles.escapePrevention || gBattleMons[gBattlerAttacker].volatiles.escapePrevention))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_BothCanNoLongerEscape;
}
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION))
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention)
gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker;
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_ESCAPE_PREVENTION))
if (!gBattleMons[gBattlerAttacker].volatiles.escapePrevention)
gDisableStructs[gBattlerAttacker].battlerPreventingEscape = gBattlerTarget;
gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_ESCAPE_PREVENTION;
gBattleMons[gBattlerTarget].volatiles.escapePrevention = TRUE;
gBattleMons[gBattlerAttacker].volatiles.escapePrevention = TRUE;
break;
case MOVE_EFFECT_REMOVE_ARG_TYPE:
{
@ -3893,7 +3896,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct)
gBattleMons[gBattlerTarget].pp[i] -= ppToDeduct;
if (!(gDisableStructs[gBattlerTarget].mimickedMoves & (1u << i))
&& !(gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[gBattlerTarget].volatiles.transformed))
{
BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[gBattlerTarget].pp[i]), &gBattleMons[gBattlerTarget].pp[i]);
MarkBattlerForControllerExec(gBattlerTarget);
@ -4128,9 +4131,9 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
{
if (!IsBattlerAlly(battler, gBattlerTarget))
continue;
if (!(gBattleMons[battler].status2 & STATUS2_WRAPPED))
if (!gBattleMons[battler].volatiles.wrapped)
{
gBattleMons[battler].status2 |= STATUS2_WRAPPED;
gBattleMons[battler].volatiles.wrapped = TRUE;
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_GRIP_CLAW)
gDisableStructs[battler].wrapTurns = (B_BINDING_TURNS >= GEN_5) ? 7 : 5;
else
@ -4539,12 +4542,12 @@ static void Cmd_jumpifstatus(void)
static void Cmd_jumpifvolatile(void)
{
CMD_ARGS(u8 battler, u8 volatileStatus, const u8 *jumpInstr);
CMD_ARGS(u8 battler, u8 _volatile, const u8 *jumpInstr);
u8 battler = GetBattlerForBattleScript(cmd->battler);
const u8 *jumpInstr = cmd->jumpInstr;
if (GetMonVolatile(battler, cmd->volatileStatus) && IsBattlerAlive(battler))
if (GetMonVolatile(battler, cmd->_volatile) && IsBattlerAlive(battler))
gBattlescriptCurrInstr = jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
@ -6119,7 +6122,7 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_RAGE: // rage check
if (gBattleMons[gBattlerTarget].status2 & STATUS2_RAGE
if (gBattleMons[gBattlerTarget].volatiles.rage
&& IsBattlerAlive(gBattlerTarget)
&& gBattlerAttacker != gBattlerTarget
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
@ -6313,7 +6316,7 @@ static void Cmd_moveend(void)
for (i = 0; i < gBattlersCount; i++)
{
if (gDisableStructs[i].substituteHP == 0)
gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE;
gBattleMons[i].volatiles.substitute = FALSE;
}
gBattleScripting.moveendState++;
break;
@ -6348,7 +6351,7 @@ static void Cmd_moveend(void)
if (!IsOnPlayerSide(gBattlerAttacker))
UpdateStallMons();
if ((gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE))
|| (gBattleMons[gBattlerAttacker].status2 & (STATUS2_FLINCHED))
|| (gBattleMons[gBattlerAttacker].volatiles.flinched)
|| gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility)
gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2;
@ -7050,7 +7053,7 @@ static void Cmd_moveend(void)
if (B_RAMPAGE_CANCELLING >= GEN_5
&& MoveHasAdditionalEffectSelf(gCurrentMove, MOVE_EFFECT_THRASH) // If we're rampaging
&& gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT // And it is unusable
&& (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn
&& gBattleMons[gBattlerAttacker].volatiles.lockConfusionTurns != 1) // And won't end this turn
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_IGNORE); // Cancel it
if (gBattleStruct->savedAttackerCount > 0)
@ -7320,10 +7323,9 @@ static void Cmd_switchindataupdate(void)
{
gBattleMons[battler].statStages[i] = oldData.statStages[i];
}
gBattleMons[battler].status2 = oldData.status2;
}
SwitchInClearSetData(battler);
SwitchInClearSetData(battler, &oldData.volatiles);
if (gBattleTypeFlags & BATTLE_TYPE_PALACE
&& gBattleMons[battler].maxHP / 2 >= gBattleMons[battler].hp
@ -7843,7 +7845,7 @@ static void UpdateSentMonFlags(u32 battler)
static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId)
{
gBattleMons[battler].status2 &= ~STATUS2_DESTINY_BOND;
gBattleMons[battler].volatiles.destinyBond = FALSE;
gHitMarker &= ~HITMARKER_DESTINYBOND;
gBattleScripting.battler = battler;
gBattleCommunication[MULTISTRING_CHOOSER] = multistringId;
@ -8220,7 +8222,7 @@ static void Cmd_handlelearnnewmove(void)
u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
if (gBattlerPartyIndexes[battler] == monId
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[battler].volatiles.transformed))
{
GiveMoveToBattleMon(&gBattleMons[battler], learnMove);
}
@ -8228,7 +8230,7 @@ static void Cmd_handlelearnnewmove(void)
{
battler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
if (gBattlerPartyIndexes[battler] == monId
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[battler].volatiles.transformed))
{
GiveMoveToBattleMon(&gBattleMons[battler], learnMove);
}
@ -8643,58 +8645,29 @@ static void Cmd_jumptocalledmove(void)
static void Cmd_statusanimation(void)
{
CMD_ARGS(u8 battler);
CMD_ARGS(u8 battler, u32 status, bool8 isVolatile);
if (gBattleControllerExecFlags == 0)
{
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 battler = GetBattlerForBattleScript(cmd->battler),
statusFlag = (cmd->isVolatile || cmd->status) ? cmd->status : gBattleMons[battler].status1;
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
&& gDisableStructs[battler].substituteHP == 0
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
{
BtlController_EmitStatusAnimation(battler, B_COMM_TO_CONTROLLER, FALSE, gBattleMons[battler].status1);
BtlController_EmitStatusAnimation(battler, B_COMM_TO_CONTROLLER, cmd->isVolatile, statusFlag);
MarkBattlerForControllerExec(battler);
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_status2animation(void)
static void Cmd_unused_0x65(void)
{
CMD_ARGS(u8 battler, u32 status2);
if (gBattleControllerExecFlags == 0)
{
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 status2ToAnim = cmd->status2;
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
&& gDisableStructs[battler].substituteHP == 0
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
{
BtlController_EmitStatusAnimation(battler, B_COMM_TO_CONTROLLER, TRUE, gBattleMons[battler].status2 & status2ToAnim);
MarkBattlerForControllerExec(battler);
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_chosenstatusanimation(void)
static void Cmd_unused_0x66(void)
{
CMD_ARGS(u8 battler, bool8 isStatus2, u32 status);
if (gBattleControllerExecFlags == 0)
{
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 wantedStatus = cmd->status;
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
&& gDisableStructs[battler].substituteHP == 0
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
{
BtlController_EmitStatusAnimation(battler, B_COMM_TO_CONTROLLER, cmd->isStatus2, wantedStatus);
MarkBattlerForControllerExec(battler);
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_yesnobox(void)
@ -9454,13 +9427,13 @@ static bool32 TryTidyUpClear(u32 battlerAtk, bool32 clear)
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (gBattleMons[i].status2 & STATUS2_SUBSTITUTE)
if (gBattleMons[i].volatiles.substitute)
{
if (clear)
{
gBattlerTarget = i;
gDisableStructs[i].substituteHP = 0;
gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE;
gBattleMons[i].volatiles.substitute = FALSE;
BattleScriptCall(BattleScript_SubstituteFade);
}
gBattlerAttacker = saveBattler;
@ -9799,7 +9772,7 @@ static void Cmd_various(void)
{
VARIOUS_ARGS(u8 infatuateWith);
gBattleScripting.battler = battler;
gBattleMons[battler].status2 |= STATUS2_INFATUATED_WITH(GetBattlerForBattleScript(cmd->infatuateWith));
gBattleMons[battler].volatiles.infatuation = INFATUATED_WITH(GetBattlerForBattleScript(cmd->infatuateWith));
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
@ -9939,12 +9912,6 @@ static void Cmd_various(void)
gStatuses3[battler] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR | STATUS3_SKY_DROPPED);
break;
}
case VARIOUS_SET_POWDER:
{
VARIOUS_ARGS();
gBattleMons[battler].status2 |= STATUS2_POWDER;
break;
}
case VARIOUS_ACUPRESSURE:
{
VARIOUS_ARGS(const u8 *failInstr);
@ -10953,10 +10920,10 @@ static void Cmd_various(void)
gBattleStruct->skyDropTargets[gBattlerAttacker] = gBattlerTarget;
gBattleStruct->skyDropTargets[gBattlerTarget] = gBattlerAttacker;
// End any multiturn effects caused by the target except STATUS2_LOCK_CONFUSE
gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_MULTIPLETURNS);
gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_UPROAR);
gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_BIDE);
// End any multiturn effects caused by the target except VOLATILE_LOCK_CONFUSE
gBattleMons[gBattlerTarget].volatiles.multipleTurns = 0;
gBattleMons[gBattlerTarget].volatiles.uproarTurns= 0;
gBattleMons[gBattlerTarget].volatiles.bideTurns = 0;
gDisableStructs[gBattlerTarget].rolloutTimer = 0;
gDisableStructs[gBattlerTarget].furyCutterCounter = 0;
@ -10982,7 +10949,7 @@ static void Cmd_various(void)
}
// Confuse target if they were in the middle of Petal Dance/Outrage/Thrash when targeted.
if (gBattleMons[gBattlerTarget].status2 & STATUS2_LOCK_CONFUSE)
if (gBattleMons[gBattlerTarget].volatiles.lockConfusionTurns)
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION;
return;
}
@ -10999,11 +10966,11 @@ static void Cmd_various(void)
gBattleStruct->skyDropTargets[gEffectBattler] = SKY_DROP_NO_TARGET;
// If the target was in the middle of Outrage/Thrash/etc. when targeted by Sky Drop, confuse them on release and do proper animation
if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE && CanBeConfused(gEffectBattler))
if (gBattleMons[gEffectBattler].volatiles.lockConfusionTurns && CanBeConfused(gEffectBattler))
{
gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE);
gBattleMons[gEffectBattler].volatiles.lockConfusionTurns = 0;
gBattlerAttacker = gEffectBattler;
gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
gBattleMons[gBattlerTarget].volatiles.confusionTurns = ((Random()) % 4) + 2;
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
return;
}
@ -11225,7 +11192,7 @@ static void Cmd_various(void)
}
else
{
if (!(gBattleMons[battler].status2 & STATUS2_ESCAPE_PREVENTION))
if (!gBattleMons[battler].volatiles.escapePrevention)
gDisableStructs[battler].noRetreat = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -11235,9 +11202,9 @@ static void Cmd_various(void)
{
VARIOUS_ARGS();
// Check infatuation
if (gBattleMons[battler].status2 & STATUS2_INFATUATION)
if (gBattleMons[battler].volatiles.infatuation)
{
gBattleMons[battler].status2 &= ~(STATUS2_INFATUATION);
gBattleMons[battler].volatiles.infatuation = 0;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; // STRINGID_TARGETGOTOVERINFATUATION
StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn);
}
@ -11256,9 +11223,9 @@ static void Cmd_various(void)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_ENCORE; // STRINGID_PKMNENCOREENDED
}
// Check torment
if (gBattleMons[battler].status2 & STATUS2_TORMENT)
if (gBattleMons[battler].volatiles.torment == TRUE)
{
gBattleMons[battler].status2 &= ~(STATUS2_TORMENT);
gBattleMons[battler].volatiles.torment = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT;
}
// Check heal block
@ -11673,7 +11640,7 @@ bool8 UproarWakeUpCheck(u8 battler)
for (i = 0; i < gBattlersCount; i++)
{
if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || hasSoundproof)
if (!(gBattleMons[i].volatiles.uproarTurns) || hasSoundproof)
continue;
gBattleScripting.battler = i;
@ -12317,10 +12284,10 @@ static void Cmd_setbide(void)
{
CMD_ARGS();
gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS;
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = TRUE;
gLockedMoves[gBattlerAttacker] = gCurrentMove;
gBideDmg[gBattlerAttacker] = 0;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_BIDE_TURN(2);
gBattleMons[gBattlerAttacker].volatiles.bideTurns = 2;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -12838,14 +12805,14 @@ static void Cmd_tryinfatuating(void)
}
else
{
if (gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION
if (gBattleMons[gBattlerTarget].volatiles.infatuation
|| !AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker);
gBattleMons[gBattlerTarget].volatiles.infatuation = INFATUATED_WITH(gBattlerAttacker);
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -12865,7 +12832,7 @@ static void Cmd_updatestatusicon(void)
{
if (!(gAbsentBattlerFlags & (1u << battler)))
{
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1, gBattleMons[battler].status2);
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
}
}
@ -12876,7 +12843,7 @@ static void Cmd_updatestatusicon(void)
battler = gBattlerAttacker;
if (!(gAbsentBattlerFlags & (1u << battler)))
{
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1, gBattleMons[battler].status2);
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
}
if ((IsDoubleBattle()))
@ -12884,7 +12851,7 @@ static void Cmd_updatestatusicon(void)
battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker)));
if (!(gAbsentBattlerFlags & (1u << battler)))
{
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1, gBattleMons[battler].status2);
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
}
}
@ -12893,7 +12860,7 @@ static void Cmd_updatestatusicon(void)
else
{
battler = GetBattlerForBattleScript(cmd->battler);
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1, gBattleMons[battler].status2);
BtlController_EmitStatusIconUpdate(battler, B_COMM_TO_CONTROLLER, gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -12924,22 +12891,22 @@ static void Cmd_setfocusenergy(void)
enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove);
if ((effect == EFFECT_DRAGON_CHEER && (!(IsDoubleBattle()) || (gAbsentBattlerFlags & (1u << battler))))
|| gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY)
|| gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy)
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FAILED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FOCUS_ENERGY_FAILED;
}
else if (effect == EFFECT_DRAGON_CHEER && !IS_BATTLER_OF_TYPE(battler, TYPE_DRAGON))
{
gBattleMons[battler].status2 |= STATUS2_DRAGON_CHEER;
gBattleMons[battler].volatiles.dragonCheer = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_GETTING_PUMPED;
}
else
{
if (GetGenConfig(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO) >= GEN_3)
gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY;
gBattleMons[battler].volatiles.focusEnergy = TRUE;
else
gBattleMons[battler].status2 |= STATUS2_DRAGON_CHEER;
gBattleMons[battler].volatiles.dragonCheer = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_GETTING_PUMPED;
}
gBattlescriptCurrInstr = cmd->nextInstr;
@ -12951,7 +12918,7 @@ static void Cmd_transformdataexecution(void)
gChosenMove = MOVE_UNAVAILABLE;
gBattlescriptCurrInstr = cmd->nextInstr;
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED
if (gBattleMons[gBattlerTarget].volatiles.transformed
|| gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON
|| gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE_NO_COMMANDER)
{
@ -12964,7 +12931,7 @@ static void Cmd_transformdataexecution(void)
u8 *battleMonAttacker, *battleMonTarget;
u8 timesGotHit;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_TRANSFORMED;
gBattleMons[gBattlerAttacker].volatiles.transformed = TRUE;
gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE;
gDisableStructs[gBattlerAttacker].disableTimer = 0;
gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality;
@ -13029,8 +12996,8 @@ static void Cmd_setsubstitute(void)
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_SUBSTITUTE;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_WRAPPED;
gBattleMons[gBattlerAttacker].volatiles.substitute = TRUE;
gBattleMons[gBattlerAttacker].volatiles.wrapped = FALSE;
if (factor == 2)
gDisableStructs[gBattlerAttacker].substituteHP = gBattleStruct->moveDamage[gBattlerAttacker] / 2;
else
@ -13047,7 +13014,7 @@ static void Cmd_mimicattackcopy(void)
CMD_ARGS(const u8 *failInstr);
if ((IsMoveMimicBanned(gLastMoves[gBattlerTarget]))
|| (gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
|| (gBattleMons[gBattlerAttacker].volatiles.transformed)
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
{
@ -13296,7 +13263,7 @@ static void Cmd_settypetorandomresistance(void)
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (gBattleMoveEffects[GetMoveEffect(gLastLandedMoves[gBattlerAttacker])].twoTurnEffect
&& gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS)
&& gBattleMons[gLastHitBy[gBattlerAttacker]].volatiles.multipleTurns)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -13422,7 +13389,7 @@ static void Cmd_copymovepermanently(void)
gChosenMove = MOVE_UNAVAILABLE;
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
if (!(gBattleMons[gBattlerAttacker].volatiles.transformed)
&& gLastPrintedMoves[gBattlerTarget] != MOVE_UNAVAILABLE
&& !IsMoveSketchBanned(gLastPrintedMoves[gBattlerTarget]))
{
@ -13541,14 +13508,14 @@ static void Cmd_trysetdestinybond(void)
}
else
{
gBattleMons[gBattlerAttacker].status2 |= STATUS2_DESTINY_BOND;
gBattleMons[gBattlerAttacker].volatiles.destinyBond = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void TrySetDestinyBondToHappen(void)
{
if (gBattleMons[gBattlerTarget].status2 & STATUS2_DESTINY_BOND
if (gBattleMons[gBattlerTarget].volatiles.destinyBond
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& !(gHitMarker & HITMARKER_GRUDGE))
{
@ -13629,7 +13596,7 @@ static void Cmd_tryspiteppreduce(void)
// if (MOVE_IS_PERMANENT(gBattlerTarget, i)), but backwards
if (!(gDisableStructs[gBattlerTarget].mimickedMoves & (1u << i))
&& !(gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[gBattlerTarget].volatiles.transformed))
{
BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[gBattlerTarget].pp[i]), &gBattleMons[gBattlerTarget].pp[i]);
MarkBattlerForControllerExec(gBattlerTarget);
@ -13672,7 +13639,7 @@ static void Cmd_healpartystatus(void)
else
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SOOTHING_AROMA;
gBattleMons[gBattlerAttacker].status1 = 0;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[gBattlerAttacker].volatiles.nightmare = FALSE;
}
else
{
@ -13688,7 +13655,7 @@ static void Cmd_healpartystatus(void)
|| !(isSoundMove && GetBattlerAbility(partner) == ABILITY_SOUNDPROOF))
{
gBattleMons[partner].status1 = 0;
gBattleMons[partner].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[partner].volatiles.nightmare = FALSE;
}
else
{
@ -13753,13 +13720,13 @@ static void Cmd_cursetarget(void)
{
CMD_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].status2 & STATUS2_CURSED)
if (gBattleMons[gBattlerTarget].volatiles.cursed)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_CURSED;
gBattleMons[gBattlerTarget].volatiles.cursed = TRUE;
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
@ -13787,11 +13754,11 @@ static void Cmd_trysetspikes(void)
}
}
static void Cmd_setforesight(void)
static void Cmd_setvolatile(void)
{
CMD_ARGS();
CMD_ARGS(u8 battler, u8 _volatile, u8 value);
gBattleMons[gBattlerTarget].status2 |= STATUS2_FORESIGHT;
SetMonVolatile(GetBattlerForBattleScript(cmd->battler), cmd->_volatile, cmd->value);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -13835,16 +13802,16 @@ static void Cmd_handlerollout(void)
}
else
{
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) // First hit.
if (!(gBattleMons[gBattlerAttacker].volatiles.multipleTurns)) // First hit.
{
gDisableStructs[gBattlerAttacker].rolloutTimer = 5;
gDisableStructs[gBattlerAttacker].rolloutTimerStartValue = 5;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS;
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = TRUE;
gLockedMoves[gBattlerAttacker] = gCurrentMove;
}
if (--gDisableStructs[gBattlerAttacker].rolloutTimer == 0) // Last hit.
{
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_MULTIPLETURNS;
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE;
}
gBattlescriptCurrInstr = cmd->nextInstr;
@ -13855,7 +13822,7 @@ static void Cmd_jumpifconfusedandstatmaxed(void)
{
CMD_ARGS(u8 stat, const u8 *jumpInstr);
if (gBattleMons[gBattlerTarget].status2 & STATUS2_CONFUSION
if (gBattleMons[gBattlerTarget].volatiles.confusionTurns > 0
&& !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN))
gBattlescriptCurrInstr = cmd->jumpInstr; // Fails if we're confused AND stat cannot be raised
else
@ -14130,10 +14097,10 @@ static void Cmd_rapidspinfree(void)
u8 atkSide = GetBattlerSide(gBattlerAttacker);
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_WRAPPED)
if (gBattleMons[gBattlerAttacker].volatiles.wrapped)
{
gBattleScripting.battler = gBattlerTarget;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_WRAPPED;
gBattleMons[gBattlerAttacker].volatiles.wrapped = FALSE;
gBattlerTarget = gBattleStruct->wrappedBy[gBattlerAttacker];
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[gBattlerAttacker]);
BattleScriptCall(BattleScript_WrapFree);
@ -14163,12 +14130,8 @@ static void Cmd_rapidspinfree(void)
}
}
static void Cmd_setdefensecurlbit(void)
static void Cmd_unused_0xBF(void)
{
CMD_ARGS();
gBattleMons[gBattlerAttacker].status2 |= STATUS2_DEFENSE_CURL;
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_recoverbasedonsunlight(void)
@ -14536,14 +14499,14 @@ static void Cmd_settorment(void)
{
CMD_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT
if (gBattleMons[gBattlerTarget].volatiles.torment == TRUE
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT;
gBattleMons[gBattlerTarget].volatiles.torment = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -15300,7 +15263,7 @@ static void Cmd_settypebasedhalvers(void)
bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (!(gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE))
if (!gBattleMons[battlerDef].volatiles.substitute)
return FALSE;
else if (MoveIgnoresSubstitute(move))
return FALSE;
@ -15313,7 +15276,7 @@ bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move)
bool32 DoesDisguiseBlockMove(u32 battler, u32 move)
{
if (!(gBattleMons[battler].species == SPECIES_MIMIKYU_DISGUISED || gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED)
|| gBattleMons[battler].status2 & STATUS2_TRANSFORMED
|| gBattleMons[battler].volatiles.transformed
|| (!gProtectStructs[battler].confusionSelfDmg && (IsBattleMoveStatus(move) || gHitMarker & HITMARKER_PASSIVE_DAMAGE))
|| gHitMarker & HITMARKER_IGNORE_DISGUISE
|| !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_DISGUISE))
@ -16881,34 +16844,23 @@ void BS_ItemCureStatus(void)
{
NATIVE_ARGS(const u8 *noStatusInstr);
u32 battler = gBattlerAttacker;
u32 previousStatus2 = 0;
bool32 statusChanged = FALSE;
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
// Heal Status2 conditions if battler is active.
// Heal volatile conditions if battler is active.
if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker])
{
previousStatus2 = gBattleMons[battler].status2;
gBattleMons[gBattlerAttacker].status2 &= ~GetItemStatus2Mask(gLastUsedItem);
}
statusChanged = ItemHealMonVolatile(battler, gLastUsedItem);
else if (IsDoubleBattle()
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
{
battler = BATTLE_PARTNER(gBattlerAttacker);
previousStatus2 = gBattleMons[battler].status2;
gBattleMons[battler].status2 &= ~GetItemStatus2Mask(gLastUsedItem);
}
if (previousStatus2 != gBattleMons[battler].status2)
statusChanged = TRUE;
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
statusChanged = ItemHealMonVolatile(BATTLE_PARTNER(gBattlerAttacker), gLastUsedItem);
// Heal Status1 conditions.
if (!HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], GetItemStatus1Mask(gLastUsedItem), battler))
{
statusChanged = TRUE;
if (GetItemStatus1Mask(gLastUsedItem) & STATUS1_SLEEP)
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
if (GetItemStatus2Mask(gLastUsedItem) & STATUS2_CONFUSION)
gBattleMons[battler].volatiles.nightmare = FALSE;
if (ItemHasVolatileFlag(gLastUsedItem, VOLATILE_CONFUSION))
gStatuses4[battler] &= ~STATUS4_INFINITE_CONFUSION;
}
@ -17168,7 +17120,7 @@ void BS_TrySetOctolock(void)
else
{
gDisableStructs[battler].octolock = TRUE;
gBattleMons[battler].status2 |= STATUS2_ESCAPE_PREVENTION;
gBattleMons[battler].volatiles.escapePrevention = TRUE;
gDisableStructs[battler].battlerPreventingEscape = gBattlerAttacker;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -17962,7 +17914,7 @@ void BS_CheckPokeFlute(void)
if (GetBattlerAbility(i) != ABILITY_SOUNDPROOF)
{
gBattleMons[i].status1 &= ~STATUS1_SLEEP;
gBattleMons[i].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[i].volatiles.nightmare = FALSE;
}
}
@ -18243,8 +18195,7 @@ void BS_TrySetConfusion(void)
if (CanBeConfused(gBattlerTarget))
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
gBattleMons[gBattlerTarget].volatiles.confusionTurns = ((Random()) % 4) + 2;
gBattleCommunication[MULTIUSE_STATE] = 1;
gEffectBattler = gBattlerTarget;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -18259,13 +18210,12 @@ void BS_TrySetInfatuation(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION)
if (!gBattleMons[gBattlerTarget].volatiles.infatuation
&& gBattleMons[gBattlerTarget].ability != ABILITY_OBLIVIOUS
&& !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL)
&& AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget))
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker);
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
gBattleMons[gBattlerTarget].volatiles.infatuation = INFATUATED_WITH(gBattlerAttacker);
gBattleCommunication[MULTIUSE_STATE] = 2;
gEffectBattler = gBattlerTarget;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -18280,11 +18230,10 @@ void BS_TrySetEscapePrevention(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION))
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention)
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION;
gBattleMons[gBattlerTarget].volatiles.escapePrevention = TRUE;
gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker;
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
gEffectBattler = gBattlerTarget;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -18298,12 +18247,11 @@ void BS_TrySetTorment(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT)
if (!(gBattleMons[gBattlerTarget].volatiles.torment == TRUE)
&& !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL))
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT;
gBattleMons[gBattlerTarget].volatiles.torment = TRUE;
gDisableStructs[gBattlerTarget].tormentTimer = gBattleTurnCounter + 3; // 3 turns excluding current turn
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
gEffectBattler = gBattlerTarget;
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -64,7 +64,7 @@ bool32 CanTerastallize(u32 battler)
{
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, FALSE);
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED && GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_TERAPAGOS)
if (gBattleMons[battler].volatiles.transformed && GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_TERAPAGOS)
return FALSE;
// Prevents Zigzagoon from terastalizing in vanilla.

View File

@ -412,7 +412,7 @@ void HandleAction_UseMove(void)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(MOVE_STRUGGLE, NO_TARGET_OVERRIDE);
}
else if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS || gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE)
else if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns || gBattleMons[gBattlerAttacker].volatiles.recharge)
{
gCurrentMove = gChosenMove = gLockedMoves[gBattlerAttacker];
}
@ -1056,18 +1056,18 @@ const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
// If target was sky dropped in the middle of Outrage/Thrash/Petal Dance,
// confuse them upon release and display "confused by fatigue" message & animation.
// Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn.
if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && skyDropState != SKY_DROP_STATUS_YAWN)
if (gBattleMons[otherSkyDropper].volatiles.lockConfusionTurns && skyDropState != SKY_DROP_STATUS_YAWN)
{
gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE);
gBattleMons[otherSkyDropper].volatiles.lockConfusionTurns = 0;
// If the target can be confused, confuse them.
// Don't use CanBeConfused, can cause issues in edge cases.
if (!(gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION
if (!(gBattleMons[otherSkyDropper].volatiles.confusionTurns > 0
|| IsAbilityAndRecord(otherSkyDropper, GetBattlerAbility(otherSkyDropper), ABILITY_OWN_TEMPO)
|| IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN)))
{
// Set confused status
gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
gBattleMons[otherSkyDropper].volatiles.confusionTurns = ((Random()) % 4) + 2;
if (skyDropState == SKY_DROP_ATTACKCANCELLER_CHECK)
{
@ -1094,7 +1094,7 @@ const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
}
// Clear skyDropTargets data, unless this CancelMultiTurnMoves is caused by Yawn, attackcanceler, or VARIOUS_GRAVITY_ON_AIRBORNE_MONS
if (!(gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[battler] < 4)
if (!(gBattleMons[otherSkyDropper].volatiles.lockConfusionTurns) && gBattleStruct->skyDropTargets[battler] < 4)
{
gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET;
gBattleStruct->skyDropTargets[otherSkyDropper] = SKY_DROP_NO_TARGET;
@ -1106,18 +1106,18 @@ const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
const u8 *CancelMultiTurnMoves(u32 battler, enum SkyDropState skyDropState)
{
const u8 *result = NULL;
gBattleMons[battler].status2 &= ~(STATUS2_UPROAR);
gBattleMons[battler].status2 &= ~(STATUS2_BIDE);
gBattleMons[battler].volatiles.uproarTurns = 0;
gBattleMons[battler].volatiles.bideTurns = 0;
if (B_RAMPAGE_CANCELLING < GEN_5)
{
gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS);
gBattleMons[battler].status2 &= ~(STATUS2_LOCK_CONFUSE);
gBattleMons[battler].volatiles.multipleTurns = 0;
gBattleMons[battler].volatiles.lockConfusionTurns = 0;
}
else if (!(gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE)
|| ((gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE) > STATUS2_LOCK_CONFUSE_TURN(1)))
else if (!gBattleMons[battler].volatiles.lockConfusionTurns
|| gBattleMons[battler].volatiles.lockConfusionTurns > 1)
{
gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS);
gBattleMons[battler].volatiles.multipleTurns = 0;
}
// Clear battler's semi-invulnerable bits if they are not held by Sky Drop.
@ -1341,7 +1341,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].status2 & STATUS2_TORMENT))
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].volatiles.torment == TRUE))
{
CancelMultiTurnMoves(battler, SKY_DROP_IGNORE);
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1588,7 +1588,7 @@ u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check)
else if (check & MOVE_LIMITATION_DISABLED && move == gDisableStructs[battler].disabledMove)
unusableMoves |= 1u << i;
// Torment
else if (check & MOVE_LIMITATION_TORMENTED && move == gLastMoves[battler] && gBattleMons[battler].status2 & STATUS2_TORMENT)
else if (check & MOVE_LIMITATION_TORMENTED && move == gLastMoves[battler] && gBattleMons[battler].volatiles.torment == TRUE)
unusableMoves |= 1u << i;
// Taunt
else if (check & MOVE_LIMITATION_TAUNT && gDisableStructs[battler].tauntTimer && IsBattleMoveStatus(move))
@ -1849,8 +1849,8 @@ void TryClearRageAndFuryCutter(void)
s32 i;
for (i = 0; i < gBattlersCount; i++)
{
if ((gBattleMons[i].status2 & STATUS2_RAGE) && gChosenMoveByBattler[i] != MOVE_RAGE)
gBattleMons[i].status2 &= ~STATUS2_RAGE;
if (gBattleMons[i].volatiles.rage && gChosenMoveByBattler[i] != MOVE_RAGE)
gBattleMons[i].volatiles.rage = FALSE;
if (gDisableStructs[i].furyCutterCounter != 0 && gChosenMoveByBattler[i] != MOVE_FURY_CUTTER)
gDisableStructs[i].furyCutterCounter = 0;
}
@ -1871,7 +1871,7 @@ static inline bool32 TryFormChangeBeforeMove(void)
static inline bool32 TryActivatePowderStatus(u32 move)
{
u32 partnerMove = gBattleMons[BATTLE_PARTNER(gBattlerAttacker)].moves[gBattleStruct->chosenMovePositions[BATTLE_PARTNER(gBattlerAttacker)]];
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER))
if (!gBattleMons[gBattlerAttacker].volatiles.powder)
return FALSE;
if (GetBattleMoveType(move) == TYPE_FIRE && !gBattleStruct->pledgeMove)
return TRUE;
@ -1890,7 +1890,7 @@ void SetAtkCancellerForCalledMove(void)
static enum MoveCanceller CancellerFlags(void)
{
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_DESTINY_BOND;
gBattleMons[gBattlerAttacker].volatiles.destinyBond = FALSE;
gStatuses3[gBattlerAttacker] &= ~STATUS3_GRUDGE;
gStatuses4[gBattlerAttacker] &= ~STATUS4_GLAIVE_RUSH;
return MOVE_STEP_SUCCESS;
@ -1917,9 +1917,9 @@ static enum MoveCanceller CancellerSkyDrop(void)
static enum MoveCanceller CancellerRecharge(void)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE)
if (gBattleMons[gBattlerAttacker].volatiles.recharge)
{
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RECHARGE;
gBattleMons[gBattlerAttacker].volatiles.recharge = TRUE;
gDisableStructs[gBattlerAttacker].rechargeTimer = 0;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
@ -1937,7 +1937,7 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void)
{
TryDeactivateSleepClause(GetBattlerSide(gBattlerAttacker), gBattlerPartyIndexes[gBattlerAttacker]);
gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[gBattlerAttacker].volatiles.nightmare = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR;
BattleScriptCall(BattleScript_MoveUsedWokeUp);
return MOVE_STEP_REMOVES_STATUS;
@ -1967,7 +1967,7 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void)
else
{
TryDeactivateSleepClause(GetBattlerSide(gBattlerAttacker), gBattlerPartyIndexes[gBattlerAttacker]);
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[gBattlerAttacker].volatiles.nightmare = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP;
BattleScriptCall(BattleScript_MoveUsedWokeUp);
return MOVE_STEP_REMOVES_STATUS;
@ -2067,7 +2067,7 @@ static enum MoveCanceller CancellerTruant(void)
static enum MoveCanceller CancellerFlinch(void)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FLINCHED)
if (gBattleMons[gBattlerAttacker].volatiles.flinched)
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
@ -2154,11 +2154,11 @@ static enum MoveCanceller CancellerConfused(void)
if (gBattleStruct->isAtkCancelerForCalledMove)
return MOVE_STEP_SUCCESS;
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION)
if (gBattleMons[gBattlerAttacker].volatiles.confusionTurns)
{
if (!(gStatuses4[gBattlerAttacker] & STATUS4_INFINITE_CONFUSION))
gBattleMons[gBattlerAttacker].status2 -= STATUS2_CONFUSION_TURN(1);
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION)
gBattleMons[gBattlerAttacker].volatiles.confusionTurns--;
if (gBattleMons[gBattlerAttacker].volatiles.confusionTurns)
{
// confusion dmg
if (RandomPercentage(RNG_CONFUSION, (GetGenConfig(GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50)))
@ -2212,9 +2212,9 @@ static enum MoveCanceller CancellerParalysed(void)
static enum MoveCanceller CancellerInfatuation(void)
{
if (!gBattleStruct->isAtkCancelerForCalledMove && gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION)
if (!gBattleStruct->isAtkCancelerForCalledMove && gBattleMons[gBattlerAttacker].volatiles.infatuation)
{
gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10);
gBattleScripting.battler = gBattleMons[gBattlerAttacker].volatiles.infatuation - 1;
if (!RandomPercentage(RNG_INFATUATION, 50))
{
BattleScriptCall(BattleScript_MoveUsedIsInLove);
@ -2234,17 +2234,16 @@ static enum MoveCanceller CancellerInfatuation(void)
static enum MoveCanceller CancellerBide(void)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE)
if (gBattleMons[gBattlerAttacker].volatiles.bideTurns)
{
gBattleMons[gBattlerAttacker].status2 -= STATUS2_BIDE_TURN(1);
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE)
if (--gBattleMons[gBattlerAttacker].volatiles.bideTurns)
{
gBattlescriptCurrInstr = BattleScript_BideStoringEnergy;
}
else
{
// This is removed in FRLG and Emerald for some reason
//gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_MULTIPLETURNS;
//gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE;
if (gBideDmg[gBattlerAttacker])
{
gCurrentMove = MOVE_BIDE;
@ -2978,7 +2977,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
case ABILITY_SOUNDPROOF:
if (IsSoundMove(move) && !(GetBattlerMoveTargetType(battlerAtk, move) & MOVE_TARGET_USER))
{
if (gBattleMons[battlerAtk].status2 & STATUS2_MULTIPLETURNS)
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
}
@ -2986,7 +2985,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
case ABILITY_BULLETPROOF:
if (IsBallisticMove(move))
{
if (gBattleMons[battlerAtk].status2 & STATUS2_MULTIPLETURNS)
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
}
@ -2996,7 +2995,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
case ABILITY_ARMOR_TAIL:
if (atkPriority > 0 && !IsBattlerAlly(battlerAtk, battlerDef))
{
if (gBattleMons[battlerAtk].status2 & STATUS2_MULTIPLETURNS)
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
}
@ -3041,7 +3040,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (gBattleMons[battlerAtk].status2 & STATUS2_MULTIPLETURNS)
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battlerAbility = partnerDef;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
@ -3549,8 +3548,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (gDisableStructs[battler].isFirstTurn == 2
&& !gDisableStructs[battler].overwrittenAbility
&& IsBattlerAlive(diagonalBattler)
&& !(gBattleMons[diagonalBattler].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& !gBattleMons[diagonalBattler].volatiles.substitute
&& !gBattleMons[diagonalBattler].volatiles.transformed
&& !gBattleMons[battler].volatiles.transformed
&& gBattleStruct->illusion[diagonalBattler].state != ILLUSION_ON
&& !(gStatuses3[diagonalBattler] & STATUS3_SEMI_INVULNERABLE_NO_COMMANDER))
{
@ -4124,7 +4124,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_ICE_FACE:
if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL | B_WEATHER_SNOW)
&& gBattleMons[battler].species == SPECIES_EISCUE_NOICE
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[battler].volatiles.transformed))
{
// TODO: Convert this to a proper FORM_CHANGE type.
gBattleMons[battler].species = SPECIES_EISCUE_ICE;
@ -4147,9 +4147,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
gBattleStruct->battlerState[battler].commandingDondozo = TRUE;
gBattleStruct->commanderActive[partner] = gBattleMons[battler].species;
gStatuses3[battler] |= STATUS3_COMMANDER;
if (gBattleMons[battler].status2 & STATUS2_CONFUSION
if (gBattleMons[battler].volatiles.confusionTurns > 0
&& !(gStatuses4[battler] & STATUS4_INFINITE_CONFUSION))
gBattleMons[battler].status2 -= STATUS2_CONFUSION_TURN(1);
gBattleMons[battler].volatiles.confusionTurns--;
BtlController_EmitSpriteInvisibility(battler, B_COMM_TO_CONTROLLER, TRUE);
MarkBattlerForControllerExec(battler);
BattleScriptPushCursorAndCallback(BattleScript_CommanderActivates);
@ -4245,7 +4245,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
gBattleMons[battler].status1 = 0;
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
gBattleScripting.battler = battler;
BattleScriptPushCursorAndCallback(BattleScript_ShedSkinActivates);
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
@ -4363,7 +4363,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_HUNGER_SWITCH:
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (!gBattleMons[battler].volatiles.transformed
&& GetActiveGimmick(battler) != GIMMICK_TERA
&& TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END))
{
@ -4728,13 +4728,13 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_CUTE_CHARM, 30) : RandomChance(RNG_CUTE_CHARM, 1, 3))
&& !(gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION)
&& !(gBattleMons[gBattlerAttacker].volatiles.infatuation)
&& AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget)
&& !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_OBLIVIOUS)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)
&& !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL))
{
gBattleMons[gBattlerAttacker].status2 |= STATUS2_INFATUATED_WITH(gBattlerTarget);
gBattleMons[gBattlerAttacker].volatiles.infatuation = INFATUATED_WITH(gBattlerTarget);
BattleScriptCall(BattleScript_CuteCharmActivates);
effect++;
}
@ -5034,7 +5034,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_OWN_TEMPO:
if (gBattleMons[battler].status2 & STATUS2_CONFUSION)
if (gBattleMons[battler].volatiles.confusionTurns > 0)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
effect = 2;
@ -5052,7 +5052,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
{
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
effect = 1;
}
@ -5074,7 +5074,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_OBLIVIOUS:
if (gBattleMons[battler].status2 & STATUS2_INFATUATION)
if (gBattleMons[battler].volatiles.infatuation)
effect = 3;
else if (gDisableStructs[battler].tauntTimer != 0)
effect = 4;
@ -5094,7 +5094,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
BattleScriptCall(BattleScript_AbilityCuredStatus);
break;
case 3: // get rid of infatuation
gBattleMons[battler].status2 &= ~STATUS2_INFATUATION;
gBattleMons[battler].volatiles.infatuation = 0;
BattleScriptCall(BattleScript_BattlerGotOverItsInfatuation);
break;
case 4: // get rid of taunt
@ -5215,7 +5215,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!gDisableStructs[battler].weatherAbilityDone
&& battlerWeatherAffected
&& gBattleMons[battler].species == SPECIES_EISCUE_NOICE
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[battler].volatiles.transformed))
{
// TODO: Convert this to a proper FORM_CHANGE type.
gBattleScripting.battler = battler;
@ -5229,7 +5229,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_PROTOSYNTHESIS:
if (!gDisableStructs[battler].weatherAbilityDone
&& (gBattleWeather & B_WEATHER_SUN) && HasWeatherEffect()
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& !gBattleMons[battler].volatiles.transformed
&& !gDisableStructs[battler].boosterEnergyActivated)
{
gDisableStructs[battler].weatherAbilityDone = TRUE;
@ -5258,7 +5258,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_QUARK_DRIVE:
if (!gDisableStructs[battler].terrainAbilityDone
&& gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& !gBattleMons[battler].volatiles.transformed
&& !gDisableStructs[battler].boosterEnergyActivated)
{
gDisableStructs[battler].terrainAbilityDone = TRUE;
@ -5358,7 +5358,7 @@ u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityS
if (abilityCantBeSuppressed)
{
// Edge case: pokemon under the effect of gastro acid transforms into a pokemon with Comatose (Todo: verify how other unsuppressable abilities behave)
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED
if (gBattleMons[battler].volatiles.transformed
&& gStatuses3[battler] & STATUS3_GASTRO_ACID
&& gBattleMons[battler].ability == ABILITY_COMATOSE)
return ABILITY_NONE;
@ -5457,7 +5457,9 @@ bool32 CanBattlerEscape(u32 battler) // no ability check
return TRUE;
else if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST))
return TRUE;
else if (gBattleMons[battler].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED))
else if (gBattleMons[battler].volatiles.escapePrevention)
return FALSE;
else if (gBattleMons[battler].volatiles.wrapped)
return FALSE;
else if (gStatuses3[battler] & STATUS3_ROOTED)
return FALSE;
@ -5793,7 +5795,7 @@ static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum Func
bool32 CanBeConfused(u32 battler)
{
if (gBattleMons[battler].status2 & STATUS2_CONFUSION
if (gBattleMons[battler].volatiles.confusionTurns > 0
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)
|| IsAbilityAndRecord(battler, GetBattlerAbility(battler),ABILITY_OWN_TEMPO))
return FALSE;
@ -6137,9 +6139,9 @@ static bool32 GetMentalHerbEffect(u32 battler)
bool32 ret = FALSE;
// Check infatuation
if (gBattleMons[battler].status2 & STATUS2_INFATUATION)
if (gBattleMons[battler].volatiles.infatuation)
{
gBattleMons[battler].status2 &= ~STATUS2_INFATUATION;
gBattleMons[battler].volatiles.infatuation = 0;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; // STRINGID_TARGETGOTOVERINFATUATION
StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn);
ret = TRUE;
@ -6163,9 +6165,9 @@ static bool32 GetMentalHerbEffect(u32 battler)
ret = TRUE;
}
// Check torment
if (gBattleMons[battler].status2 & STATUS2_TORMENT)
if (gBattleMons[battler].volatiles.torment == TRUE)
{
gBattleMons[battler].status2 &= ~STATUS2_TORMENT;
gBattleMons[battler].volatiles.torment = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT;
ret = TRUE;
}
@ -6209,7 +6211,7 @@ static u32 TryConsumeMirrorHerb(u32 battler, enum ItemCaseId caseID)
u32 TryBoosterEnergy(u32 battler, u32 ability, enum ItemCaseId caseID)
{
if (gDisableStructs[battler].boosterEnergyActivated || gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (gDisableStructs[battler].boosterEnergyActivated || gBattleMons[battler].volatiles.transformed)
return ITEM_NO_EFFECT;
if (((ability == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && HasWeatherEffect()))
@ -6368,14 +6370,14 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
if (gBattleMons[battler].status1 & STATUS1_SLEEP && !UnnerveOn(battler, gLastUsedItem))
{
gBattleMons[battler].status1 &= ~STATUS1_SLEEP;
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
BattleScriptCall(BattleScript_BerryCureSlpRet);
effect = ITEM_STATUS_CHANGE;
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
}
break;
case HOLD_EFFECT_CURE_CONFUSION:
if (gBattleMons[battler].status2 & STATUS2_CONFUSION && !UnnerveOn(battler, gLastUsedItem))
if (gBattleMons[battler].volatiles.confusionTurns > 0 && !UnnerveOn(battler, gLastUsedItem))
{
RemoveConfusionStatus(battler);
BattleScriptCall(BattleScript_BerryCureConfusionRet);
@ -6392,14 +6394,14 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
}
break;
case HOLD_EFFECT_CURE_STATUS:
if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].status2 & STATUS2_CONFUSION) && !UnnerveOn(battler, gLastUsedItem))
if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].volatiles.confusionTurns > 0) && !UnnerveOn(battler, gLastUsedItem))
{
if (gBattleMons[battler].status1 & STATUS1_PSN_ANY)
StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
{
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
}
@ -6413,7 +6415,7 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
if (gBattleMons[battler].status1 & STATUS1_FREEZE || gBattleMons[battler].status1 & STATUS1_FROSTBITE)
StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
if (gBattleMons[battler].status2 & STATUS2_CONFUSION)
if (gBattleMons[battler].volatiles.confusionTurns > 0)
StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
gBattleMons[battler].status1 = 0;
@ -6425,10 +6427,10 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
break;
case HOLD_EFFECT_CRITICAL_UP: // lansat berry
if (B_BERRIES_INSTANT >= GEN_4
&& !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY)
&& !(gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy)
&& HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem))
{
gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY;
gBattleMons[battler].volatiles.focusEnergy = TRUE;
gBattleScripting.battler = battler;
gPotentialItemEffectBattler = battler;
BattleScriptCall(BattleScript_BerryFocusEnergyRet);
@ -6453,7 +6455,7 @@ static inline bool32 TryCureStatus(u32 battler, enum ItemCaseId caseId)
u32 effect = ITEM_NO_EFFECT;
u32 string = 0;
if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].status2 & STATUS2_CONFUSION) && !UnnerveOn(battler, gLastUsedItem))
if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].volatiles.confusionTurns > 0) && !UnnerveOn(battler, gLastUsedItem))
{
if (gBattleMons[battler].status1 & STATUS1_PSN_ANY)
{
@ -6462,7 +6464,7 @@ static inline bool32 TryCureStatus(u32 battler, enum ItemCaseId caseId)
}
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
{
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
string++;
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
@ -6482,7 +6484,7 @@ static inline bool32 TryCureStatus(u32 battler, enum ItemCaseId caseId)
StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
string++;
}
if (gBattleMons[battler].status2 & STATUS2_CONFUSION)
if (gBattleMons[battler].volatiles.confusionTurns > 0)
{
StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
string++;
@ -6585,10 +6587,10 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
break;
case HOLD_EFFECT_CRITICAL_UP:
if (B_BERRIES_INSTANT >= GEN_4
&& !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY)
&& !(gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy)
&& HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem))
{
gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY;
gBattleMons[battler].volatiles.focusEnergy = TRUE;
gBattleScripting.battler = battler;
BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2);
effect = ITEM_EFFECT_OTHER;
@ -6652,7 +6654,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
&& !UnnerveOn(battler, gLastUsedItem))
{
gBattleMons[battler].status1 &= ~STATUS1_SLEEP;
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
BattleScriptExecute(BattleScript_BerryCureSlpEnd2);
effect = ITEM_STATUS_CHANGE;
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
@ -6808,10 +6810,10 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, caseID);
break;
case HOLD_EFFECT_CRITICAL_UP:
if (!(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY)
if (!(gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy)
&& HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem))
{
gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY;
gBattleMons[battler].volatiles.focusEnergy = TRUE;
gBattleScripting.battler = battler;
BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2);
effect = ITEM_EFFECT_OTHER;
@ -6862,14 +6864,14 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
if (gBattleMons[battler].status1 & STATUS1_SLEEP && !UnnerveOn(battler, gLastUsedItem))
{
gBattleMons[battler].status1 &= ~STATUS1_SLEEP;
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
gBattleMons[battler].volatiles.nightmare = FALSE;
BattleScriptExecute(BattleScript_BerryCureSlpEnd2);
effect = ITEM_STATUS_CHANGE;
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
}
break;
case HOLD_EFFECT_CURE_CONFUSION:
if (gBattleMons[battler].status2 & STATUS2_CONFUSION && !UnnerveOn(battler, gLastUsedItem))
if (gBattleMons[battler].volatiles.confusionTurns > 0 && !UnnerveOn(battler, gLastUsedItem))
{
RemoveConfusionStatus(battler);
BattleScriptExecute(BattleScript_BerryCureConfusionEnd2);
@ -7270,7 +7272,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
void ClearVariousBattlerFlags(u32 battler)
{
gDisableStructs[battler].furyCutterCounter = 0;
gBattleMons[battler].status2 &= ~STATUS2_DESTINY_BOND;
gBattleMons[battler].volatiles.destinyBond = FALSE;
gStatuses3[battler] &= ~STATUS3_GRUDGE;
gStatuses4[battler] &= ~ STATUS4_GLAIVE_RUSH;
}
@ -7459,7 +7461,7 @@ u8 GetAttackerObedienceForAction()
// is not obedient
enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove);
if (moveEffect == EFFECT_RAGE)
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RAGE;
gBattleMons[gBattlerAttacker].volatiles.rage = FALSE;
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (moveEffect == EFFECT_SNORE || moveEffect == EFFECT_SLEEP_TALK))
return DISOBEYS_WHILE_ASLEEP;
@ -7485,7 +7487,7 @@ u8 GetAttackerObedienceForAction()
// try putting asleep
int i;
for (i = 0; i < gBattlersCount; i++)
if (gBattleMons[i].status2 & STATUS2_UPROAR)
if (gBattleMons[i].volatiles.uproarTurns)
break;
if (i == gBattlersCount)
return DISOBEYS_FALL_ASLEEP;
@ -7895,7 +7897,7 @@ u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer)
u32 i;
for (i = 1; i < (5 - rolloutTimer); i++)
basePower *= 2;
if (gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL)
if (gBattleMons[battlerAtk].volatiles.defenseCurl)
basePower *= 2;
return basePower;
}
@ -8431,7 +8433,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
u8 defHighestStat = GetHighestStatId(battlerDef);
if (((ctx->weather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battlerDef].boosterEnergyActivated)
&& ((IsBattleMovePhysical(move) && defHighestStat == STAT_DEF) || (IsBattleMoveSpecial(move) && defHighestStat == STAT_SPDEF))
&& !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[battlerDef].volatiles.transformed))
modifier = uq4_12_multiply(modifier, UQ_4_12(0.7));
}
break;
@ -8440,7 +8442,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
u8 defHighestStat = GetHighestStatId(battlerDef);
if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battlerDef].boosterEnergyActivated)
&& ((IsBattleMovePhysical(move) && defHighestStat == STAT_DEF) || (IsBattleMoveSpecial(move) && defHighestStat == STAT_SPDEF))
&& !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED))
&& !(gBattleMons[battlerDef].volatiles.transformed))
modifier = uq4_12_multiply(modifier, UQ_4_12(0.7));
}
break;
@ -8683,7 +8685,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
modifier = uq4_12_multiply(modifier, UQ_4_12(1.5));
break;
case ABILITY_PROTOSYNTHESIS:
if (!(gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED))
if (!(gBattleMons[battlerAtk].volatiles.transformed))
{
u32 atkHighestStat = GetHighestStatId(battlerAtk);
if (((ctx->weather & B_WEATHER_SUN) && HasWeatherEffect()) || gDisableStructs[battlerAtk].boosterEnergyActivated)
@ -8694,7 +8696,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
}
break;
case ABILITY_QUARK_DRIVE:
if (!(gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED))
if (!(gBattleMons[battlerAtk].volatiles.transformed))
{
u32 atkHighestStat = GetHighestStatId(battlerAtk);
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battlerAtk].boosterEnergyActivated)
@ -8919,7 +8921,7 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
break;
case HOLD_EFFECT_METAL_POWDER:
if (gBattleMons[battlerDef].species == SPECIES_DITTO && usesDefStat && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED))
if (gBattleMons[battlerDef].species == SPECIES_DITTO && usesDefStat && !(gBattleMons[battlerDef].volatiles.transformed))
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
break;
case HOLD_EFFECT_EVIOLITE:
@ -9521,7 +9523,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m
if (ctx->updateFlags)
RecordItemEffectBattle(ctx->battlerDef, HOLD_EFFECT_RING_TARGET);
}
else if ((ctx->moveType == TYPE_FIGHTING || ctx->moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[ctx->battlerDef].status2 & STATUS2_FORESIGHT && mod == UQ_4_12(0.0))
else if ((ctx->moveType == TYPE_FIGHTING || ctx->moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[ctx->battlerDef].volatiles.foresight && mod == UQ_4_12(0.0))
{
mod = UQ_4_12(1.0);
}
@ -9964,7 +9966,7 @@ void ActivateUltraBurst(u32 battler)
bool32 IsBattlerMegaEvolved(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (gBattleMons[battler].volatiles.transformed)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isMegaEvolution);
}
@ -9972,7 +9974,7 @@ bool32 IsBattlerMegaEvolved(u32 battler)
bool32 IsBattlerPrimalReverted(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Primal Revesion.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (gBattleMons[battler].volatiles.transformed)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isPrimalReversion);
}
@ -9980,7 +9982,7 @@ bool32 IsBattlerPrimalReverted(u32 battler)
bool32 IsBattlerUltraBursted(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Ultra Burst.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (gBattleMons[battler].volatiles.transformed)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isUltraBurst);
}
@ -9988,7 +9990,7 @@ bool32 IsBattlerUltraBursted(u32 battler)
bool32 IsBattlerInTeraForm(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as a true Tera Form.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
if (gBattleMons[battler].volatiles.transformed)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isTeraForm);
}
@ -10106,7 +10108,7 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method)
bool32 CanBattlerFormChange(u32 battler, enum FormChanges method)
{
// Can't change form if transformed.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED
if (gBattleMons[battler].volatiles.transformed
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
return FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle.
@ -10856,7 +10858,7 @@ void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing)
void RemoveConfusionStatus(u32 battler)
{
gBattleMons[battler].status2 &= ~STATUS2_CONFUSION;
gBattleMons[battler].volatiles.confusionTurns = 0;
gStatuses4[battler] &= ~STATUS4_INFINITE_CONFUSION;
}
@ -11415,7 +11417,7 @@ bool32 TrySwitchInEjectPack(enum ItemCaseId caseID)
return FALSE;
}
#define UNPACK_VOLATILE_GETTERS(_enum, _fieldName, _typeBitSize, ...) case _enum: return gBattleMons[battler].volatiles._fieldName;
#define UNPACK_VOLATILE_GETTERS(_enum, _fieldName, _typeMaxValue, ...) case _enum: return gBattleMons[battler].volatiles._fieldName;
// Gets the value of a volatile status flag for a certain battler
// Primarily used for the debug menu and scripts. Outside of it explicit references are preferred
@ -11433,7 +11435,7 @@ u32 GetMonVolatile(u32 battler, enum Volatile _volatile)
}
}
#define UNPACK_VOLATILE_SETTERS(_enum, _fieldName, _typeBitSize, ...) case _enum: gBattleMons[battler].volatiles._fieldName = min(GET_VOLATILE_MAXIMUM(_typeBitSize), newValue); break;
#define UNPACK_VOLATILE_SETTERS(_enum, _fieldName, _typeMaxValue, ...) case _enum: gBattleMons[battler].volatiles._fieldName = min(GET_VOLATILE_MAXIMUM(_typeMaxValue), newValue); break;
// Sets the value of a volatile status flag for a certain battler
// Primarily used for the debug menu and scripts. Outside of it explicit references are preferred
@ -11452,6 +11454,30 @@ void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue)
}
}
bool32 ItemHealMonVolatile(u32 battler, u16 itemId)
{
bool32 statusChanged = FALSE;
const u8 *effect = GetItemEffect(itemId);
if (effect[3] & ITEM3_STATUS_ALL)
{
statusChanged = (gBattleMons[battler].volatiles.infatuation || gBattleMons[battler].volatiles.confusionTurns > 0);
gBattleMons[battler].volatiles.infatuation = 0;
gBattleMons[battler].volatiles.confusionTurns = 0;
}
else if (effect[0] & ITEM0_INFATUATION)
{
statusChanged = !!gBattleMons[battler].volatiles.infatuation;
gBattleMons[battler].volatiles.infatuation = 0;
}
else if (effect[3] & ITEM3_CONFUSION)
{
statusChanged = gBattleMons[battler].volatiles.confusionTurns > 0;
gBattleMons[battler].volatiles.confusionTurns = 0;
}
return statusChanged;
}
// Hazards are added to a queue and applied based in order (FIFO)
void PushHazardTypeToQueue(u32 side, enum Hazards hazardType)
{
@ -11634,7 +11660,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
if (defAbility == ABILITY_UNAWARE)
accStage = DEFAULT_STAT_STAGE;
if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
if (gBattleMons[battlerDef].volatiles.foresight || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
buff = accStage;
else
buff = accStage + DEFAULT_STAT_STAGE - evasionStage;

View File

@ -133,7 +133,7 @@ u32 BattlePalace_TryEscapeStatus(u8 battler)
{
// Wake up from Uproar
gBattleMons[battler].status1 &= ~(STATUS1_SLEEP);
gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE);
gBattleMons[battler].volatiles.nightmare = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR;
BattleScriptCall(BattleScript_MoveUsedWokeUp);
effect = 2;
@ -162,7 +162,7 @@ u32 BattlePalace_TryEscapeStatus(u8 battler)
else
{
// Wake up
gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE);
gBattleMons[battler].volatiles.nightmare = FALSE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP;
BattleScriptCall(BattleScript_MoveUsedWokeUp);
effect = 2;

View File

@ -486,9 +486,9 @@ void SetZEffect(void)
break;
}
case Z_EFFECT_BOOST_CRITS:
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY_ANY))
if (!(gBattleMons[gBattlerAttacker].volatiles.dragonCheer || gBattleMons[gBattlerAttacker].volatiles.focusEnergy))
{
gBattleMons[gBattlerAttacker].status2 |= STATUS2_FOCUS_ENERGY;
gBattleMons[gBattlerAttacker].volatiles.focusEnergy = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_BOOST_CRITS;
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;

View File

@ -3108,7 +3108,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_ACC_UP_1 },
.argument = { .status = STATUS2_FOCUS_ENERGY },
.argument = { .status = VOLATILE_FOCUS_ENERGY },
.ignoresProtect = TRUE,
.mirrorMoveBanned = TRUE,
.snatchAffected = TRUE,

View File

@ -958,17 +958,18 @@ u32 GetItemStatus1Mask(u16 itemId)
return 0;
}
u32 GetItemStatus2Mask(u16 itemId)
bool32 ItemHasVolatileFlag(u16 itemId, enum Volatile _volatile)
{
const u8 *effect = GetItemEffect(itemId);
if (effect[3] & ITEM3_STATUS_ALL)
return STATUS2_INFATUATION | STATUS2_CONFUSION;
else if (effect[0] & ITEM0_INFATUATION)
return STATUS2_INFATUATION;
else if (effect[3] & ITEM3_CONFUSION)
return STATUS2_CONFUSION;
else
return 0;
switch (_volatile)
{
case VOLATILE_CONFUSION:
return (effect[3] & ITEM3_STATUS_ALL) || (effect[3] & ITEM3_CONFUSION);
case VOLATILE_INFATUATION:
return (effect[3] & ITEM3_STATUS_ALL) || (effect[0] & ITEM0_INFATUATION);
default:
return FALSE;
}
}
u32 GetItemSellPrice(u32 itemId)

View File

@ -1191,12 +1191,25 @@ void ItemUseInBattle_PartyMenuChooseMove(u8 taskId)
ItemUseInBattle_ShowPartyMenu(taskId);
}
static bool32 SelectedMonHasStatus2(u16 itemId)
static bool32 IteamHealsMonVolatile(u32 battler, u16 itemId)
{
const u8 *effect = GetItemEffect(itemId);
if (effect[3] & ITEM3_STATUS_ALL)
return (gBattleMons[battler].volatiles.infatuation || gBattleMons[battler].volatiles.confusionTurns > 0);
else if (effect[0] & ITEM0_INFATUATION)
return gBattleMons[battler].volatiles.infatuation;
else if (effect[3] & ITEM3_CONFUSION)
return gBattleMons[battler].volatiles.confusionTurns > 0;
return FALSE;
}
static bool32 SelectedMonHasVolatile(u16 itemId)
{
if (gPartyMenu.slotId == 0)
return gBattleMons[0].status2 & GetItemStatus2Mask(itemId);
return IteamHealsMonVolatile(0, itemId);
else if (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI) && gPartyMenu.slotId == 1)
return gBattleMons[2].status2 & GetItemStatus2Mask(itemId);
return IteamHealsMonVolatile(2, itemId);
return FALSE;
}
@ -1224,7 +1237,7 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
cannotUse = TRUE;
break;
case EFFECT_ITEM_SET_FOCUS_ENERGY:
if (gBattleMons[gBattlerInMenuId].status2 & STATUS2_FOCUS_ENERGY_ANY)
if (gBattleMons[gBattlerInMenuId].volatiles.dragonCheer || gBattleMons[gBattlerInMenuId].volatiles.focusEnergy)
cannotUse = TRUE;
break;
case EFFECT_ITEM_SET_MIST:
@ -1272,13 +1285,13 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
break;
case EFFECT_ITEM_CURE_STATUS:
if (!((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId))
|| SelectedMonHasStatus2(itemId)))
|| SelectedMonHasVolatile(itemId)))
cannotUse = TRUE;
break;
case EFFECT_ITEM_HEAL_AND_CURE_STATUS:
if ((hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP))
&& !((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId))
|| SelectedMonHasStatus2(itemId)))
|| SelectedMonHasVolatile(itemId)))
cannotUse = TRUE;
break;
case EFFECT_ITEM_REVIVE:

View File

@ -3693,7 +3693,7 @@ void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst)
for (i = 0; i < NUM_BATTLE_STATS; i++)
dst->statStages[i] = DEFAULT_STAT_STAGE;
dst->status2 = 0;
memset(&dst->volatiles, 0, sizeof(struct Volatiles));
}
void CopyPartyMonToBattleData(u32 battler, u32 partyIndex)

View File

@ -756,7 +756,7 @@ void RecordedBattle_CheckMovesetChanges(u8 mode)
gDisableStructs[battler].mimickedMoves |= mimickedMoveSlots[j] << j;
}
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
if (!(gBattleMons[battler].volatiles.transformed))
{
struct Pokemon *mon = GetBattlerMon(battler);
for (j = 0; j < MAX_MON_MOVES; j++)

View File

@ -36,7 +36,7 @@ SINGLE_BATTLE_TEST("Shield Dust blocks secondary effects")
MESSAGE("The opposing Vivillon was prevented from healing!");
}
} THEN { // Can't find good way to test trapping
EXPECT(!(opponent->status2 & STATUS2_ESCAPE_PREVENTION));
EXPECT(!opponent->volatiles.escapePrevention);
}
}
@ -78,8 +78,8 @@ SINGLE_BATTLE_TEST("Shield Dust does not block primary effects")
}
} THEN { // Can't find good way to test trapping
if (move == MOVE_JAW_LOCK) {
EXPECT(opponent->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(player->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(opponent->volatiles.escapePrevention);
EXPECT(player->volatiles.escapePrevention);
}
}
}

View File

@ -1221,7 +1221,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Terror traps both opponents")
MESSAGE("The opposing Wobbuffet can no longer escape!");
MESSAGE("The opposing Wobbuffet can no longer escape!");
} THEN { // Can't find good way to test trapping
EXPECT(opponentLeft->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(opponentLeft->volatiles.escapePrevention);
}
}
@ -1238,7 +1238,7 @@ SINGLE_BATTLE_TEST("Dynamax: Baton Pass passes G-Max Terror's escape prevention
ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_TERROR, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, opponent);
} THEN {
EXPECT(opponent->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(opponent->volatiles.escapePrevention);
}
}

View File

@ -217,7 +217,7 @@ SINGLE_BATTLE_TEST("Berserk Gene causes infinite confusion") // check if bit is
}
}
SINGLE_BATTLE_TEST("Berserk Gene causes confusion timer to not tick down", u32 status2)
SINGLE_BATTLE_TEST("Berserk Gene causes confusion timer to not tick down", u32 confusionTurns)
{
u32 turns;
PARAMETRIZE { turns = 1; }
@ -231,9 +231,9 @@ SINGLE_BATTLE_TEST("Berserk Gene causes confusion timer to not tick down", u32 s
TURN {}
}
} THEN {
results[i].status2 = player->status2;
results[i].confusionTurns = player->volatiles.confusionTurns;
} FINALLY {
EXPECT_EQ(results[0].status2, results[1].status2);
EXPECT_EQ(results[0].confusionTurns, results[1].confusionTurns);
}
}

View File

@ -41,7 +41,7 @@ SINGLE_BATTLE_TEST("Covert Cloak blocks secondary effects")
MESSAGE("The opposing Wobbuffet was prevented from healing!");
}
} THEN { // Can't find good way to test trapping
EXPECT(!(opponent->status2 & STATUS2_ESCAPE_PREVENTION));
EXPECT(!opponent->volatiles.escapePrevention);
}
}
@ -82,8 +82,8 @@ SINGLE_BATTLE_TEST("Covert Cloak does not block primary effects")
}
} THEN { // Can't find good way to test trapping
if (move == MOVE_JAW_LOCK) {
EXPECT(opponent->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(player->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(opponent->volatiles.escapePrevention);
EXPECT(player->volatiles.escapePrevention);
}
}
}

View File

@ -389,6 +389,6 @@ SINGLE_BATTLE_TEST("Full Heal, Heal Powder and Local Specialties heal a battler
} SCENE {
MESSAGE("Wobbuffet had its status healed!");
} THEN {
EXPECT_EQ(player->status2, STATUS1_NONE); // because we dont have STATUS2_NONE
EXPECT(player->volatiles.confusionTurns == 0);
}
}

View File

@ -19,7 +19,7 @@ SINGLE_BATTLE_TEST("Attract causes the target to become infatuated with the user
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Nidoking fell in love!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
EXPECT(opponent->volatiles.infatuation);
}
}
@ -35,7 +35,7 @@ SINGLE_BATTLE_TEST("Attract ignores type immunity")
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Misdreavus fell in love!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
EXPECT(opponent->volatiles.infatuation);
}
}
@ -51,7 +51,7 @@ SINGLE_BATTLE_TEST("Attract bypasses Substitute")
ANIMATION(ANIM_TYPE_MOVE, MOVE_ATTRACT, player);
MESSAGE("The opposing Nidoking fell in love!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
EXPECT(opponent->volatiles.infatuation);
}
}
@ -69,7 +69,7 @@ SINGLE_BATTLE_TEST("Attract fails if the target is already infatuated")
MESSAGE("Nidoqueen used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(opponent->status2 & STATUS2_INFATUATION);
EXPECT(opponent->volatiles.infatuation);
}
}
@ -84,7 +84,7 @@ SINGLE_BATTLE_TEST("Attract fails when used on a Pokémon of the same gender")
MESSAGE("Nidoqueen used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
EXPECT(!(opponent->volatiles.infatuation));
}
}
@ -100,6 +100,6 @@ SINGLE_BATTLE_TEST("Attract fails when used on a genderless Pokémon")
MESSAGE("Nidoqueen used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
EXPECT(!(opponent->volatiles.infatuation));
}
}

View File

@ -37,7 +37,7 @@ TO_DO_BATTLE_TEST("Baton Pass doesn't pass ability changes");
//
// Move these to the corresponding effect files.
//
TO_DO_BATTLE_TEST("Baton Pass passes confusion status"); // test/battle/status2/confusion.c
TO_DO_BATTLE_TEST("Baton Pass passes confusion status"); // test/battle/volatiles/confusion.c
TO_DO_BATTLE_TEST("Baton Pass passes Fairy lock's escape prevention effect"); // test/battle/move_effect/fairy_lock.c
TO_DO_BATTLE_TEST("Baton Pass passes Focus Energy's effect"); // test/battle/move_effect/focus_energy.c

View File

@ -92,7 +92,7 @@ SINGLE_BATTLE_TEST("Attract fails when used by a genderless Pokémon")
MESSAGE("Starmie used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
EXPECT(!(opponent->volatiles.infatuation));
}
}
@ -107,6 +107,6 @@ SINGLE_BATTLE_TEST("Attract fails if both the user and the target are genderless
MESSAGE("Starmie used Attract!");
MESSAGE("But it failed!");
} THEN {
EXPECT(!(opponent->status2 & STATUS2_INFATUATION));
EXPECT(!(opponent->volatiles.infatuation));
}
}

View File

@ -114,6 +114,6 @@ SINGLE_BATTLE_TEST("Petal Dance does not lock mons that copy the move with Dance
ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_DANCE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_DANCE, opponent);
// How do you actually test locking?
EXPECT(!(opponent->status2 & STATUS2_MULTIPLETURNS));
EXPECT(!(opponent->volatiles.multipleTurns));
}
}

View File

@ -17,7 +17,7 @@ SINGLE_BATTLE_TEST("Jaw Lock traps both opponents")
ANIMATION(ANIM_TYPE_MOVE, MOVE_JAW_LOCK, player);
MESSAGE("Neither Pokémon can run away!");
} THEN { // Can't find good way to test trapping
EXPECT(opponent->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(player->status2 & STATUS2_ESCAPE_PREVENTION);
EXPECT(opponent->volatiles.escapePrevention);
EXPECT(player->volatiles.escapePrevention);
}
}