feat: implement logic with new code

This commit is contained in:
Moos Toet 2025-12-11 22:52:02 +01:00
parent 91c7bd875c
commit c5b00cc02a
16 changed files with 303 additions and 233 deletions

View File

@ -2737,3 +2737,9 @@
callnative ScrCmd_istmrelearneractive, requests_effects=1
.4byte \destination
.endm
@ Sets a starting status for the next battle. Can be stacked.
.macro setstartingstatus status:req
callnative ScrCmd_setstartingstatus
.byte \status
.endm

View File

@ -691,14 +691,12 @@ struct BattleStruct
struct LinkBattlerHeader linkBattlerHeader;
struct BattleVideo battleVideo;
} multiBuffer;
u32 startingStatus; // statuses to apply at battle start. defined in constants/battle.h
u8 battlerKOAnimsRunning:3;
u8 friskedAbility:1; // If identifies two mons, show the ability pop-up only once.
u8 fickleBeamBoosted:1;
u8 poisonPuppeteerConfusion:1;
u8 toxicChainPriority:1; // If Toxic Chain will trigger on target, all other non volatiles will be blocked
u8 battlersSorted:1; // To avoid unnessasery computation
u16 startingStatusTimer;
struct BattleTvMovePoints tvMovePoints;
struct BattleTv tv;
u8 AI_monToSwitchIntoId[MAX_BATTLERS_COUNT];
@ -1095,6 +1093,7 @@ extern u8 gSentPokesToOpponent[2];
extern struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT];
extern struct BattleScripting gBattleScripting;
extern struct BattleStruct *gBattleStruct;
extern struct StartingStatuses gStartingStatuses;
extern struct AiBattleData *gAiBattleData;
extern struct AiThinkingStruct *gAiThinkingStruct;
extern struct AiLogicData *gAiLogicData;

View File

@ -448,5 +448,7 @@ bool32 IsDazzlingAbility(enum Ability ability);
bool32 IsAllowedToUseBag(void);
bool32 IsAnyTargetTurnDamaged(u32 battlerAtk);
bool32 IsMimikyuDisguised(u32 battler);
void SetStartingStatus(enum StartingStatus status);
void ResetStartingStatuses(void);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -224,10 +224,6 @@
// Var Settings
// To use the following features, change the 0 for a var present in include/constants/vars.h, preferably an unused one.
// Eg: You may rename VAR_UNUSED_0x404E to a descriptive name and use it below.
#define B_VAR_STARTING_STATUS 0 // If this var has a value, assigning a STARTING_STATUS_xx to it before battle causes the battle to start with that status active.
// This var should never remain non-zero long enough for the player to save.
#define B_VAR_STARTING_STATUS_HAZARDS 0 // Same as above, but for entry hazards. Uses STARTING_HAZARD_xx constants.
#define B_VAR_STARTING_STATUS_TIMER 0 // If this var has a value >= 1, field effects will last that number of turns, otherwise they last until overwritten.
#define B_VAR_WILD_AI_FLAGS 0 // If not 0, you can use this var to add to default wild AI flags. IMPORTANT: NOT usable with flags above (1 << 15)
// This var should never remain non-zero long enough for the player to save.
// For better wild AI handling, edit GetWildAiFlags() in src/battle_ai_main.c

View File

@ -1132,12 +1132,6 @@
// Vars
#undef B_VAR_DIFFICULTY
#define B_VAR_DIFFICULTY TESTING_VAR_DIFFICULTY
#undef B_VAR_STARTING_STATUS
#define B_VAR_STARTING_STATUS TESTING_VAR_STARTING_STATUS
#undef B_VAR_STARTING_STATUS_HAZARDS
#define B_VAR_STARTING_STATUS_HAZARDS TESTING_VAR_STARTING_STATUS_HAZARDS
#undef B_VAR_STARTING_STATUS_TIMER
#define B_VAR_STARTING_STATUS_TIMER TESTING_VAR_STARTING_STATUS_TIMER
// Flags
#undef B_FLAG_SLEEP_CLAUSE

View File

