Volatile status refactoring (#6983)

This commit is contained in:
Nephrite 2025-06-18 08:22:41 +03:00 committed by GitHub
parent a765b8b45d
commit 06cd3af3ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 267 additions and 131 deletions

View File

@ -116,10 +116,10 @@
.byte 0x17
.endm
.macro clearstatus2 battler:req, status2:req
.macro clearvolatile battler:req, volatile:req
.byte 0x18
.byte \battler
.4byte \status2
.byte \volatile
.endm
.macro tryfaintmon battler:req
@ -153,10 +153,10 @@
.4byte \jumpInstr
.endm
.macro jumpifstatus2 battler:req, flags:req, jumpInstr:req
.macro jumpifvolatile battler:req, volatile:req, jumpInstr:req
.byte 0x1d
.byte \battler
.4byte \flags
.byte \volatile
.4byte \jumpInstr
.endm

View File

@ -152,7 +152,7 @@ BattleScript_EffectShedTail::
attackstring
ppreduce
waitstate
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
jumpifvolatile BS_ATTACKER, VOLATILE_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_ButItFailed
setsubstitute
@ -695,7 +695,7 @@ BattleScript_BeakBlastBurn::
BattleScript_EffectSkyDrop::
attackcanceler
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2
jumpifvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS, BattleScript_SkyDropTurn2
ppreduce
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
@ -722,8 +722,8 @@ BattleScript_SkyDropFlyingType:
printstring STRINGID_ITDOESNTAFFECT
waitmessage B_WAIT_TIME_LONG
makevisible BS_ATTACKER
jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_SkyDropFlyingAlreadyConfused
jumpifstatus2 BS_TARGET, STATUS2_LOCK_CONFUSE, BattleScript_SkyDropFlyingConfuseLock
jumpifvolatile BS_TARGET, VOLATILE_CONFUSION, BattleScript_SkyDropFlyingAlreadyConfused
jumpifvolatile BS_TARGET, VOLATILE_LOCK_CONFUSE, BattleScript_SkyDropFlyingConfuseLock
goto BattleScript_MoveEnd
BattleScript_SkyDropChangedTarget:
pause B_WAIT_TIME_SHORT
@ -736,8 +736,8 @@ BattleScript_SkyDropChangedTarget:
BattleScript_SkyDropFlyingConfuseLock:
seteffectprimary MOVE_EFFECT_CONFUSION
BattleScript_SkyDropFlyingAlreadyConfused:
clearstatus2 BS_TARGET, STATUS2_LOCK_CONFUSE
jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_MoveEnd
clearvolatile BS_TARGET, VOLATILE_LOCK_CONFUSE
jumpifvolatile BS_TARGET, VOLATILE_CONFUSION, BattleScript_MoveEnd
setbyte BS_ATTACKER, BS_TARGET
goto BattleScript_ThrashConfuses
@ -926,7 +926,7 @@ BattleScript_EffectNoRetreat::
attackanimation
waitanimation
call BattleScript_AllStatsUp
jumpifstatus2 BS_TARGET, STATUS2_ESCAPE_PREVENTION, BattleScript_MoveEnd
jumpifvolatile BS_TARGET, VOLATILE_ESCAPE_PREVENTION, BattleScript_MoveEnd
seteffectprimary MOVE_EFFECT_PREVENT_ESCAPE | MOVE_EFFECT_AFFECTS_USER
printstring STRINGID_CANTESCAPEDUETOUSEDMOVE
waitmessage B_WAIT_TIME_LONG
@ -1352,7 +1352,7 @@ BattleScript_EffectPowder::
accuracycheck BattleScript_PrintMoveMissed, NO_ACC_CALC_CHECK_LOCK_ON
attackstring
ppreduce
jumpifstatus2 BS_TARGET, STATUS2_POWDER, BattleScript_ButItFailed
jumpifvolatile BS_TARGET, VOLATILE_POWDER, BattleScript_ButItFailed
setpowder BS_TARGET
attackanimation
waitanimation
@ -1470,7 +1470,7 @@ BattleScript_EffectGearUpEnd:
BattleScript_EffectAcupressure::
attackcanceler
jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectAcupressureTry
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_PrintMoveMissed
jumpifvolatile BS_TARGET, VOLATILE_SUBSTITUTE, BattleScript_PrintMoveMissed
BattleScript_EffectAcupressureTry:
attackstring
ppreduce
@ -3176,7 +3176,7 @@ BattleScript_StatDownEnd::
BattleScript_MirrorArmorReflect::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_MirrorArmorDoesntAffect
jumpifvolatile BS_ATTACKER, VOLATILE_SUBSTITUTE, BattleScript_MirrorArmorDoesntAffect
BattleScript_MirrorArmorReflectStatLoss:
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_MIRROR_ARMOR | STAT_CHANGE_NOT_PROTECT_AFFECTED | STAT_CHANGE_ALLOW_PTR, BattleScript_MirrorArmorReflectEnd
jumpifbyte CMP_LESS_THAN, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_MirrorArmorReflectAnim
@ -3402,7 +3402,8 @@ BattleScript_EffectFocusEnergy::
attackcanceler
attackstring
ppreduce
jumpifstatus2 BS_ATTACKER, STATUS2_FOCUS_ENERGY_ANY, BattleScript_ButItFailed
jumpifvolatile BS_ATTACKER, VOLATILE_DRAGON_CHEER, BattleScript_ButItFailed
jumpifvolatile BS_ATTACKER, VOLATILE_FOCUS_ENERGY, BattleScript_ButItFailed
setfocusenergy BS_TARGET
attackanimation
waitanimation
@ -3416,7 +3417,7 @@ BattleScript_EffectConfuse::
ppreduce
jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_OwnTempoPrevents
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_AlreadyConfused
jumpifvolatile BS_TARGET, VOLATILE_CONFUSION, BattleScript_AlreadyConfused
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected
@ -3544,7 +3545,7 @@ BattleScript_PowerHerbActivation:
return
BattleScript_EffectTwoTurnsAttack::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn
jumpifvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn
tryfiretwoturnmovewithoutcharging BS_ATTACKER, BattleScript_EffectHit @ e.g. Solar Beam
call BattleScript_FirstChargingTurn
@ -3553,7 +3554,7 @@ BattleScript_EffectTwoTurnsAttack::
goto BattleScript_MoveEnd
BattleScript_EffectGeomancy::
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_GeomancySecondTurn
jumpifvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS, BattleScript_GeomancySecondTurn
jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_GeomancySecondTurn
call BattleScript_FirstChargingTurn
jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd
@ -3561,7 +3562,7 @@ BattleScript_EffectGeomancy::
BattleScript_GeomancySecondTurn:
attackcanceler
setbyte sB_ANIM_TURN, 1
clearstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS
clearvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS
orword gHitMarker, HITMARKER_NO_PPDEDUCT
attackstring
jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_SPATK, MAX_STAT_STAGE, BattleScript_GeomancyDoMoveAnim
@ -3632,7 +3633,7 @@ BattleScript_TwoTurnMovesSecondTurn::
BattleScript_TwoTurnMovesSecondTurnRet:
setbyte sB_ANIM_TURN, 1
setbyte sB_ANIM_TARGETS_HIT, 0
clearstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS
clearvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS
clearsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP
return
@ -3641,7 +3642,7 @@ BattleScript_EffectSubstitute::
ppreduce
attackstring
waitstate
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
jumpifvolatile BS_ATTACKER, VOLATILE_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
setsubstitute
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SUBSTITUTE_FAILED, BattleScript_SubstituteString
orword gHitMarker, HITMARKER_PASSIVE_DAMAGE
@ -3672,7 +3673,7 @@ BattleScript_EffectRage::
seteffectprimary MOVE_EFFECT_RAGE
goto BattleScript_HitFromAtkString
BattleScript_RageMiss::
clearstatus2 BS_ATTACKER, STATUS2_RAGE
clearvolatile BS_ATTACKER, VOLATILE_RAGE
goto BattleScript_PrintMoveMissed
BattleScript_EffectMimic::
@ -3939,7 +3940,7 @@ BattleScript_EffectMeanLook::
attackstring
ppreduce
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
jumpifstatus2 BS_TARGET, STATUS2_ESCAPE_PREVENTION, BattleScript_ButItFailed
jumpifvolatile BS_TARGET, VOLATILE_ESCAPE_PREVENTION, BattleScript_ButItFailed
jumpifsubstituteblocks BattleScript_ButItFailed
.if B_GHOSTS_ESCAPE >= GEN_6
jumpiftype BS_TARGET, TYPE_GHOST, BattleScript_ButItFailed
@ -3956,7 +3957,7 @@ BattleScript_EffectNightmare::
attackstring
ppreduce
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus2 BS_TARGET, STATUS2_NIGHTMARE, BattleScript_ButItFailed
jumpifvolatile BS_TARGET, VOLATILE_NIGHTMARE, BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_SLEEP, BattleScript_NightmareWorked
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_NightmareWorked
goto BattleScript_ButItFailed
@ -4068,7 +4069,7 @@ BattleScript_EffectForesight::
attackstring
ppreduce
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
jumpifstatus2 BS_TARGET, STATUS2_FORESIGHT, BattleScript_ButItFailed
jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed
setforesight
BattleScript_IdentifiedFoe:
attackanimation
@ -4119,7 +4120,7 @@ BattleScript_EffectSandstorm::
BattleScript_EffectRollout::
attackcanceler
attackstring
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_RolloutCheckAccuracy
jumpifvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS, BattleScript_RolloutCheckAccuracy
ppreduce
BattleScript_RolloutCheckAccuracy::
accuracycheck BattleScript_RolloutHit, ACC_CURR_MOVE
@ -4545,7 +4546,7 @@ BattleScript_EffectUproar::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_UproarHit
jumpifvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS, BattleScript_UproarHit
ppreduce
BattleScript_UproarHit::
goto BattleScript_HitFromCritCalc
@ -5818,7 +5819,7 @@ BattleScript_BideStoringEnergy::
BattleScript_BideAttack::
attackcanceler
clearstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS
clearvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS
printstring STRINGID_PKMNUNLEASHEDENERGY
waitmessage B_WAIT_TIME_LONG
accuracycheck BattleScript_MoveMissed, ACC_CURR_MOVE
@ -5841,7 +5842,7 @@ BattleScript_BideAttack::
BattleScript_BideNoEnergyToAttack::
attackcanceler
clearstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS
clearvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS
printstring STRINGID_PKMNUNLEASHEDENERGY
waitmessage B_WAIT_TIME_LONG
goto BattleScript_ButItFailed
@ -7669,7 +7670,7 @@ BattleScript_IntimidateLoop:
jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_IntimidateLoopIncrement
jumpiftargetally BattleScript_IntimidateLoopIncrement
jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement
jumpifvolatile BS_TARGET, VOLATILE_SUBSTITUTE, BattleScript_IntimidateLoopIncrement
jumpifintimidateabilityprevented
BattleScript_IntimidateEffect:
copybyte sBATTLER, gBattlerAttacker
@ -7740,7 +7741,7 @@ BattleScript_SupersweetSyrupLoop:
jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_SupersweetSyrupLoopIncrement
jumpiftargetally BattleScript_SupersweetSyrupLoopIncrement
jumpifabsent BS_TARGET, BattleScript_SupersweetSyrupLoopIncrement
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_SupersweetSyrupLoopIncrement
jumpifvolatile BS_TARGET, VOLATILE_SUBSTITUTE, BattleScript_SupersweetSyrupLoopIncrement
BattleScript_SupersweetSyrupEffect:
copybyte sBATTLER, gBattlerAttacker
setstatchanger STAT_EVASION, 1, TRUE

View File

@ -141,7 +141,8 @@ BattleScript_ItemSetMist::
BattleScript_ItemSetFocusEnergy::
call BattleScript_UseItemMessage
jumpifstatus2 BS_ATTACKER, STATUS2_FOCUS_ENERGY_ANY, BattleScript_ButItFailed
jumpifvolatile BS_ATTACKER, VOLATILE_DRAGON_CHEER, BattleScript_ButItFailed
jumpifvolatile BS_ATTACKER, VOLATILE_FOCUS_ENERGY, BattleScript_ButItFailed
setfocusenergy BS_ATTACKER
playmoveanimation BS_ATTACKER, MOVE_FOCUS_ENERGY
waitanimation

View File

@ -393,5 +393,7 @@ bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 HadMoreThanHalfHpNowDoesnt(u32 battler);
void UpdateStallMons(void);
bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId);
u32 GetMonVolatile(u32 battler, enum Volatile volatile);
void SetMonVolatile(u32 battler, enum Volatile volatile, u32 newValue);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -134,8 +134,54 @@ enum BattlerId
#define STATUS1_REFRESH (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE)
enum VolatileFlags
{
V_BATON_PASSABLE = (1 << 0),
};
// Volatile status ailments
// These are removed after exiting the battle or switching out
// 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)
*/
#define VOLATILE_DEFINITIONS(F) \
F(VOLATILE_CONFUSION, confusionTurns, (u32, 3), V_BATON_PASSABLE) \
F(VOLATILE_FLINCHED, flinched, (u32, 1)) \
F(VOLATILE_UPROAR, uproarTurns, (u32, 3)) \
F(VOLATILE_TORMENT, torment, (u32, 1)) \
F(VOLATILE_BIDE, bideTurns, (u32, 2)) \
F(VOLATILE_LOCK_CONFUSE, lockConfusionTurns, (u32, 2)) \
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_DEFENSE_CURL, defenseCurl, (u32, 1)) \
F(VOLATILE_TRANSFORMED, transformed, (u32, 1)) \
F(VOLATILE_RECHARGE, recharge, (u32, 1)) \
F(VOLATILE_RAGE, rage, (u32, 1)) \
F(VOLATILE_SUBSTITUTE, substitute, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 1)) \
F(VOLATILE_ESCAPE_PREVENTION, escapePrevention, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_NIGHTMARE, nightmare, (u32, 1)) \
F(VOLATILE_CURSED, cursed, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_FORESIGHT, foresight, (u32, 1)) \
F(VOLATILE_DRAGON_CHEER, dragonCheer, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_FOCUS_ENERGY, focusEnergy, (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)))
#define UNPACK_VOLATILE_ENUMS(_enum, ...) _enum,
enum Volatile
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_ENUMS)
/* Expands to VOLATILE_CONFUSION, VOLATILE_FLINCHED, etc. */
};
// Old flags
#define STATUS2_CONFUSION (1 << 0 | 1 << 1 | 1 << 2)
#define STATUS2_CONFUSION_TURN(num) ((num) << 0)
#define STATUS2_FLINCHED (1 << 3)
@ -149,6 +195,7 @@ enum BattlerId
#define STATUS2_MULTIPLETURNS (1 << 12)
#define STATUS2_WRAPPED (1 << 13)
#define STATUS2_POWDER (1 << 14)
//#define STATUS2_UNUSED (1 << 15)
#define STATUS2_INFATUATION (1 << 16 | 1 << 17 | 1 << 18 | 1 << 19) // 4 bits, one for every battler
#define STATUS2_INFATUATED_WITH(battler) (1u << (battler + 16))
#define STATUS2_DEFENSE_CURL (1 << 20)

