diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index df4e00fb19..c899901eae 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -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 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 51ba648943..576f53217e 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -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 diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 27e3706e3f..8cb16379da 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -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 diff --git a/include/battle_util.h b/include/battle_util.h index 963f2288e4..24519ce022 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -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 diff --git a/include/constants/battle.h b/include/constants/battle.h index 68a1c25e6f..35bc9b4c58 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -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) diff --git a/include/metaprogram.h b/include/metaprogram.h index be62b2f9e5..e12822946a 100644 --- a/include/metaprogram.h +++ b/include/metaprogram.h @@ -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 diff --git a/include/pokemon.h b/include/pokemon.h index 65202eb905..3219eac07f 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -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 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 92292c1bcb..4f1b251092 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -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); diff --git a/src/battle_debug.c b/src/battle_debug.c index ffe1b76cea..bf48d0e36b 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -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; diff --git a/src/battle_main.c b/src/battle_main.c index 8b7ac3386b..ebf1ce36c6 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -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); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b00f6801bd..1cbc0caaa2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -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; diff --git a/src/battle_util.c b/src/battle_util.c index 029d1d4361..8d6504d186 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -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; + } +}