@ -669,68 +669,64 @@ enum FaintedActions
FAINTED_ACTIONS_MAX_CASE,
};
// Constants for B_VAR_STARTING_STATUS
// Timer value controlled by B_VAR_STARTING_STATUS_TIMER
// Enum, fieldName, Type, max value
#define STARTING_STATUS_DEFINITIONS(F) \
F(STARTING_STATUS_ELECTRIC_TERRAIN, electricTerrain, (u32, 1)) /* Electric Terrain (Permanent) */ \
F(STARTING_STATUS_ELECTRIC_TERRAIN_TEMPORARY, electricTerrainTemporary, (u32, 1)) /* Electric Terrain Temporary (5 turns) */ \
F(STARTING_STATUS_MISTY_TERRAIN, mistyTerrain, (u32, 1)) /* Misty Terrain (Permanent) */ \
F(STARTING_STATUS_MISTY_TERRAIN_TEMPORARY, mistyTerrainTemporary, (u32, 1)) /* Misty Terrain Temporary (5 turns) */ \
F(STARTING_STATUS_GRASSY_TERRAIN, grassyTerrain, (u32, 1)) /* Grassy Terrain (Permanent) */ \
F(STARTING_STATUS_GRASSY_TERRAIN_TEMPORARY, grassyTerrainTemporary, (u32, 1)) /* Grassy Terrain Temporary (5 turns) */ \
F(STARTING_STATUS_PSYCHIC_TERRAIN, psychicTerrain, (u32, 1)) /* Psychic Terrain (Permanent) */ \
F(STARTING_STATUS_PSYCHIC_TERRAIN_TEMPORARY, psychicTerrainTemporary, (u32, 1)) /* Psychic Terrain Temporary (5 turns) */ \
F(STARTING_STATUS_TRICK_ROOM, trickRoom, (u32, 1)) /* Trick Room (Permanent) */ \
F(STARTING_STATUS_TRICK_ROOM_TEMPORARY, trickRoomTemporary, (u32, 1)) /* Trick Room Temporary (5 turns) */ \
F(STARTING_STATUS_MAGIC_ROOM, magicRoom, (u32, 1)) /* Magic Room (Permanent) */ \
F(STARTING_STATUS_MAGIC_ROOM_TEMPORARY, magicRoomTemporary, (u32, 1)) /* Magic Room Temporary (5 turns) */ \
F(STARTING_STATUS_WONDER_ROOM, wonderRoom, (u32, 1)) /* Wonder Room (Permanent) */ \
F(STARTING_STATUS_WONDER_ROOM_TEMPORARY, wonderRoomTemporary, (u32, 1)) /* Wonder Room Temporary (5 turns) */ \
F(STARTING_STATUS_TAILWIND_PLAYER, tailwindPlayer, (u32, 1)) /* Tailwind Player (Permanent) */ \
F(STARTING_STATUS_TAILWIND_PLAYER_TEMPORARY, tailwindPlayerTemporary, (u32, 1)) /* Tailwind Player Temporary (4/3 turns) */ \
F(STARTING_STATUS_TAILWIND_OPPONENT, tailwindOpponent, (u32, 1)) /* Tailwind Opponent (Permanent) */ \
F(STARTING_STATUS_TAILWIND_OPPONENT_TEMPORARY, tailwindOpponentTemporary, (u32, 1)) /* Tailwind Opponent Temporary (4/3 turns) */ \
F(STARTING_STATUS_RAINBOW_PLAYER, rainbowPlayer, (u32, 1)) /* Rainbow Player (Permanent) */ \
F(STARTING_STATUS_RAINBOW_PLAYER_TEMPORARY, rainbowPlayerTemporary, (u32, 1)) /* Rainbow Player Temporary (4 turns) */ \
F(STARTING_STATUS_RAINBOW_OPPONENT, rainbowOpponent, (u32, 1)) /* Rainbow Opponent (Permanent) */ \
F(STARTING_STATUS_RAINBOW_OPPONENT_TEMPORARY, rainbowOpponentTemporary, (u32, 1)) /* Rainbow Opponent Temporary (4 turns) */ \
F(STARTING_STATUS_SEA_OF_FIRE_PLAYER, seaOfFirePlayer, (u32, 1)) /* Sea Of Fire Player (Permanent) */ \
F(STARTING_STATUS_SEA_OF_FIRE_PLAYER_TEMPORARY, seaOfFirePlayerTemporary, (u32, 1)) /* Sea Of Fire Player Temporary (4 turns) */ \
F(STARTING_STATUS_SEA_OF_FIRE_OPPONENT, seaOfFireOpponent, (u32, 1)) /* Sea Of Fire Opponent (Permanent) */ \
F(STARTING_STATUS_SEA_OF_FIRE_OPPONENT_TEMPORARY, seaOfFireOpponentTemporary, (u32, 1)) /* Sea Of Fire Opponent Temporary (4 turns) */ \
F(STARTING_STATUS_SWAMP_PLAYER, swampPlayer, (u32, 1)) /* Swamp Player (Permanent) */ \
F(STARTING_STATUS_SWAMP_PLAYER_TEMPORARY, swampPlayerTemporary, (u32, 1)) /* Swamp Player Temporary (4 turns) */ \
F(STARTING_STATUS_SWAMP_OPPONENT, swampOpponent, (u32, 1)) /* Swamp Opponent (Permanent) */ \
F(STARTING_STATUS_SWAMP_OPPONENT_TEMPORARY, swampOpponentTemporary, (u32, 1)) /* Swamp Opponent Temporary (4 turns) */ \
/* Hazards */ \
F(STARTING_STATUS_SPIKES_PLAYER_L1, spikesPlayerL1, (u32, 1)) /* Spikes Player Layer 1 */ \
F(STARTING_STATUS_SPIKES_PLAYER_L2, spikesPlayerL2, (u32, 1)) /* Spikes Player Layer 2 */ \
F(STARTING_STATUS_SPIKES_PLAYER_L3, spikesPlayerL3, (u32, 1)) /* Spikes Player Layer 3 */ \
F(STARTING_STATUS_SPIKES_OPPONENT_L1, spikesOpponentL1, (u32, 1)) /* Spikes Opponent Layer 1 */ \
F(STARTING_STATUS_SPIKES_OPPONENT_L2, spikesOpponentL2, (u32, 1)) /* Spikes Opponent Layer 2 */ \
F(STARTING_STATUS_SPIKES_OPPONENT_L3, spikesOpponentL3, (u32, 1)) /* Spikes Opponent Layer 3 */ \
F(STARTING_STATUS_TOXIC_SPIKES_PLAYER_L1, toxicSpikesPlayerL1, (u32, 1)) /* Toxic Spikes Player Layer 1 */ \
F(STARTING_STATUS_TOXIC_SPIKES_PLAYER_L2, toxicSpikesPlayerL2, (u32, 1)) /* Toxic Spikes Player Layer 2 */ \
F(STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L1, toxicSpikesOpponentL1, (u32, 1)) /* Toxic Spikes Opponent Layer 1 */ \
F(STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L2, toxicSpikesOpponentL2, (u32, 1)) /* Toxic Spikes Opponent Layer 2 */ \
F(STARTING_STATUS_STICKY_WEB_PLAYER, stickyWebPlayer, (u32, 1)) /* Sticky Web Player */ \
F(STARTING_STATUS_STICKY_WEB_OPPONENT, stickyWebOpponent, (u32, 1)) /* Sticky Web Opponent */ \
F(STARTING_STATUS_STEALTH_ROCK_PLAYER, stealthRockPlayer, (u32, 1)) /* Stealth Rock Player */ \
F(STARTING_STATUS_STEALTH_ROCK_OPPONENT, stealthRockOpponent, (u32, 1)) /* Stealth Rock Opponent */ \
F(STARTING_STATUS_SHARP_STEEL_PLAYER, sharpSteelPlayer, (u32, 1)) /* Sharp Steel Player */ \
F(STARTING_STATUS_SHARP_STEEL_OPPONENT, sharpSteelOpponent, (u32, 1)) /* Sharp Steel Opponent */ \
#define UNPACK_STARTING_STATUS_ENUMS(_enum, ...) _enum,
// Constants for SetStartingStatus
enum StartingStatus
{
STARTING_STATUS_ELECTRIC_TERRAIN = (1 << 0), // Electric Terrain
STARTING_STATUS_MISTY_TERRAIN = (1 << 1), // Misty Terrain
STARTING_STATUS_GRASSY_TERRAIN = (1 << 2), // Grassy Terrain
STARTING_STATUS_PSYCHIC_TERRAIN = (1 << 3), // Psychic Terrain
STARTING_STATUS_TRICK_ROOM = (1 << 4), // Trick Room
STARTING_STATUS_MAGIC_ROOM = (1 << 5), // Magic Room
STARTING_STATUS_WONDER_ROOM = (1 << 6), // Wonder Room
STARTING_STATUS_TAILWIND_PLAYER = (1 << 7), // Tailwind Player
STARTING_STATUS_TAILWIND_OPPONENT = (1 << 8), // Tailwind Opponent
STARTING_STATUS_RAINBOW_PLAYER = (1 << 9), // Rainbow Player
STARTING_STATUS_RAINBOW_OPPONENT = (1 << 10), // Rainbow Opponent
STARTING_STATUS_SEA_OF_FIRE_PLAYER = (1 << 11), // Sea Of Fire Player
STARTING_STATUS_SEA_OF_FIRE_OPPONENT = (1 << 12), // Sea Of Fire Opponent
STARTING_STATUS_SWAMP_PLAYER = (1 << 13), // Swamp Player
STARTING_STATUS_SWAMP_OPPONENT = (1 << 14), // Swamp Opponent
// Hazards (bits 15-30)
STARTING_STATUS_SPIKES_PLAYER_L1 = (1 << 15), // Spikes Player Layer 1
STARTING_STATUS_SPIKES_PLAYER_L2 = (1 << 16), // Spikes Player Layer 2
STARTING_STATUS_SPIKES_PLAYER_L3 = (1 << 17), // Spikes Player Layer 3
STARTING_STATUS_SPIKES_OPPONENT_L1 = (1 << 18), // Spikes Opponent Layer 1
STARTING_STATUS_SPIKES_OPPONENT_L2 = (1 << 19), // Spikes Opponent Layer 2
STARTING_STATUS_SPIKES_OPPONENT_L3 = (1 << 20), // Spikes Opponent Layer 3
STARTING_STATUS_TOXIC_SPIKES_PLAYER_L1 = (1 << 21), // Toxic Spikes Player Layer 1
STARTING_STATUS_TOXIC_SPIKES_PLAYER_L2 = (1 << 22), // Toxic Spikes Player Layer 2
STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L1 = (1 << 23), // Toxic Spikes Opponent Layer 1
STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L2 = (1 << 24), // Toxic Spikes Opponent Layer 2
STARTING_STATUS_STICKY_WEB_PLAYER = (1 << 25), // Sticky Web Player
STARTING_STATUS_STICKY_WEB_OPPONENT = (1 << 26), // Sticky Web Opponent
STARTING_STATUS_STEALTH_ROCK_PLAYER = (1 << 27), // Stealth Rock Player
STARTING_STATUS_STEALTH_ROCK_OPPONENT = (1 << 28), // Stealth Rock Opponent
STARTING_STATUS_SHARP_STEEL_PLAYER = (1 << 29), // Steelsurge Player
STARTING_STATUS_SHARP_STEEL_OPPONENT = (1 << 30), // Steelsurge Opponent
STARTING_STATUS_DEFINITIONS(UNPACK_STARTING_STATUS_ENUMS)
};
// Constants for B_VAR_STARTING_STATUS_HAZARDS (shifted to fit in u16)
// These map to the same internal bits as STARTING_STATUS_* hazards
enum StartingHazard
{
STARTING_HAZARD_SPIKES_PLAYER_L1 = (1 << 0), // Spikes Player Layer 1
STARTING_HAZARD_SPIKES_PLAYER_L2 = (1 << 1), // Spikes Player Layer 2
STARTING_HAZARD_SPIKES_PLAYER_L3 = (1 << 2), // Spikes Player Layer 3
STARTING_HAZARD_SPIKES_OPPONENT_L1 = (1 << 3), // Spikes Opponent Layer 1
STARTING_HAZARD_SPIKES_OPPONENT_L2 = (1 << 4), // Spikes Opponent Layer 2
STARTING_HAZARD_SPIKES_OPPONENT_L3 = (1 << 5), // Spikes Opponent Layer 3
STARTING_HAZARD_TOXIC_SPIKES_PLAYER_L1 = (1 << 6), // Toxic Spikes Player Layer 1
STARTING_HAZARD_TOXIC_SPIKES_PLAYER_L2 = (1 << 7), // Toxic Spikes Player Layer 2
STARTING_HAZARD_TOXIC_SPIKES_OPPONENT_L1 = (1 << 8), // Toxic Spikes Opponent Layer 1
STARTING_HAZARD_TOXIC_SPIKES_OPPONENT_L2 = (1 << 9), // Toxic Spikes Opponent Layer 2
STARTING_HAZARD_STICKY_WEB_PLAYER = (1 << 10), // Sticky Web Player
STARTING_HAZARD_STICKY_WEB_OPPONENT = (1 << 11), // Sticky Web Opponent
STARTING_HAZARD_STEALTH_ROCK_PLAYER = (1 << 12), // Stealth Rock Player
STARTING_HAZARD_STEALTH_ROCK_OPPONENT = (1 << 13), // Stealth Rock Opponent
STARTING_HAZARD_SHARP_STEEL_PLAYER = (1 << 14), // Steelsurge Player
STARTING_HAZARD_SHARP_STEEL_OPPONENT = (1 << 15), // Steelsurge Opponent
};
#define STARTING_HAZARD_SHIFT 15 // Shift value to convert STARTING_HAZARD_* to internal STARTING_STATUS_* bits
enum SlideMsgStates
{
PRINT_SLIDE_MESSAGE,

View File

@ -327,9 +327,9 @@
#if TESTING
#define TESTING_VARS_START 0x9000
#define TESTING_VAR_DIFFICULTY (TESTING_VARS_START + 0x0)
#define TESTING_VAR_STARTING_STATUS (TESTING_VARS_START + 0x1)
#define TESTING_VAR_STARTING_STATUS_HAZARDS (TESTING_VARS_START + 0x2)
#define TESTING_VAR_STARTING_STATUS_TIMER (TESTING_VARS_START + 0x3)
#define TESTING_VAR_UNUSED_1 (TESTING_VARS_START + 0x1)
#define TESTING_VAR_UNUSED_2 (TESTING_VARS_START + 0x2)
#define TESTING_VAR_UNUSED_3 (TESTING_VARS_START + 0x3)
#define TESTING_VAR_UNUSED_4 (TESTING_VARS_START + 0x4)
#define TESTING_VAR_UNUSED_5 (TESTING_VARS_START + 0x5)
#define TESTING_VAR_UNUSED_6 (TESTING_VARS_START + 0x6)

View File

@ -88,12 +88,20 @@ enum TrainerBattleType
TRAINER_BATTLE_TYPE_DOUBLES,
};
#define UNPACK_STARTING_STATUSES_STRUCT(_enum, _fieldName, _typeMaxValue, ...) INVOKE_WITH_(UNPACK_STARTING_STATUSES_STRUCT_, _fieldName, UNPACK_B(_typeMaxValue));
#define UNPACK_STARTING_STATUSES_STRUCT_(_fieldName, _type, ...) _type FIRST(__VA_OPT__(_fieldName:BIT_SIZE(FIRST(__VA_ARGS__)),) _fieldName)
struct StartingStatuses
{
STARTING_STATUS_DEFINITIONS(UNPACK_STARTING_STATUSES_STRUCT)
};
struct Trainer
{
u64 aiFlags;
const struct TrainerMon *party;
u16 items[MAX_TRAINER_ITEMS];
u32 startingStatus; // this trainer starts a battle with a given status. see include/constants/battle.h for values
struct StartingStatuses startingStatus; // this trainer starts a battle with a given status. see include/constants/battle.h for values
u8 trainerClass;
u8 encounterMusic_gender; // last bit is gender
u8 trainerPic;
@ -271,7 +279,7 @@ static inline const u8 GetTrainerBackPicFromId(u16 trainerId)
return GetTrainerStructFromId(trainerId)->trainerBackPic;
}
static inline const u32 GetTrainerStartingStatusFromId(u16 trainerId)
static inline const struct StartingStatuses GetTrainerStartingStatusFromId(u16 trainerId)
{
return GetTrainerStructFromId(trainerId)->startingStatus;
}

