Skip to content

Commit

Permalink
Merge pull request #112 from lhearachel/battle-dev
Browse files Browse the repository at this point in the history
Document BattleSystem_CanUseMove and associated sub-functions
  • Loading branch information
lhearachel authored Nov 11, 2023
2 parents c30b9cb + bbbec7a commit f816d09
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 237 deletions.
4 changes: 2 additions & 2 deletions include/battle/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
#define MON_HAS_TYPE(mon, type) (BattleMon_Get(battleCtx, mon, BATTLEMON_TYPE_1, NULL) == type || BattleMon_Get(battleCtx, mon, BATTLEMON_TYPE_2, NULL) == type)
#define MON_IS_NOT_TYPE(mon, type) (BattleMon_Get(battleCtx, mon, BATTLEMON_TYPE_1, NULL) != type && BattleMon_Get(battleCtx, mon, BATTLEMON_TYPE_2, NULL) != type)

#define NO_TARGET_SINGLE_TURN (battleCtx->defender == BATTLER_NONE && BattleMove_IsMultiTurn(battleCtx, battleCtx->moveCur) == FALSE)
#define NO_TARGET_SINGLE_TURN (battleCtx->defender == BATTLER_NONE && Move_IsMultiTurn(battleCtx, battleCtx->moveCur) == FALSE)
#define NO_TARGET_MULTI_TURN (battleCtx->defender == BATTLER_NONE \
&& BattleMove_IsMultiTurn(battleCtx, battleCtx->moveCur) == TRUE \
&& Move_IsMultiTurn(battleCtx, battleCtx->moveCur) == TRUE \
&& ( \
(ATTACKING_MON.statusVolatile & VOLATILE_CONDITION_MOVE_LOCKED) \
|| (battleCtx->battleStatusMask & SYSCTL_LAST_OF_MULTI_TURN) \
Expand Down
21 changes: 11 additions & 10 deletions include/constants/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,17 @@
#define BATTLE_STATUS_GIRATINA (1 << 6)
#define BATTLE_STATUS_DISTORTION (1 << 7)

#define STRUGGLE_CHECK_NO_MOVES (1 << 0)
#define STRUGGLE_CHECK_NO_PP (1 << 1)
#define STRUGGLE_CHECK_DISABLED (1 << 2)
#define STRUGGLE_CHECK_TORMENTED (1 << 3)
#define STRUGGLE_CHECK_TAUNTED (1 << 4)
#define STRUGGLE_CHECK_IMPRISONED (1 << 5)
#define STRUGGLE_CHECK_GRAVITY (1 << 6)
#define STRUGGLE_CHECK_HEAL_BLOCKED (1 << 7)
#define STRUGGLE_CHECK_CHOICED (1 << 9)
#define STRUGGLE_CHECK_ALL ~0
#define CHECK_INVALID_NO_MOVE (1 << 0)
#define CHECK_INVALID_NO_PP (1 << 1)
#define CHECK_INVALID_DISABLED (1 << 2)
#define CHECK_INVALID_TORMENTED (1 << 3)
#define CHECK_INVALID_TAUNTED (1 << 4)
#define CHECK_INVALID_IMPRISONED (1 << 5)
#define CHECK_INVALID_GRAVITY (1 << 6)
#define CHECK_INVALID_HEAL_BLOCK (1 << 7)
#define CHECK_INVALID_ENCORE (1 << 8) // this one is only implicit
#define CHECK_INVALID_CHOICE_ITEM (1 << 9)
#define CHECK_INVALID_ALL ~0

#define STRUGGLING_MOVE_1 (1 << 0)
#define STRUGGLING_MOVE_2 (1 << 1)
Expand Down
140 changes: 131 additions & 9 deletions include/overlay016/ov16_0225177C.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,54 @@ void BattleSystem_UpdateAfterSwitch(BattleSystem *battleSys, BattleContext *batt
*/
void BattleSystem_CleanupFaintedMon(BattleSystem *battleSys, BattleContext *battleCtx, int battler);
void BattleSystem_SetupNextTurn(BattleSystem * param0, BattleContext * param1);
int BattleSystem_CheckStruggling(BattleSystem * param0, BattleContext * param1, int param2, int param3, int param4);

/**
* @brief Compute which moves are invalid for the battler to use, performing
* checks according to the input opcode mask.
*
* The following op-codes are supported in the input mask:
* - CHECK_INVALID_NO_MOVE -> flag empty move-slots
* - CHECK_INVALID_NO_PP -> flag moves with no remaining PP
* - CHECK_INVALID_DISABLED -> flag the battler's Disabled move
* - CHECK_INVALID_TORMENTED -> flag the battler's last-used move
* - CHECK_INVALID_TAUNTED -> flag any of the battler's moves with 0 power
* - CHECK_INVALID_IMPRISONED -> flag any of the battler's Imprisoned moves
* - CHECK_INVALID_GRAVITY -> flag any of the battler's moves which fail in high gravity
* - CHECK_INVALID_HEAL_BLOCK -> flag any of the battler's healing moves
* - CHECK_INVALID_CHOICE_ITEM -> flag any of the battler's moves other than their choice-locked move
*
* @param battleSys
* @param battleCtx
* @param battler The battler choosing their move
* @param invalidMoves An initial mask of moves which should be deemed invalid
* @param opMask An input opcode mask determining which checks should be
* made for move validity
* @return A bitmask denoting which of the battler's move slots are invalid for
* selection
*/
int BattleSystem_CheckInvalidMoves(BattleSystem *battleSys, BattleContext *battleCtx, int battler, int invalidMoves, int opMask);

/**
* @brief Determine if a battler can use the move in the given slot, populating
* the input BattleMessage struct with a message to display if they cannot.
*
* @param battleSys
* @param battleCtx
* @param battler
* @param moveSlot The slot of the move that the battler is trying to use
* @param[out] msgOut An output message for display to the player
* @return TRUE if the move can be used, FALSE if it cannot
*/
BOOL BattleSystem_CanUseMove(BattleSystem *battleSys, BattleContext *battleCtx, int battler, int moveSlot, BattleMessage *msgOut);
int Battler_SlotForMove(BattleMon * param0, u16 param1);

/**
* @brief Determine which slot of the Pokemon's moveset contains a given move.
*
* @param mon
* @param move
* @return The slot of the Pokemon's moveset containing the input move
*/
int Battler_SlotForMove(BattleMon *mon, u16 move);

/**
* @brief Apply type-chart effectiveness for a given move against its target.
Expand Down Expand Up @@ -523,7 +568,16 @@ u16 Battler_SelectedMove(BattleContext * param0, int param1);
* be a mask of the battlers matching the criteria.
*/
int BattleSystem_CountAbility(BattleSystem *battleSys, BattleContext *battleCtx, enum CountAbilityMode mode, int battler, int ability);
BOOL BattleMove_IsMultiTurn(BattleContext * param0, int param1);

/**
* @brief Determine if a given move is a multi-turn move.
*
* @param battleCtx
* @param move
* @return TRUE if the move is a multi-turn move (one which has a charging
* turn); FALSE if not
*/
BOOL Move_IsMultiTurn(BattleContext *battleCtx, int move);

/**
* @brief Access a particular entry in the type-matchup table.
Expand Down Expand Up @@ -571,7 +625,17 @@ int BattleSystem_TypeMatchupMultiplier(u8 attackingType, u8 defendingType1, u8 d
* @return TRUE if the move is invoker-class, FALSE if not.
*/
BOOL Move_IsInvoker(u16 move);
BOOL BattleSystem_IsGhostCurse(BattleContext * param0, u16 param1, int param2);

/**
* @brief Check if a given move is Curse being used by a Ghost-type Pokemon.
*
* @param battleCtx
* @param move
* @param battler
* @return TRUE if the move is Curse and the battler has the Ghost typing;
* FALSE otherwise
*/
BOOL Move_IsGhostCurse(BattleContext *battleCtx, u16 move, int battler);

/**
* @brief Determine if a battler's item can be stolen.
Expand Down Expand Up @@ -600,7 +664,16 @@ BOOL BattleSystem_NotHoldingMail(BattleContext * param0, int param1);
* @return TRUE if Whirlwind should succeed, FALSE if it should fail.
*/
BOOL BattleSystem_CanWhirlwind(BattleSystem *battleSys, BattleContext *battleCtx);
u8 Battler_Ability(BattleContext * param0, int param1);

/**
* @brief Get the battler's ability, accounting for disrupting effects on itself,
* e.g. Gastro Acid, Gravity, and Ingrain.
*
* @param battleCtx
* @param battler
* @return The battler's ability
*/
u8 Battler_Ability(BattleContext *battleCtx, int battler);

/**
* @brief Check if the given defender has the specified ability, treating it as
Expand Down Expand Up @@ -634,7 +707,19 @@ BOOL BattleSystem_AnyReplacementMons(BattleSystem *battleSys, BattleContext *bat
BOOL BattleSystem_Trapped(BattleSystem * param0, BattleContext * param1, int param2, BattleMessage * param3);
BOOL BattleSystem_TryEscape(BattleSystem * param0, BattleContext * param1, int param2);
BOOL Battler_CheckTruant(BattleContext * param0, int param1);
BOOL BattleSystem_Imprisoned(BattleSystem * param0, BattleContext * param1, int param2, int param3);

/**
* @brief Checks if a given move has been Imprisoned by one of the battler's
* opponents.
*
* @param battleSys
* @param battleCtx
* @param battler The battler trying to execute a move.
* @param move The move to be executed.
* @return TRUE if any of the battler's opponents has used Imprison and also
* knows the requested move; FALSE otherwise.
*/
BOOL Move_Imprisoned(BattleSystem *battleSys, BattleContext *battleCtx, int battler, int move);

/**
* @brief Check if any active battlers are flagged as having the given move
Expand All @@ -649,8 +734,31 @@ BOOL BattleSystem_Imprisoned(BattleSystem * param0, BattleContext * param1, int
BOOL BattleSystem_AnyBattlersWithMoveEffect(BattleSystem *battleSys, BattleContext *battleCtx, int effectMask);
void BattleSystem_SetupLoop(BattleSystem *battleSys, BattleContext *battleCtx);
void BattleSystem_SortMonsBySpeed(BattleSystem * param0, BattleContext * param1);
BOOL BattleSystem_FailsInHighGravity(BattleSystem * param0, BattleContext * param1, int param2, int param3);
BOOL BattleSystem_HealBlocked(BattleSystem * param0, BattleContext * param1, int param2, int param3);

/**
* @brief Check if a given move should fail due to high gravity conditions.
*
* @param battleSys
* @param battleCtx
* @param battler
* @param move The move to be executed
* @return TRUE if the field is under high gravity conditions AND the move
* should fail in such conditions; FALSE otherwise
*/
BOOL Move_FailsInHighGravity(BattleSystem *battleSys, BattleContext *battleCtx, int battler, int move);

/**
* @brief Check if a given move should fail due to the battler being afflicted
* by Heal Block.
*
* @param battleSys
* @param battleCtx
* @param battler The battler trying to execute a move
* @param move The move to be executed
* @return TRUE if the battler is afflicted with Heal Block AND the move should
* fail due to such afflicition; FALSE otherwise
*/
BOOL Move_HealBlocked(BattleSystem *battleSys, BattleContext *battleCtx, int battler, int move);
void BattleSystem_UpdateLastResort(BattleSystem * param0, BattleContext * param1);

/**
Expand Down Expand Up @@ -1134,7 +1242,21 @@ void BattleSystem_SortMonsInTrickRoom(BattleSystem * param0, BattleContext * par
* @return TRUE if the status effect should be shown, FALSE otherwise.
*/
BOOL BattleSystem_ShouldShowStatusEffect(BattleContext *battleCtx, int battler, int status);
BOOL BattleSystem_TriggerHeldItemOnPivotMove(BattleSystem * param0, BattleContext * param1, int * param2);

/**
* @brief Trigger a held item's effect after a pivoting move has been used.
*
* This is meant to support triggering effects such as Shell Bell, Life Orb,
* and Sticky Barb transferral before the user of a pivoting move switches out.
*
* @param battleSys
* @param battleCtx
* @param[out] subscript Return param for the subscript to load for a
* triggered effect.
* @return TRUE if a subscript should be loaded for a triggered effect,
* FALSE otherwise.
*/
BOOL BattleSystem_TriggerHeldItemOnPivotMove(BattleSystem *battleSys, BattleContext *battleCtx, int *subscript);
void BattleSystem_DecPPForPressure(BattleContext * param0, int param1, int param2);
BOOL BattleSystem_RecordingStopped(BattleSystem * param0, BattleContext * param1);

Expand Down
4 changes: 2 additions & 2 deletions src/overlay014/ov14_0221FC20.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ void ov14_0221FC20 (BattleSystem * param0, BattleContext * param1, u8 param2, u8
param3 = param3 >> 1;
}

v1 = BattleSystem_CheckStruggling(param0, param1, param2, 0, 0xffffffff);
v1 = BattleSystem_CheckInvalidMoves(param0, param1, param2, 0, CHECK_INVALID_ALL);

for (v0 = 0; v0 < LEARNED_MOVES_MAX; v0++) {
if (v1 & FlagIndex(v0)) {
Expand Down Expand Up @@ -541,7 +541,7 @@ static u8 ov14_0221FE38 (BattleSystem * param0, BattleContext * param1)
}

if (v7 == 174) {
if (BattleSystem_IsGhostCurse(param1, v7, param1->aiContext.attacker) == 0) {
if (Move_IsGhostCurse(param1, v7, param1->aiContext.attacker) == 0) {
param1->aiContext.selectedTarget[param1->aiContext.attacker] = param1->aiContext.attacker;
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/overlay016/battle_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ static void BattleController_CommandSelectionInput(BattleSystem *battleSys, Batt

switch (BattleContext_IOBufferVal(battleCtx, i)) {
case PLAYER_INPUT_FIGHT:
if (BattleSystem_CheckStruggling(battleSys, battleCtx, i, 0, STRUGGLE_CHECK_ALL) == STRUGGLING_ALL) {
if (BattleSystem_CheckInvalidMoves(battleSys, battleCtx, i, 0, CHECK_INVALID_ALL) == STRUGGLING_ALL) {
// Don't let the player select a move if they are out of PP on all moves
battleCtx->turnFlags[i].struggling = TRUE;

Expand Down Expand Up @@ -2169,9 +2169,9 @@ static int BattleController_CheckObedience(BattleSystem *battleSys, BattleContex

rand1 = ((BattleSystem_RandNext(battleSys) & 0xFF) * (ATTACKING_MON.level + maxLevel)) >> 8;
if (rand1 < maxLevel) {
rand1 = BattleSystem_CheckStruggling(battleSys, battleCtx, battleCtx->attacker, FlagIndex(ATTACKER_MOVE_SLOT), STRUGGLE_CHECK_ALL);
rand1 = BattleSystem_CheckInvalidMoves(battleSys, battleCtx, battleCtx->attacker, FlagIndex(ATTACKER_MOVE_SLOT), CHECK_INVALID_ALL);

if (rand1 == 0xF) {
if (rand1 == STRUGGLING_ALL) {
*nextSeq = BATTLE_SUBSEQ_DISOBEY_DO_NOTHING;
return OBEY_CHECK_DO_NOTHING;
}
Expand Down Expand Up @@ -2325,7 +2325,7 @@ static BOOL BattleController_HasNoTarget(BattleSystem *battleSys, BattleContext

// Don't alter the target for charge-turn moves that are just charging up this turn
if (battleCtx->defender == BATTLER_NONE
&& BattleMove_IsMultiTurn(battleCtx, battleCtx->moveCur) == TRUE
&& Move_IsMultiTurn(battleCtx, battleCtx->moveCur) == TRUE
&& result == FALSE
&& solarMove == FALSE
&& Battler_HeldItemEffect(battleCtx, battleCtx->attacker) != HOLD_EFFECT_CHARGE_SKIP
Expand Down Expand Up @@ -2570,7 +2570,7 @@ static BOOL BattleController_CheckStatusDisruption(BattleSystem *battleSys, Batt
break;

case CHECK_STATUS_STATE_IMPRISON:
if (BattleSystem_Imprisoned(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur)) {
if (Move_Imprisoned(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur)) {
battleCtx->moveFailFlags[battleCtx->attacker].imprisoned = TRUE;

LOAD_SUBSEQ(BATTLE_SUBSEQ_MOVE_IS_IMPRISONED);
Expand All @@ -2584,7 +2584,7 @@ static BOOL BattleController_CheckStatusDisruption(BattleSystem *battleSys, Batt
break;

case CHECK_STATUS_STATE_GRAVITY:
if (BattleSystem_FailsInHighGravity(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur)) {
if (Move_FailsInHighGravity(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur)) {
battleCtx->moveFailFlags[battleCtx->attacker].gravity = TRUE;

LOAD_SUBSEQ(BATTLE_SUBSEQ_MOVE_FAIL_GRAVITY);
Expand All @@ -2598,7 +2598,7 @@ static BOOL BattleController_CheckStatusDisruption(BattleSystem *battleSys, Batt
break;

case CHECK_STATUS_STATE_HEAL_BLOCK:
if (BattleSystem_HealBlocked(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur)) {
if (Move_HealBlocked(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveCur)) {
battleCtx->moveFailFlags[battleCtx->attacker].healBlocked = TRUE;

LOAD_SUBSEQ(BATTLE_SUBSEQ_MOVE_IS_HEAL_BLOCKED);
Expand Down Expand Up @@ -3007,8 +3007,8 @@ static int BattleController_CheckMoveHitOverrides(BattleSystem *battleSys, Battl

if (battleCtx->turnFlags[defender].protecting
&& (MOVE_DATA(move).flags & MOVE_FLAG_CAN_PROTECT)
&& (move != MOVE_CURSE || BattleSystem_IsGhostCurse(battleCtx, move, attacker) == TRUE) // Ghost-Curse can be Protected
&& (BattleMove_IsMultiTurn(battleCtx, move) == FALSE || (battleCtx->battleStatusMask & SYSCTL_LAST_OF_MULTI_TURN))) {
&& (move != MOVE_CURSE || Move_IsGhostCurse(battleCtx, move, attacker) == TRUE) // Ghost-Curse can be Protected
&& (Move_IsMultiTurn(battleCtx, move) == FALSE || (battleCtx->battleStatusMask & SYSCTL_LAST_OF_MULTI_TURN))) {
Battler_UnlockMoveChoice(battleSys, battleCtx, attacker);
battleCtx->moveStatusFlags |= MOVE_STATUS_PROTECTED;
return 0;
Expand Down Expand Up @@ -4348,7 +4348,7 @@ static BOOL BattleController_CheckBattleOver(BattleSystem * battleSys, BattleCon
static BOOL BattleController_MustSelectTarget(BattleSystem *battleSys, BattleContext *battleCtx, u8 battler, u32 battleType, int *range, int moveSlot, u32 *target)
{
if (battleCtx->battleMons[battler].moves[moveSlot] == MOVE_CURSE
&& BattleSystem_IsGhostCurse(battleCtx, battleCtx->battleMons[battler].moves[moveSlot], battler) == FALSE) {
&& Move_IsGhostCurse(battleCtx, battleCtx->battleMons[battler].moves[moveSlot], battler) == FALSE) {
*range = RANGE_USER;
} else {
*range = MOVE_DATA(battleCtx->battleMons[battler].moves[moveSlot]).range;
Expand Down
6 changes: 3 additions & 3 deletions src/overlay016/battle_script.c
Original file line number Diff line number Diff line change
Expand Up @@ -5011,7 +5011,7 @@ static BOOL BtlCmd_TryConversion2(BattleSystem *battleSys, BattleContext *battle
if (battleCtx->conversion2Move[battleCtx->attacker]
&& battleCtx->conversion2Battler[battleCtx->attacker] != BATTLER_NONE) {
// Fail to execute if the source move's owner is locked into the first turn of a multi-turn move
if (BattleMove_IsMultiTurn(battleCtx, battleCtx->conversion2Move[battleCtx->attacker])
if (Move_IsMultiTurn(battleCtx, battleCtx->conversion2Move[battleCtx->attacker])
&& (battleCtx->battleMons[battleCtx->conversion2Battler[battleCtx->attacker]].statusVolatile & VOLATILE_CONDITION_MOVE_LOCKED)) {
BattleScript_Iter(battleCtx, jumpOnFail);
return FALSE;
Expand Down Expand Up @@ -5154,13 +5154,13 @@ static BOOL BtlCmd_TrySleepTalk(BattleSystem *battleSys, BattleContext *battleCt
|| ATTACKING_MON.moves[i] == MOVE_FOCUS_PUNCH
|| ATTACKING_MON.moves[i] == MOVE_UPROAR
|| ATTACKING_MON.moves[i] == MOVE_CHATTER
|| BattleMove_IsMultiTurn(battleCtx, ATTACKING_MON.moves[i])) {
|| Move_IsMultiTurn(battleCtx, ATTACKING_MON.moves[i])) {
invalidMovesMask |= FlagIndex(i);
}
}

// Check for other invalid moves (only skip the PP check)
invalidMovesMask = BattleSystem_CheckStruggling(battleSys, battleCtx, battleCtx->attacker, invalidMovesMask, ~STRUGGLE_CHECK_NO_PP);
invalidMovesMask = BattleSystem_CheckInvalidMoves(battleSys, battleCtx, battleCtx->attacker, invalidMovesMask, ~CHECK_INVALID_NO_PP);
if (invalidMovesMask == STRUGGLING_ALL) {
BattleScript_Iter(battleCtx, jumpOnFail);
} else {
Expand Down
Loading

0 comments on commit f816d09

Please sign in to comment.