View File

@ -48,9 +48,26 @@
* Useful for passing arguments which may contain commas into a macro. */
#define UNPACK(...) __VA_ARGS__
/* Updated version that can extract arguments from brackets as well.
* Examples:
*
* UNPACK_B(a, b) => a, b
* UNPACK_B((a, b)) => a, b
* UNPACK_B((a)) => a
*
* The simple UNPACK is used for extracting non-bracketed arguments.
* */
#define UNPACK_EXTRA(...) IF_YOU_SEE_ME_SOMETHING_IS_WRONG, __VA_ARGS__
#define UNPACK_B(a) INVOKE(UNPACK_B_, a, UNPACK_EXTRA a)
#define UNPACK_B_(a, b, ...) __VA_OPT__(UNPACK)a
/* Expands to 'macro(...args, ...)'. */
#define INVOKE_WITH(macro, args, ...) INVOKE_WITH_(macro, UNPACK args __VA_OPT__(, __VA_ARGS__))
#define INVOKE_WITH_(macro, ...) macro(__VA_ARGS__)
#define INVOKE_WITH(macro, args, ...) INVOKE(macro, UNPACK args __VA_OPT__(, __VA_ARGS__))
#define INVOKE(macro, ...) macro(__VA_ARGS__)
/* Same as INVOKE_WITH but uses UNPACK_B to unpack arguments and only applies macro to args if there are any. */
#define INVOKE_WITH_B(macro, args, ...) INVOKE_B(macro, UNPACK_B(args) __VA_OPT__(, __VA_ARGS__))
#define INVOKE_B(macro, ...) __VA_OPT__(macro(__VA_ARGS__))
/* Recursive macros.
* Based on https://www.scs.stanford.edu/~dm/blog/va-opt.html
@ -143,4 +160,15 @@ Input must be of the form (upper << lower) where upper can be up to 3, lower up
/* Will read a compressed bit stored by COMPRESS_BIT into a single byte */
#define UNCOMPRESS_BITS(compressed) ((compressed >> 5) << (compressed & 0x1F))
/* Bit maxima */
#define MAX_u8 0xFF
#define MAX_u16 0xFFFF
#define MAX_u32 0xFFFFFFFF
/* Finds the maximum value of the given number of bits (up to 32 - obviously)*/
#define MAX_BITS(_bit) (MAX_u32 >> (32 - _bit))
/* Finds the required digits to display the number (maximum 4) */
#define MAX_DIGITS(_num) 1 + !!(_num / 10) + !!(_num / 100) + !!(_num / 1000)
#endif