View File

@ -209,6 +209,7 @@ EWRAM_DATA u8 gSentPokesToOpponent[2] = {0};
EWRAM_DATA struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA struct BattleScripting gBattleScripting = {0};
EWRAM_DATA struct BattleStruct *gBattleStruct = NULL;
EWRAM_DATA struct StartingStatuses gStartingStatuses = {0};
EWRAM_DATA struct AiThinkingStruct *gAiThinkingStruct = NULL;
EWRAM_DATA struct AiLogicData *gAiLogicData = NULL;
EWRAM_DATA struct AiPartyData *gAiPartyData = NULL;
@ -3735,21 +3736,18 @@ static void DoBattleIntro(void)
for (battler = 0; battler < gBattlersCount; battler++)
GetBattlerPartyState(battler)->sentOut = TRUE;
#define UNPACK_STARTING_STATUS_TO_BATTLE(_enum, _fieldName, ...) gStartingStatuses._fieldName = (statusesOpponentA._fieldName || statusesOpponentB._fieldName || gStartingStatuses._fieldName);
struct StartingStatuses statusesOpponentA = {0};
struct StartingStatuses statusesOpponentB = {0};
// Try to set a status to start the battle with
gBattleStruct->startingStatus = 0;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
gBattleStruct->startingStatus |= GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentA);
gBattleStruct->startingStatus |= GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentB);
gBattleStruct->startingStatusTimer = 0; // infinite
statusesOpponentA = GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentA);
statusesOpponentB = GetTrainerStartingStatusFromId(TRAINER_BATTLE_PARAM.opponentB);
}
if (B_VAR_STARTING_STATUS != 0)
{
gBattleStruct->startingStatus |= VarGet(B_VAR_STARTING_STATUS);
gBattleStruct->startingStatusTimer = VarGet(B_VAR_STARTING_STATUS_TIMER);
}
if (B_VAR_STARTING_STATUS_HAZARDS != 0)
gBattleStruct->startingStatus |= (u32)VarGet(B_VAR_STARTING_STATUS_HAZARDS) << STARTING_HAZARD_SHIFT;
STARTING_STATUS_DEFINITIONS(UNPACK_STARTING_STATUS_TO_BATTLE);
gBattleMainFunc = TryDoEventsBeforeFirstTurn;
}
break;
@ -3807,10 +3805,11 @@ static void TryDoEventsBeforeFirstTurn(void)
return;
break;
case FIRST_TURN_EVENTS_STARTING_STATUS:
while (gBattleStruct->startingStatus)
while (TRUE)
{
if (TryFieldEffects(FIELD_EFFECT_TRAINER_STATUSES))
return;
break;
}
gBattleStruct->eventState.beforeFristTurn++;
break;