View File

@ -309,6 +309,38 @@ 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)
struct Volatiles
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_STRUCT)
// Expands to:
// u32 confusionTurns:3;
// u32 flinched:1;
// u32 uproarTurns:3;
// u32 torment:1;
// u32 bideTurns:2;
// u32 lockConfusionTurns:2;
// u32 multipleTurns:1;
// u32 wrapped:1;
// u32 powder:1;
// u32 padding:1;
// u32 infatuation:4; // one bit for each battler
// u32 defenseCurl:1;
// u32 transformed:1;
// u32 recharge:1;
// u32 rage:1;
// u32 substitute:1;
// u32 destinyBond:1;
// u32 escapePrevention:1;
// u32 nightmare:1;
// u32 cursed:1;
// u32 foresight:1;
// u32 dragonCheer:1;
// u32 focusEnergy:1;
};
struct BattlePokemon
{
/*0x00*/ u16 species;
@ -340,10 +372,15 @@ struct BattlePokemon
/*0x45*/ u32 experience;
/*0x49*/ u32 personality;
/*0x4D*/ u32 status1;
/*0x51*/ u32 status2;
/*0x55*/ u32 otId;
/*0x59*/ u8 metLevel;
/*0x5A*/ bool8 isShiny;
/*0x51*/ union {
struct {
u32 status2; // To be expanded to include Status3/4
};
struct Volatiles volatiles;
};
/*0x5D*/ u32 otId;
/*0x61*/ u8 metLevel;
/*0x62*/ bool8 isShiny;
};
struct EvolutionParam

View File

@ -1632,7 +1632,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
if (HasDamagingMove(battlerDef) && !((gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE)
|| IsBattlerIncapacitated(battlerDef, abilityDef)
|| gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)))
|| gBattleMons[battlerDef].volatiles.infatuation
|| gBattleMons[battlerDef].volatiles.confusionTurns))
ADJUST_SCORE(-10);
if (HasMoveWithEffect(battlerAtk, EFFECT_SUBSTITUTE) && !(gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE))
ADJUST_SCORE(-10);

View File

@ -104,7 +104,7 @@ enum
LIST_ITEM_STATS,
LIST_ITEM_STAT_STAGES,
LIST_ITEM_STATUS1,
LIST_ITEM_STATUS2,
LIST_ITEM_VOLATILE,
LIST_ITEM_STATUS3,
LIST_ITEM_STATUS4,
LIST_ITEM_SIDE_STATUS,
@ -139,23 +139,6 @@ enum
LIST_STATUS1_FROSTBITE,
};
enum
{
LIST_STATUS2_CONFUSION,
LIST_STATUS2_FLINCHED,
LIST_STATUS2_TORMENT,
LIST_STATUS2_POWDER,
LIST_STATUS2_DEFENSE_CURL,
LIST_STATUS2_RECHARGE,
LIST_STATUS2_RAGE,
LIST_STATUS2_DESTINY_BOND,
LIST_STATUS2_ESCAPE_PREVENTION,
LIST_STATUS2_CURSED,
LIST_STATUS2_FORESIGHT,
LIST_STATUS2_DRAGON_CHEER,
LIST_STATUS2_FOCUS_ENERGY
};
enum
{
LIST_STATUS3_LEECH_SEED_HEALER,
@ -268,6 +251,7 @@ enum
VAL_BITFIELD_8,
VAL_BITFIELD_16,
VAL_BITFIELD_32,
VAL_VOLATILE,
VAR_SIDE_STATUS,
VAR_SHOW_HP,
VAR_SUBSTITUTE,
@ -290,7 +274,7 @@ static const u8 sText_Types[] = _("Types");
static const u8 sText_Stats[] = _("Stats");
static const u8 sText_StatStages[] = _("Stat Stages");
static const u8 sText_Status1[] = _("Status1");
static const u8 sText_Status2[] = _("Status2");
static const u8 sText_VolatileStatus[] = _("Volatiles");
static const u8 sText_Status3[] = _("Status3");
static const u8 sText_Status4[] = _("Status4");
static const u8 sText_SideStatus[] = _("Side Status");
@ -435,23 +419,6 @@ static const struct BitfieldInfo sStatus1Bitfield[] =
{/*Frostbite*/ 1, 12},
};
static const struct BitfieldInfo sStatus2Bitfield[] =
{
{/*Confusion*/ 3, 0},
{/*Flinched*/ 1, 3},
{/*Torment*/ 1, 7},
{/*Powder*/ 1, 14},
{/*Defense Curl*/ 1, 20},
{/*Recharge*/ 1, 22},
{/*Rage*/ 1, 23},
{/*Destiny Bond*/ 1, 25},
{/*Escape Prevention*/ 1, 26},
{/*Cursed*/ 1, 28},
{/*Foresight*/ 1, 29},
{/*Dragon Cheer*/ 1, 30},
{/*Focus Energy*/ 1, 31},
};
static const struct BitfieldInfo sStatus3Bitfield[] =
{
{/*Leech Seed Battler*/ 2, 0},
@ -532,7 +499,7 @@ static const struct ListMenuItem sMainListItems[] =
{sText_Stats, LIST_ITEM_STATS},
{sText_StatStages, LIST_ITEM_STAT_STAGES},
{sText_Status1, LIST_ITEM_STATUS1},
{sText_Status2, LIST_ITEM_STATUS2},
{sText_VolatileStatus, LIST_ITEM_VOLATILE},
{sText_Status3, LIST_ITEM_STATUS3},
{sText_Status4, LIST_ITEM_STATUS4},
{sText_SideStatus, LIST_ITEM_SIDE_STATUS},
@ -566,21 +533,21 @@ static const struct ListMenuItem sStatus1ListItems[] =
{sText_Frostbite, LIST_STATUS1_FROSTBITE},
};
static const struct ListMenuItem sStatus2ListItems[] =
static const struct ListMenuItem sVolatileStatusListItems[] =
{
{sText_Confusion, LIST_STATUS2_CONFUSION},
{sText_Flinched, LIST_STATUS2_FLINCHED},
{sText_Torment, LIST_STATUS2_TORMENT},
{sText_Powder, LIST_STATUS2_POWDER},
{sText_DefenseCurl, LIST_STATUS2_DEFENSE_CURL},
{sText_Recharge, LIST_STATUS2_RECHARGE},
{sText_Rage, LIST_STATUS2_RAGE},
{sText_DestinyBond, LIST_STATUS2_DESTINY_BOND},
{sText_EscapePrevention, LIST_STATUS2_ESCAPE_PREVENTION},
{sText_Cursed, LIST_STATUS2_CURSED},
{sText_Foresight, LIST_STATUS2_FORESIGHT},
{sText_DragonCheer, LIST_STATUS2_DRAGON_CHEER},
{sText_FocusEnergy, LIST_STATUS2_FOCUS_ENERGY},
{COMPOUND_STRING("Confusion"), VOLATILE_CONFUSION},
{COMPOUND_STRING("Flinched"), VOLATILE_FLINCHED},
{COMPOUND_STRING("Torment"), VOLATILE_TORMENT},
{COMPOUND_STRING("Powder"), VOLATILE_POWDER},
{COMPOUND_STRING("DefenseCurl"), VOLATILE_DEFENSE_CURL},
{COMPOUND_STRING("Recharge"), VOLATILE_RECHARGE},
{COMPOUND_STRING("Rage"), VOLATILE_RAGE},
{COMPOUND_STRING("DestinyBond"), VOLATILE_DESTINY_BOND},
{COMPOUND_STRING("EscapePrevention"), VOLATILE_ESCAPE_PREVENTION},
{COMPOUND_STRING("Cursed"), VOLATILE_CURSED},
{COMPOUND_STRING("Foresight"), VOLATILE_FORESIGHT},
{COMPOUND_STRING("DragonCheer"), VOLATILE_DRAGON_CHEER},
{COMPOUND_STRING("FocusEnergy"), VOLATILE_FOCUS_ENERGY},
};
static const struct ListMenuItem sStatus3ListItems[] =
@ -805,19 +772,6 @@ static const struct BgTemplate sBgTemplates[] =
}
};
static const u8 sBitsToMaxDigit[] =
{
[0] = 0,
[1] = 1, // max 1
[2] = 1, // max 3
[3] = 1, // max 7
[4] = 2, // max 15
[5] = 2, // max 31
[6] = 2, // max 63
[7] = 3, // max 127
[8] = 3, // max 255
};
static const bool8 sHasChangeableEntries[LIST_ITEM_COUNT] =
{
[LIST_ITEM_MOVES] = TRUE,
@ -1586,10 +1540,9 @@ static void CreateSecondaryListMenu(struct BattleDebugMenu *data)
itemsCount = ARRAY_COUNT(sStatus1ListItems);
data->bitfield = sStatus1Bitfield;
break;
case LIST_ITEM_STATUS2:
listTemplate.items = sStatus2ListItems;
itemsCount = ARRAY_COUNT(sStatus2ListItems);
data->bitfield = sStatus2Bitfield;
case LIST_ITEM_VOLATILE:
listTemplate.items = sVolatileStatusListItems;
itemsCount = ARRAY_COUNT(sVolatileStatusListItems);
break;
case LIST_ITEM_STATUS3:
listTemplate.items = sStatus3ListItems;
@ -1808,6 +1761,9 @@ static void UpdateBattlerValue(struct BattleDebugMenu *data)
*(u32 *)(data->modifyArrows.modifiedValPtr) &= ~(GetBitfieldToAndValue(data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount));
*(u32 *)(data->modifyArrows.modifiedValPtr) |= (data->modifyArrows.currValue << data->bitfield[data->currentSecondaryListItemId].currBit);
break;
case VAL_VOLATILE:
SetMonVolatile(data->battlerId, data->currentSecondaryListItemId, data->modifyArrows.currValue);
break;
case VAR_SIDE_STATUS:
*GetSideStatusValue(data, TRUE, data->modifyArrows.currValue != 0) = data->modifyArrows.currValue;
break;
@ -1818,12 +1774,12 @@ static void UpdateBattlerValue(struct BattleDebugMenu *data)
*(u8 *)(data->modifyArrows.modifiedValPtr) = data->modifyArrows.currValue;
if (*(u8 *)(data->modifyArrows.modifiedValPtr) == 0)
{
gBattleMons[data->battlerId].status2 &= ~STATUS2_SUBSTITUTE;
gBattleMons[data->battlerId].volatiles.substitute = FALSE;
gBattleSpritesDataPtr->battlerData[data->battlerId].behindSubstitute = 0;
}
else
{
gBattleMons[data->battlerId].status2 |= STATUS2_SUBSTITUTE;
gBattleMons[data->battlerId].volatiles.substitute = TRUE;
gBattleSpritesDataPtr->battlerData[data->battlerId].behindSubstitute = 1;
}
break;
@ -2175,11 +2131,28 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data)
data->modifyArrows.currValue = GetBitfieldValue(gBattleMons[data->battlerId].status1, data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS;
case LIST_ITEM_STATUS2:
data->modifyArrows.modifiedValPtr = &gBattleMons[data->battlerId].status2;
data->modifyArrows.currValue = GetBitfieldValue(gBattleMons[data->battlerId].status2, data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS;
case LIST_ITEM_VOLATILE:
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;
switch (data->currentSecondaryListItemId)
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_MAX_SIZE)
/* Expands to the following:
* case VOLATILE_CONFUSION:
data->modifyArrows.maxValue = MAX_BITS(3); // Max value 7
break;
* case VOLATILE_FLINCHED:
data->modifyArrows.maxValue = MAX_BITS(1); // Max value 1
break;
* ...etc.
*/
default:
data->modifyArrows.maxValue = 0;
}
data->modifyArrows.maxDigits = MAX_DIGITS(data->modifyArrows.maxValue);
break;
case LIST_ITEM_STATUS3:
data->modifyArrows.modifiedValPtr = &gStatuses3[data->battlerId];
data->modifyArrows.currValue = GetBitfieldValue(gStatuses3[data->battlerId], data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
@ -2198,7 +2171,7 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data)
CASE_ITEM_STATUS:
data->modifyArrows.minValue = 0;
data->modifyArrows.maxValue = (1 << data->bitfield[data->currentSecondaryListItemId].bitsCount) - 1;
data->modifyArrows.maxDigits = sBitsToMaxDigit[data->bitfield[data->currentSecondaryListItemId].bitsCount];
data->modifyArrows.maxDigits = MAX_DIGITS(data->modifyArrows.maxValue);
break;
case LIST_ITEM_SIDE_STATUS:
data->modifyArrows.minValue = 0;