View File

@ -3621,7 +3621,7 @@ static u32 GetFirstBattlerOnSide(u32 side)
return GetBattlerAtPosition(side == B_SIDE_PLAYER ? B_POSITION_PLAYER_LEFT : B_POSITION_OPPONENT_LEFT);
}
static inline bool32 SetStartingFieldStatus(u32 flag, u32 message, u32 anim, u16 *timer)
static inline bool32 SetStartingFieldStatus(u32 flag, u32 message, u32 anim, u16 *timer, u16 time)
{
if (!(gFieldStatuses & flag))
{
@ -3630,10 +3630,7 @@ static inline bool32 SetStartingFieldStatus(u32 flag, u32 message, u32 anim, u16
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses |= flag;
gBattleScripting.animArg1 = anim;
if (gBattleStruct->startingStatusTimer)
*timer = gBattleStruct->startingStatusTimer;
else
*timer = 0; // Infinite
*timer = time;
return TRUE;
}
@ -3641,7 +3638,7 @@ static inline bool32 SetStartingFieldStatus(u32 flag, u32 message, u32 anim, u16
return FALSE;
}
static inline bool32 SetStartingSideStatus(u32 flag, u32 side, u32 message, u32 anim, u16 *timer)
static inline bool32 SetStartingSideStatus(u32 flag, u32 side, u32 message, u32 anim, u16 *timer, u16 time)
{
if (!(gSideStatuses[side] & flag))
{
@ -3649,10 +3646,7 @@ static inline bool32 SetStartingSideStatus(u32 flag, u32 side, u32 message, u32
gBattleCommunication[MULTISTRING_CHOOSER] = message;
gSideStatuses[side] |= flag;
gBattleScripting.animArg1 = anim;
if (gBattleStruct->startingStatusTimer)
*timer = gBattleStruct->startingStatusTimer;
else
*timer = 0; // Infinite
*timer = time;
return TRUE;
}
@ -3736,14 +3730,14 @@ bool32 TryFieldEffects(enum FieldEffectCases caseId)
switch (caseId)
{
case FIELD_EFFECT_TRAINER_STATUSES: // starting field/side/etc statuses with a variable
if (gBattleStruct->startingStatus & STARTING_STATUS_ELECTRIC_TERRAIN)
if (gStartingStatuses.electricTerrain || gStartingStatuses.electricTerrainTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_ELECTRIC_TERRAIN,
B_MSG_TERRAIN_SET_ELECTRIC,
0,
&gFieldTimers.terrainTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_ELECTRIC_TERRAIN;
&gFieldTimers.terrainTimer, gStartingStatuses.electricTerrain ? 0 : 5);
gStartingStatuses.electricTerrainTemporary = gStartingStatuses.electricTerrain = FALSE;
isTerrain = TRUE;
if (effect)
{
@ -3751,14 +3745,14 @@ bool32 TryFieldEffects(enum FieldEffectCases caseId)
return TRUE;
}
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_MISTY_TERRAIN)
else if (gStartingStatuses.mistyTerrain || gStartingStatuses.mistyTerrainTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_MISTY_TERRAIN,
B_MSG_TERRAIN_SET_MISTY,
0,
&gFieldTimers.terrainTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_MISTY_TERRAIN;
&gFieldTimers.terrainTimer, gStartingStatuses.mistyTerrain ? 0 : 5);
gStartingStatuses.mistyTerrainTemporary = gStartingStatuses.mistyTerrain = FALSE;
isTerrain = TRUE;
if (effect)
{
@ -3766,14 +3760,14 @@ bool32 TryFieldEffects(enum FieldEffectCases caseId)
return TRUE;
}
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_GRASSY_TERRAIN)
else if (gStartingStatuses.grassyTerrain || gStartingStatuses.grassyTerrainTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_GRASSY_TERRAIN,
B_MSG_TERRAIN_SET_GRASSY,
0,
&gFieldTimers.terrainTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_GRASSY_TERRAIN;
&gFieldTimers.terrainTimer, gStartingStatuses.grassyTerrain ? 0 : 5);
gStartingStatuses.grassyTerrainTemporary = gStartingStatuses.grassyTerrain = FALSE;
isTerrain = TRUE;
if (effect)
{
@ -3781,207 +3775,239 @@ bool32 TryFieldEffects(enum FieldEffectCases caseId)
return TRUE;
}
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_PSYCHIC_TERRAIN)
else if (gStartingStatuses.psychicTerrain || gStartingStatuses.psychicTerrainTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_PSYCHIC_TERRAIN,
B_MSG_TERRAIN_SET_PSYCHIC,
0,
&gFieldTimers.terrainTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_PSYCHIC_TERRAIN;
&gFieldTimers.terrainTimer, gStartingStatuses.psychicTerrain ? 0 : 5);
gStartingStatuses.psychicTerrainTemporary = gStartingStatuses.psychicTerrain = FALSE;
isTerrain = TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_TRICK_ROOM)
else if (gStartingStatuses.trickRoom || gStartingStatuses.trickRoomTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_TRICK_ROOM,
B_MSG_SET_TRICK_ROOM,
B_ANIM_TRICK_ROOM,
&gFieldTimers.trickRoomTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TRICK_ROOM;
&gFieldTimers.trickRoomTimer, gStartingStatuses.trickRoom ? 0 : 5);
gStartingStatuses.trickRoomTemporary = gStartingStatuses.trickRoom = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_MAGIC_ROOM)
else if (gStartingStatuses.magicRoom || gStartingStatuses.magicRoomTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_MAGIC_ROOM,
B_MSG_SET_MAGIC_ROOM,
B_ANIM_MAGIC_ROOM,
&gFieldTimers.magicRoomTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_MAGIC_ROOM;
&gFieldTimers.magicRoomTimer, gStartingStatuses.magicRoom ? 0 : 5);
gStartingStatuses.magicRoomTemporary = gStartingStatuses.magicRoom = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_WONDER_ROOM)
else if (gStartingStatuses.wonderRoom || gStartingStatuses.wonderRoomTemporary)
{
effect = SetStartingFieldStatus(
STATUS_FIELD_WONDER_ROOM,
B_MSG_SET_WONDER_ROOM,
B_ANIM_WONDER_ROOM,
&gFieldTimers.wonderRoomTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_WONDER_ROOM;
&gFieldTimers.wonderRoomTimer, gStartingStatuses.wonderRoom ? 0 : 5);
gStartingStatuses.wonderRoomTemporary = gStartingStatuses.wonderRoom = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_TAILWIND_PLAYER)
else if (gStartingStatuses.tailwindPlayer || gStartingStatuses.tailwindPlayerTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_TAILWIND,
B_SIDE_PLAYER,
B_MSG_SET_TAILWIND,
B_ANIM_TAILWIND,
&gSideTimers[B_SIDE_PLAYER].tailwindTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TAILWIND_PLAYER;
&gSideTimers[B_SIDE_PLAYER].tailwindTimer, gStartingStatuses.tailwindPlayer ? 0 : (B_TAILWIND_TURNS >= GEN_5 ? 4 : 3));
gStartingStatuses.tailwindPlayerTemporary = gStartingStatuses.tailwindPlayer = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_TAILWIND_OPPONENT)
else if (gStartingStatuses.tailwindOpponent || gStartingStatuses.tailwindOpponentTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_TAILWIND,
B_SIDE_OPPONENT,
B_MSG_SET_TAILWIND,
B_ANIM_TAILWIND,
&gSideTimers[B_SIDE_OPPONENT].tailwindTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TAILWIND_OPPONENT;
&gSideTimers[B_SIDE_OPPONENT].tailwindTimer, gStartingStatuses.tailwindOpponent ? 0 : (B_TAILWIND_TURNS >= GEN_5 ? 4 : 3));
gStartingStatuses.tailwindOpponentTemporary = gStartingStatuses.tailwindOpponent = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_RAINBOW_PLAYER)
else if (gStartingStatuses.rainbowPlayer || gStartingStatuses.rainbowPlayerTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_RAINBOW,
B_SIDE_PLAYER,
B_MSG_SET_RAINBOW,
B_ANIM_RAINBOW,
&gSideTimers[B_SIDE_PLAYER].rainbowTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_RAINBOW_PLAYER;
&gSideTimers[B_SIDE_PLAYER].rainbowTimer, gStartingStatuses.rainbowPlayer ? 0 : 4);
gStartingStatuses.rainbowPlayerTemporary = gStartingStatuses.rainbowPlayer = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_RAINBOW_OPPONENT)
else if (gStartingStatuses.rainbowOpponent || gStartingStatuses.rainbowOpponentTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_RAINBOW,
B_SIDE_OPPONENT,
B_MSG_SET_RAINBOW,
B_ANIM_RAINBOW,
&gSideTimers[B_SIDE_OPPONENT].rainbowTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_RAINBOW_OPPONENT;
&gSideTimers[B_SIDE_OPPONENT].rainbowTimer, gStartingStatuses.rainbowOpponent ? 0 : 4);
gStartingStatuses.rainbowOpponentTemporary = gStartingStatuses.rainbowOpponent = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SEA_OF_FIRE_PLAYER)
else if (gStartingStatuses.seaOfFirePlayer || gStartingStatuses.seaOfFirePlayerTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_SEA_OF_FIRE,
B_SIDE_PLAYER,
B_MSG_SET_SEA_OF_FIRE,
B_ANIM_SEA_OF_FIRE,
&gSideTimers[B_SIDE_PLAYER].seaOfFireTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SEA_OF_FIRE_PLAYER;
&gSideTimers[B_SIDE_PLAYER].seaOfFireTimer, gStartingStatuses.seaOfFirePlayer ? 0 : 4);
gStartingStatuses.seaOfFirePlayerTemporary = gStartingStatuses.seaOfFirePlayer = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SEA_OF_FIRE_OPPONENT)
else if (gStartingStatuses.seaOfFireOpponent || gStartingStatuses.seaOfFireOpponentTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_SEA_OF_FIRE,
B_SIDE_OPPONENT,
B_MSG_SET_SEA_OF_FIRE,
B_ANIM_SEA_OF_FIRE,
&gSideTimers[B_SIDE_OPPONENT].seaOfFireTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SEA_OF_FIRE_OPPONENT;
&gSideTimers[B_SIDE_OPPONENT].seaOfFireTimer, gStartingStatuses.seaOfFireOpponent ? 0 : 4);
gStartingStatuses.seaOfFireOpponentTemporary = gStartingStatuses.seaOfFireOpponent = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SWAMP_PLAYER)
else if (gStartingStatuses.swampPlayer || gStartingStatuses.swampPlayerTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_SWAMP,
B_SIDE_PLAYER,
B_MSG_SET_SWAMP,
B_ANIM_SWAMP,
&gSideTimers[B_SIDE_PLAYER].swampTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SWAMP_PLAYER;
&gSideTimers[B_SIDE_PLAYER].swampTimer, gStartingStatuses.swampPlayer ? 0 : 4);
gStartingStatuses.swampPlayerTemporary = gStartingStatuses.swampPlayer = FALSE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SWAMP_OPPONENT)
else if (gStartingStatuses.swampOpponent || gStartingStatuses.swampOpponentTemporary)
{
effect = SetStartingSideStatus(
SIDE_STATUS_SWAMP,
B_SIDE_OPPONENT,
B_MSG_SET_SWAMP,
B_ANIM_SWAMP,
&gSideTimers[B_SIDE_OPPONENT].swampTimer);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SWAMP_OPPONENT;
&gSideTimers[B_SIDE_OPPONENT].swampTimer, gStartingStatuses.swampOpponent ? 0 : 4);
gStartingStatuses.swampOpponentTemporary = gStartingStatuses.swampOpponent = FALSE;
}
// Hazards - Spikes
else if (gBattleStruct->startingStatus & STARTING_STATUS_SPIKES_PLAYER_L1)
else if (gStartingStatuses.spikesPlayerL1)
{
effect = SetStartingHazardStatus(HAZARDS_SPIKES, B_SIDE_PLAYER, 1, B_MSG_SET_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SPIKES_PLAYER_L1;
gStartingStatuses.spikesPlayerL1 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SPIKES_PLAYER_L2)
else if (gStartingStatuses.spikesPlayerL2)
{
effect = SetStartingHazardStatus(HAZARDS_SPIKES, B_SIDE_PLAYER, 2, B_MSG_SET_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SPIKES_PLAYER_L2;
gStartingStatuses.spikesPlayerL2 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SPIKES_PLAYER_L3)
else if (gStartingStatuses.spikesPlayerL3)
{
effect = SetStartingHazardStatus(HAZARDS_SPIKES, B_SIDE_PLAYER, 3, B_MSG_SET_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SPIKES_PLAYER_L3;
gStartingStatuses.spikesPlayerL3 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SPIKES_OPPONENT_L1)
else if (gStartingStatuses.spikesOpponentL1)
{
effect = SetStartingHazardStatus(HAZARDS_SPIKES, B_SIDE_OPPONENT, 1, B_MSG_SET_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SPIKES_OPPONENT_L1;
gStartingStatuses.spikesOpponentL1 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SPIKES_OPPONENT_L2)
else if (gStartingStatuses.spikesOpponentL2)
{
effect = SetStartingHazardStatus(HAZARDS_SPIKES, B_SIDE_OPPONENT, 2, B_MSG_SET_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SPIKES_OPPONENT_L2;
gStartingStatuses.spikesOpponentL2 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SPIKES_OPPONENT_L3)
else if (gStartingStatuses.spikesOpponentL3)
{
effect = SetStartingHazardStatus(HAZARDS_SPIKES, B_SIDE_OPPONENT, 3, B_MSG_SET_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SPIKES_OPPONENT_L3;
gStartingStatuses.spikesOpponentL3 = FALSE;
if (effect)
return TRUE;
}
// Hazards - Toxic Spikes
else if (gBattleStruct->startingStatus & STARTING_STATUS_TOXIC_SPIKES_PLAYER_L1)
else if (gStartingStatuses.toxicSpikesPlayerL1)
{
effect = SetStartingHazardStatus(HAZARDS_TOXIC_SPIKES, B_SIDE_PLAYER, 1, B_MSG_SET_POISON_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TOXIC_SPIKES_PLAYER_L1;
gStartingStatuses.toxicSpikesPlayerL1 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_TOXIC_SPIKES_PLAYER_L2)
else if (gStartingStatuses.toxicSpikesPlayerL2)
{
effect = SetStartingHazardStatus(HAZARDS_TOXIC_SPIKES, B_SIDE_PLAYER, 2, B_MSG_SET_POISON_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TOXIC_SPIKES_PLAYER_L2;
gStartingStatuses.toxicSpikesPlayerL2 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L1)
else if (gStartingStatuses.toxicSpikesOpponentL1)
{
effect = SetStartingHazardStatus(HAZARDS_TOXIC_SPIKES, B_SIDE_OPPONENT, 1, B_MSG_SET_POISON_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L1;
gStartingStatuses.toxicSpikesOpponentL1 = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L2)
else if (gStartingStatuses.toxicSpikesOpponentL2)
{
effect = SetStartingHazardStatus(HAZARDS_TOXIC_SPIKES, B_SIDE_OPPONENT, 2, B_MSG_SET_POISON_SPIKES);
gBattleStruct->startingStatus &= ~STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L2;
gStartingStatuses.toxicSpikesOpponentL2 = FALSE;
if (effect)
return TRUE;
}
// Hazards - Sticky Web
else if (gBattleStruct->startingStatus & STARTING_STATUS_STICKY_WEB_PLAYER)
else if (gStartingStatuses.stickyWebPlayer)
{
effect = SetStartingHazardStatus(HAZARDS_STICKY_WEB, B_SIDE_PLAYER, 1, B_MSG_SET_STICKY_WEB);
gBattleStruct->startingStatus &= ~STARTING_STATUS_STICKY_WEB_PLAYER;
gStartingStatuses.stickyWebPlayer = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_STICKY_WEB_OPPONENT)
else if (gStartingStatuses.stickyWebOpponent)
{
effect = SetStartingHazardStatus(HAZARDS_STICKY_WEB, B_SIDE_OPPONENT, 1, B_MSG_SET_STICKY_WEB);
gBattleStruct->startingStatus &= ~STARTING_STATUS_STICKY_WEB_OPPONENT;
gStartingStatuses.stickyWebOpponent = FALSE;
if (effect)
return TRUE;
}
// Hazards - Stealth Rock
else if (gBattleStruct->startingStatus & STARTING_STATUS_STEALTH_ROCK_PLAYER)
else if (gStartingStatuses.stealthRockPlayer)
{
effect = SetStartingHazardStatus(HAZARDS_STEALTH_ROCK, B_SIDE_PLAYER, 1, B_MSG_SET_STEALTH_ROCK);
gBattleStruct->startingStatus &= ~STARTING_STATUS_STEALTH_ROCK_PLAYER;
gStartingStatuses.stealthRockPlayer = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_STEALTH_ROCK_OPPONENT)
else if (gStartingStatuses.stealthRockOpponent)
{
effect = SetStartingHazardStatus(HAZARDS_STEALTH_ROCK, B_SIDE_OPPONENT, 1, B_MSG_SET_STEALTH_ROCK);
gBattleStruct->startingStatus &= ~STARTING_STATUS_STEALTH_ROCK_OPPONENT;
gStartingStatuses.stealthRockOpponent = FALSE;
if (effect)
return TRUE;
}
// Hazards - Steelsurge
else if (gBattleStruct->startingStatus & STARTING_STATUS_SHARP_STEEL_PLAYER)
else if (gStartingStatuses.sharpSteelPlayer)
{
effect = SetStartingHazardStatus(HAZARDS_STEELSURGE, B_SIDE_PLAYER, 1, B_MSG_SET_SHARP_STEEL);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SHARP_STEEL_PLAYER;
gStartingStatuses.sharpSteelPlayer = FALSE;
if (effect)
return TRUE;
}
else if (gBattleStruct->startingStatus & STARTING_STATUS_SHARP_STEEL_OPPONENT)
else if (gStartingStatuses.sharpSteelOpponent)
{
effect = SetStartingHazardStatus(HAZARDS_STEELSURGE, B_SIDE_OPPONENT, 1, B_MSG_SET_SHARP_STEEL);
gBattleStruct->startingStatus &= ~STARTING_STATUS_SHARP_STEEL_OPPONENT;
gStartingStatuses.sharpSteelOpponent = FALSE;
if (effect)
return TRUE;
}
if (effect)
{
@ -11529,3 +11555,20 @@ bool32 IsMimikyuDisguised(u32 battler)
return gBattleMons[battler].species == SPECIES_MIMIKYU_DISGUISED
|| gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED;
}
#define UNPACK_STARTING_STATUS_TO_EWRAM(_enum, _fieldName, ...) case _enum: gStartingStatuses._fieldName = TRUE; break;
void SetStartingStatus(enum StartingStatus status)
{
switch (status)
{
STARTING_STATUS_DEFINITIONS(UNPACK_STARTING_STATUS_TO_EWRAM);
}
}
#define UNPACK_STARTING_STATUS_RESET(_enum, _fieldName, ...) gStartingStatuses._fieldName = FALSE;
void ResetStartingStatuses(void)
{
STARTING_STATUS_DEFINITIONS(UNPACK_STARTING_STATUS_RESET);
}

View File

@ -2,6 +2,7 @@
#include "overworld.h"
#include "battle_pyramid.h"
#include "battle_setup.h"
#include "battle_util.h"
#include "berry.h"
#include "bg.h"
#include "cable_club.h"
@ -418,17 +419,7 @@ void Overworld_ResetStateAfterDigEscRope(void)
#if B_RESET_FLAGS_VARS_AFTER_WHITEOUT == TRUE
void Overworld_ResetBattleFlagsAndVars(void)
{
#if B_VAR_STARTING_STATUS != 0
VarSet(B_VAR_STARTING_STATUS, 0);
#endif
#if B_VAR_STARTING_STATUS_HAZARDS != 0
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
#endif
#if B_VAR_STARTING_STATUS_TIMER != 0
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
#endif
ResetStartingStatuses();
#if B_VAR_WILD_AI_FLAGS != 0
VarSet(B_VAR_WILD_AI_FLAGS,0);

View File

@ -1,6 +1,7 @@
#include "global.h"
#include "frontier_util.h"
#include "battle_setup.h"
#include "battle_util.h"
#include "berry.h"
#include "clock.h"
#include "coins.h"
@ -3291,3 +3292,12 @@ bool8 ScrCmd_istmrelearneractive(struct ScriptContext *ctx)
return FALSE;
}
bool8 ScrCmd_setstartingstatus(struct ScriptContext *ctx)
{
enum StartingStatus status = ScriptReadByte(ctx);
SetStartingStatus(status);
return FALSE;
}

View File

@ -2,18 +2,15 @@
#include "event_data.h"
#include "test/battle.h"
#if B_VAR_STARTING_STATUS_HAZARDS != 0
SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS_HAZARDS can start Spikes on the opposing side", s16 damage)
SINGLE_BATTLE_TEST("SetStartingStatus can start Spikes on the opposing side", s16 damage)
{
u32 startingHazard;
u16 startingHazard;
u32 divisor;
PARAMETRIZE { startingHazard = STARTING_HAZARD_SPIKES_OPPONENT_L1; divisor = 8; }
PARAMETRIZE { startingHazard = STARTING_HAZARD_SPIKES_OPPONENT_L3; divisor = 4; }
PARAMETRIZE { startingHazard = STARTING_STATUS_SPIKES_OPPONENT_L1; divisor = 8; }
PARAMETRIZE { startingHazard = STARTING_STATUS_SPIKES_OPPONENT_L3; divisor = 4; }
VarSet(B_VAR_STARTING_STATUS_HAZARDS, startingHazard);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(startingHazard);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -27,14 +24,13 @@ SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS_HAZARDS can start Spikes on the opposi
HP_BAR(opponent, damage: maxHP / divisor);
MESSAGE("The opposing Wynaut was hurt by the spikes!");
} FINALLY {
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
ResetStartingStatuses();
}
}
SINGLE_BATTLE_TEST("Starting Toxic Spikes poison the opposing switch-in")
{
VarSet(B_VAR_STARTING_STATUS_HAZARDS, STARTING_HAZARD_TOXIC_SPIKES_OPPONENT_L1);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L1);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -48,14 +44,13 @@ SINGLE_BATTLE_TEST("Starting Toxic Spikes poison the opposing switch-in")
STATUS_ICON(opponent, poison: TRUE);
NOT STATUS_ICON(opponent, badPoison: TRUE);
} THEN {
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
ResetStartingStatuses();
}
}
SINGLE_BATTLE_TEST("Starting Toxic Spikes badly poison the opposing switch-in")
{
VarSet(B_VAR_STARTING_STATUS_HAZARDS, STARTING_HAZARD_TOXIC_SPIKES_OPPONENT_L2);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(STARTING_STATUS_TOXIC_SPIKES_OPPONENT_L2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -68,14 +63,13 @@ SINGLE_BATTLE_TEST("Starting Toxic Spikes badly poison the opposing switch-in")
MESSAGE("The opposing Wynaut was badly poisoned!");
STATUS_ICON(opponent, badPoison: TRUE);
} THEN {
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
ResetStartingStatuses();
}
}
SINGLE_BATTLE_TEST("Starting Sticky Web lowers Speed on entry")
{
VarSet(B_VAR_STARTING_STATUS_HAZARDS, STARTING_HAZARD_STICKY_WEB_OPPONENT);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(STARTING_STATUS_STICKY_WEB_OPPONENT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -88,14 +82,13 @@ SINGLE_BATTLE_TEST("Starting Sticky Web lowers Speed on entry")
MESSAGE("The opposing Wynaut was caught in a sticky web!");
MESSAGE("The opposing Wynaut's Speed fell!");
} THEN {
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
ResetStartingStatuses();
}
}
SINGLE_BATTLE_TEST("Starting Stealth Rock damages the opposing switch-in")
{
VarSet(B_VAR_STARTING_STATUS_HAZARDS, STARTING_HAZARD_STEALTH_ROCK_OPPONENT);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(STARTING_STATUS_STEALTH_ROCK_OPPONENT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -109,14 +102,13 @@ SINGLE_BATTLE_TEST("Starting Stealth Rock damages the opposing switch-in")
HP_BAR(opponent, damage: maxHP / 2);
MESSAGE("Pointed stones dug into the opposing Charizard!");
} THEN {
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
ResetStartingStatuses();
}
}
SINGLE_BATTLE_TEST("Starting sharp steel damages the opposing switch-in")
{
VarSet(B_VAR_STARTING_STATUS_HAZARDS, STARTING_HAZARD_SHARP_STEEL_OPPONENT);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(STARTING_STATUS_SHARP_STEEL_OPPONENT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -130,8 +122,6 @@ SINGLE_BATTLE_TEST("Starting sharp steel damages the opposing switch-in")
HP_BAR(opponent, damage: maxHP / 4);
MESSAGE("The sharp steel bit into the opposing Sylveon!");
} THEN {
VarSet(B_VAR_STARTING_STATUS_HAZARDS, 0);
ResetStartingStatuses();
}
}
#endif // B_VAR_STARTING_STATUS_HAZARDS

View File

@ -2,9 +2,7 @@
#include "event_data.h"
#include "test/battle.h"
#if B_VAR_STARTING_STATUS != 0
SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS starts a chosen terrain at the beginning of battle and lasts infinitely long")
SINGLE_BATTLE_TEST("SetStartingStatus starts a chosen terrain at the beginning of battle and lasts infinitely long if it's defined as such")
{
u16 terrain;
@ -12,9 +10,9 @@ SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS starts a chosen terrain at the beginni
PARAMETRIZE { terrain = STARTING_STATUS_PSYCHIC_TERRAIN; }
PARAMETRIZE { terrain = STARTING_STATUS_MISTY_TERRAIN; }
PARAMETRIZE { terrain = STARTING_STATUS_ELECTRIC_TERRAIN; }
PARAMETRIZE { terrain = STARTING_STATUS_ELECTRIC_TERRAIN_TEMPORARY; }
VarSet(B_VAR_STARTING_STATUS, terrain);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(terrain);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -45,15 +43,20 @@ SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS starts a chosen terrain at the beginni
break;
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
MESSAGE("The weirdness disappeared from the battlefield!");
if (terrain != STARTING_STATUS_ELECTRIC_TERRAIN_TEMPORARY) {
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
MESSAGE("The weirdness disappeared from the battlefield!");
MESSAGE("The electricity disappeared from the battlefield.");
MESSAGE("The mist disappeared from the battlefield.");
MESSAGE("The grass disappeared from the battlefield.");
}
} else {
MESSAGE("The electricity disappeared from the battlefield.");
MESSAGE("The mist disappeared from the battlefield.");
MESSAGE("The grass disappeared from the battlefield.");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
}
} THEN {
VarSet(B_VAR_STARTING_STATUS, 0);
ResetStartingStatuses();
}
}
@ -64,8 +67,7 @@ SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts
PARAMETRIZE { viaMove = TRUE; }
PARAMETRIZE { viaMove = FALSE; }
VarSet(B_VAR_STARTING_STATUS, STARTING_STATUS_ELECTRIC_TERRAIN);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
SetStartingStatus(STARTING_STATUS_ELECTRIC_TERRAIN);
GIVEN {
PLAYER(SPECIES_TAPU_BULU) { Ability(viaMove == TRUE ? ABILITY_TELEPATHY : ABILITY_GRASSY_SURGE); }
@ -107,8 +109,6 @@ SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts
MESSAGE("The grass disappeared from the battlefield.");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
} THEN {
VarSet(B_VAR_STARTING_STATUS, 0);
ResetStartingStatuses();
}
}
#endif // B_VAR_STARTING_STATUS

View File

@ -235,7 +235,9 @@ static u32 BattleTest_EstimateCost(void *data)
const struct BattleTest *test = data;
memset(STATE, 0, sizeof(*STATE));
STATE->runRandomly = TRUE;
ResetStartingStatuses();
InvokeTestFunction(test);
ResetStartingStatuses();
cost = 1;
if (STATE->parametersCount != 0)
cost *= STATE->parametersCount;

View File

@ -19,7 +19,7 @@
#define PARTY_SIZE 255
#define MAX_MON_MOVES 4
#define MAX_MON_TAGS 32
#define STARTING_STATUS_COUNT 32
#define STARTING_STATUS_COUNT 64
struct String
{
@ -1663,6 +1663,40 @@ static void fprint_constant(FILE *f, const char *prefix, struct String s)
}
}
static void fprint_symbol(FILE *f, struct String s)
{
if (s.string_n > 0)
{
bool upper = false;
for (int i = 0; i < s.string_n; i++)
{
unsigned char c = s.string[i];
if ('A' <= c && c <= 'Z')
{
if (upper)
{
fputc(c, f);
upper = false;
continue;
}
fputc(c + 'a' - 'A', f);
}
else if ('a' <= c && c <= 'z')
fputc(c, f);
else if ('0' <= c && c <= '9')
fputc(c, f);
else if (c == '\'')
;
else
upper = true;
}
}
else
{
fprintf(f, "NONE");
}
}
// This is a really stupid helper for 'fprint_species'.
static bool is_utf8_character(struct String s, int *i, const unsigned char *utf8)
{
@ -1858,14 +1892,14 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par
if (trainer->starting_status_n > 0)
{
fprintf(f, "#line %d\n", trainer->starting_status_line);
fprintf(f, " .startingStatus = ");
fprintf(f, " .startingStatus = { ");
for (int i = 0; i < trainer->starting_status_n; i++)
{
if (i > 0)
fprintf(f, " | ");
fprint_constant(f, "STARTING_STATUS", trainer->starting_status[i]);
fprintf(f, ".");
fprint_symbol(f, trainer->starting_status[i]);
fprintf(f, " = TRUE, ");
}
fprintf(f, ",\n");
fprintf(f, "},\n");
}
if (!is_empty_string(trainer->pool_rules))