View File

@ -3115,11 +3115,14 @@ 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;)
void SwitchInClearSetData(u32 battler)
{
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)
@ -3139,7 +3142,15 @@ void SwitchInClearSetData(u32 battler)
}
if (effect == EFFECT_BATON_PASS)
{
gBattleMons[battler].status2 &= (STATUS2_CONFUSION | STATUS2_FOCUS_ENERGY_ANY | STATUS2_SUBSTITUTE | STATUS2_ESCAPE_PREVENTION | STATUS2_CURSED);
// 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;
* ...etc
*/
gStatuses3[battler] &= (STATUS3_LEECHSEED_BATTLER | STATUS3_LEECHSEED | STATUS3_ALWAYS_HITS | STATUS3_PERISH_SONG | STATUS3_ROOTED
| STATUS3_GASTRO_ACID | STATUS3_EMBARGO | STATUS3_TELEKINESIS | STATUS3_MAGNET_RISE | STATUS3_HEAL_BLOCK
| STATUS3_AQUA_RING | STATUS3_POWER_TRICK);

View File

@ -369,12 +369,12 @@ static void Cmd_printselectionstringfromtable(void);
static void Cmd_setadditionaleffects(void);
static void Cmd_seteffectprimary(void);
static void Cmd_seteffectsecondary(void);
static void Cmd_clearstatus2(void);
static void Cmd_clearvolatile(void);
static void Cmd_tryfaintmon(void);
static void Cmd_dofaintanimation(void);
static void Cmd_cleareffectsonfaint(void);
static void Cmd_jumpifstatus(void);
static void Cmd_jumpifstatus2(void);
static void Cmd_jumpifvolatile(void);
static void Cmd_jumpifability(void);
static void Cmd_jumpifsideaffecting(void);
static void Cmd_jumpifstat(void);
@ -628,12 +628,12 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_setadditionaleffects, //0x15
Cmd_seteffectprimary, //0x16
Cmd_seteffectsecondary, //0x17
Cmd_clearstatus2, //0x18
Cmd_clearvolatile, //0x18
Cmd_tryfaintmon, //0x19
Cmd_dofaintanimation, //0x1A
Cmd_cleareffectsonfaint, //0x1B
Cmd_jumpifstatus, //0x1C
Cmd_jumpifstatus2, //0x1D
Cmd_jumpifvolatile, //0x1D
Cmd_jumpifability, //0x1E
Cmd_jumpifsideaffecting, //0x1F
Cmd_jumpifstat, //0x20
@ -1601,7 +1601,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
calc = (calc * 80) / 100; // 1.2 snow cloak loss
break;
case ABILITY_TANGLED_FEET:
if (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
if (gBattleMons[battlerDef].volatiles.confusionTurns)
calc = (calc * 50) / 100; // 1.5 tangled feet loss
break;
}
@ -4555,15 +4555,14 @@ static void Cmd_seteffectsecondary(void)
SetMoveEffect(FALSE, FALSE);
}
static void Cmd_clearstatus2(void)
static void Cmd_clearvolatile(void)
{
CMD_ARGS(u8 battler, u32 status2);
CMD_ARGS(u8 battler, u8 _volatile);
u32 status2 = cmd->status2;
u32 battler = GetBattlerForBattleScript(cmd->battler);
gBattleMons[battler].status2 &= ~status2;
if (status2 & STATUS2_MULTIPLETURNS)
SetMonVolatile(battler, cmd->_volatile, 0);
if (cmd->_volatile == VOLATILE_MULTIPLETURNS)
gProtectStructs[battler].chargingTurn = FALSE;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -4724,15 +4723,14 @@ static void Cmd_jumpifstatus(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_jumpifstatus2(void)
static void Cmd_jumpifvolatile(void)
{
CMD_ARGS(u8 battler, u32 flags, const u8 *jumpInstr);
CMD_ARGS(u8 battler, u8 volatileStatus, const u8 *jumpInstr);
u8 battler = GetBattlerForBattleScript(cmd->battler);
u32 flags = cmd->flags;
const u8 *jumpInstr = cmd->jumpInstr;
if (gBattleMons[battler].status2 & flags && IsBattlerAlive(battler))
if (GetMonVolatile(battler, cmd->volatileStatus) && IsBattlerAlive(battler))
gBattlescriptCurrInstr = jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;

View File

@ -11326,3 +11326,40 @@ bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId)
}
return FALSE;
}
#define UNPACK_VOLATILE_GETTERS(_enum, _fieldName, _typeBitSize, ...) 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
u32 GetMonVolatile(u32 battler, enum Volatile _volatile)
{
switch (_volatile)
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_GETTERS)
/* Expands to:
case VOLATILE_CONFUSION:
return gBattleMons[battler].volatiles.confusionTurns;
*/
default: // Invalid volatile status
return 0;
}
}
#define UNPACK_VOLATILE_SETTERS(_enum, _fieldName, _typeBitSize, ...) case _enum: gBattleMons[battler].volatiles._fieldName = min(GET_VOLATILE_MAXIMUM(_typeBitSize), 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
void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue)
{
switch (_volatile)
{
VOLATILE_DEFINITIONS(UNPACK_VOLATILE_SETTERS)
/* Expands to:
case VOLATILE_CONFUSION:
gBattleMons[battler].volatiles.confusionTurns = min(MAX_BITS(3), newValue);
break;
*/
default: // Invalid volatile status
return;
}
}