From d07252f5c5b8d1f98f0f74585e9bfcf22d421ee9 Mon Sep 17 00:00:00 2001 From: lhearachel Date: Tue, 10 Oct 2023 23:41:45 -0700 Subject: [PATCH 1/3] Document BattleSystem_Defender, RandomOpponent, and EnemyInSlot --- include/overlay016/ov16_0223DF00.h | 18 +- include/overlay016/ov16_0225177C.h | 37 +++- src/overlay016/battle_controller.c | 14 +- src/overlay016/battle_script.c | 4 +- src/overlay016/ov16_0223DF00.c | 26 +-- src/overlay016/ov16_0225177C.c | 284 +++++++++++++---------------- src/overlay016/ov16_0226485C.c | 2 +- 7 files changed, 199 insertions(+), 186 deletions(-) diff --git a/include/overlay016/ov16_0223DF00.h b/include/overlay016/ov16_0223DF00.h index 7b24e6029d..1159b49e58 100644 --- a/include/overlay016/ov16_0223DF00.h +++ b/include/overlay016/ov16_0223DF00.h @@ -36,6 +36,9 @@ #include "enums.h" +#define ENEMY_IN_SLOT_RIGHT 0 +#define ENEMY_IN_SLOT_LEFT 2 + UnkStruct_02018340 * ov16_0223DF00(BattleSystem * param0); UnkStruct_0205AA50 * ov16_0223DF04(BattleSystem * param0, int param1); u32 BattleSystem_BattleType(BattleSystem * param0); @@ -120,7 +123,20 @@ int ov16_0223E22C(BattleSystem * param0); int ov16_0223E240(BattleSystem * param0); int BattleSystem_MapHeader(BattleSystem * param0); int BattleSystem_Partner(BattleSystem * param0, int param1); -int ov16_0223E2A4(BattleSystem * param0, int param1, int param2); + +/** + * @brief Get the battler who is an enemy of the input attacker and occupies + * a particular slot on the opposing team. + * + * If the battle is a double-battle, then this will only ever return the + * singular opponent. + * + * @param battleSys + * @param attacker + * @param slot + * @return Battler who is an enemy and in the given slot on the enemy team. + */ +int BattleSystem_EnemyInSlot(BattleSystem *battleSys, int attacker, int slot); BOOL ov16_0223E30C(BattleSystem * param0, int param1, int param2, int param3, int param4); u32 BattleSystem_BattleStatus(BattleSystem * param0); int ov16_0223EBF8(BattleSystem * param0); diff --git a/include/overlay016/ov16_0225177C.h b/include/overlay016/ov16_0225177C.h index 96f610a899..a29f0fa1a6 100644 --- a/include/overlay016/ov16_0225177C.h +++ b/include/overlay016/ov16_0225177C.h @@ -208,7 +208,26 @@ void BattleSystem_NoExpGain(BattleContext * param0, int param1); void BattleSystem_FlagExpGain(BattleSystem * param0, BattleContext * param1, int param2); BOOL BattleSystem_CheckPrimaryEffect(BattleSystem * param0, BattleContext * param1, int * param2); BOOL BattleSystem_TriggerSecondaryEffect(BattleSystem *battleSys, BattleContext *battleCtx, int *nextSeq); -int BattleSystem_Defender(BattleSystem * param0, BattleContext * param1, int param2, u16 param3, int param4, int param5); + +/** + * @brief Find the defender for the move. + * + * The defender can be chosen at random in certain scenarios, but only while + * the randomize parameter is TRUE: + * + * 1. The move's range is Me First + * 2. The move's range is Acupressure + * 3. The move is a standard single-target attack, e.g., Thunderbolt + * + * @param battleSys + * @param battleCtx + * @param attacker + * @param move The move being used + * @param randomize If TRUE, permit randomization of target-selection for certain moves + * @param inRange An input range to prefer for the move over whatever range is set on the move data + * @return The chosen defender for the move + */ +int BattleSystem_Defender(BattleSystem *battleSys, BattleContext *battleCtx, int attacker, u16 move, BOOL randomize, int inRange); void BattleSystem_RedirectTarget(BattleSystem * param0, BattleContext * param1, int param2, u16 param3); BOOL BattleMove_TriggerRedirectionAbilities(BattleSystem * param0, BattleContext * param1); void BattleMon_CopyToParty(BattleSystem * param0, BattleContext * param1, int param2); @@ -335,7 +354,21 @@ int BattleSystem_CheckImmunityAbilities(BattleContext * param0, int param1, int BOOL BattleSystem_TriggerTurnEndAbility(BattleSystem * param0, BattleContext * param1, int param2); int BattleSystem_Divide(int param0, int param1); int BattleSystem_ShowMonChecks(BattleSystem * param0, BattleContext * param1); -int BattleSystem_RandomOpponent(BattleSystem * param0, BattleContext * param1, int param2); + +/** + * @brief Pick a random opponent for the given attacker. + * + * In a single-battle, this will always return the lone opponent. + * + * In a double-battle, if one of the two opponents has fainted, then the other + * opponent will always be chosen. + * + * @param battleSys + * @param battleCtx + * @param attacker + * @return A random opponent of the attacker's. + */ +int BattleSystem_RandomOpponent(BattleSystem *battleSys, BattleContext *battleCtx, int attacker); BOOL BattleSystem_TriggerAbilityOnHit(BattleSystem *battleSys, BattleContext *battleCtx, int *nextSeq); BOOL BattleSystem_RecoverStatusByAbility(BattleSystem * param0, BattleContext * param1, int param2, int param3); BOOL ov16_022577A4(BattleContext * param0, int param1, int param2); diff --git a/src/overlay016/battle_controller.c b/src/overlay016/battle_controller.c index 9cb7777d0f..7ed58f5446 100644 --- a/src/overlay016/battle_controller.c +++ b/src/overlay016/battle_controller.c @@ -1901,17 +1901,17 @@ static void BattleController_TurnEnd(BattleSystem *battleSys, BattleContext *bat static void BattleController_FightCommand(BattleSystem *battleSys, BattleContext *battleCtx) { - BOOL overrideMoveChoice = FALSE; + BOOL randomizeTarget = FALSE; battleCtx->attacker = battleCtx->battlerActionOrder[battleCtx->turnOrderCounter]; if (ATTACKER_TURN_FLAGS.struggling) { battleCtx->moveTemp = MOVE_STRUGGLE; - overrideMoveChoice = TRUE; + randomizeTarget = TRUE; } else if (ATTACKING_MON.moveEffectsData.encoredMove && ATTACKING_MON.moveEffectsData.encoredMove == ATTACKING_MON.moves[ATTACKING_MON.moveEffectsData.encoredMoveSlot]) { // Attacker is locked into one of its moves by Encore battleCtx->moveTemp = ATTACKING_MON.moveEffectsData.encoredMove; - overrideMoveChoice = TRUE; + randomizeTarget = TRUE; } else if (ATTACKING_MON.moveEffectsData.encoredMove && ATTACKING_MON.moveEffectsData.encoredMove != ATTACKING_MON.moves[ATTACKING_MON.moveEffectsData.encoredMoveSlot]) { // Attacker is Encored, but does not actually know the move in the Encored slot. This can @@ -1922,7 +1922,7 @@ static void BattleController_FightCommand(BattleSystem *battleSys, BattleContext ATTACKING_MON.moveEffectsData.encoredMove = MOVE_NONE; ATTACKING_MON.moveEffectsData.encoredMoveSlot = 0; ATTACKING_MON.moveEffectsData.encoredTurns = 0; - overrideMoveChoice = TRUE; + randomizeTarget = TRUE; } else if (BattleSystem_CanPickCommand(battleCtx, battleCtx->attacker) == FALSE) { // Relock the attacker into its move. There should be no override here, as the attacker // should not have been able to choose any input. @@ -1931,14 +1931,14 @@ static void BattleController_FightCommand(BattleSystem *battleSys, BattleContext // If the attacker somehow selected a move that it does not know, prefer the move that's // actually in the chosen slot. battleCtx->moveTemp = ATTACKING_MON.moves[battleCtx->moveSlot[battleCtx->attacker]]; - overrideMoveChoice = TRUE; + randomizeTarget = TRUE; } else { battleCtx->moveTemp = ATTACKING_MON.moves[battleCtx->moveSlot[battleCtx->attacker]]; } battleCtx->moveCur = battleCtx->moveTemp; battleCtx->command = BATTLE_CONTROL_BEFORE_MOVE; - battleCtx->defender = BattleSystem_Defender(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveTemp, overrideMoveChoice, 0); + battleCtx->defender = BattleSystem_Defender(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveTemp, randomizeTarget, 0); BattleIO_ClearMessageBox(battleSys); } @@ -2225,7 +2225,7 @@ static int BattleController_CheckObedience(BattleSystem *battleSys, BattleContex ATTACKING_MOVE = rand2; battleCtx->moveTemp = ATTACKING_MON.moves[ATTACKING_MOVE]; battleCtx->moveCur = battleCtx->moveTemp; - battleCtx->defender = BattleSystem_Defender(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveTemp, 1, 0); + battleCtx->defender = BattleSystem_Defender(battleSys, battleCtx, battleCtx->attacker, battleCtx->moveTemp, TRUE, 0); if (battleCtx->defender == BATTLER_NONE) { ATTACKER_ACTION[BATTLE_ACTION_CHOOSE_TARGET] = BattleSystem_RandomOpponent(battleSys, battleCtx, battleCtx->attacker); diff --git a/src/overlay016/battle_script.c b/src/overlay016/battle_script.c index 1a3dc0d049..468ab1cbc3 100644 --- a/src/overlay016/battle_script.c +++ b/src/overlay016/battle_script.c @@ -5802,8 +5802,8 @@ static BOOL ov16_02246688 (BattleSystem * param0, BattleContext * param1) BattleScript_Iter(param1, 1); v0 = BattleScript_Read(param1); - v6 = ov16_0223E2A4(param0, param1->attacker, 0); - v7 = ov16_0223E2A4(param0, param1->attacker, 2); + v6 = BattleSystem_EnemyInSlot(param0, param1->attacker, 0); + v7 = BattleSystem_EnemyInSlot(param0, param1->attacker, 2); param1->battleMons[v6].moveEffectsMask |= 0x40000000; param1->battleMons[v7].moveEffectsMask |= 0x40000000; diff --git a/src/overlay016/ov16_0223DF00.c b/src/overlay016/ov16_0223DF00.c index 318523e05d..ad5bdc5e60 100644 --- a/src/overlay016/ov16_0223DF00.c +++ b/src/overlay016/ov16_0223DF00.c @@ -124,7 +124,7 @@ int ov16_0223E22C(BattleSystem * param0); int ov16_0223E240(BattleSystem * param0); int BattleSystem_MapHeader(BattleSystem * param0); int BattleSystem_Partner(BattleSystem *battleSys, int battler); -int ov16_0223E2A4(BattleSystem * param0, int param1, int param2); +int BattleSystem_EnemyInSlot(BattleSystem *battleSys, int attacker, int slot); BOOL ov16_0223E30C(BattleSystem * param0, int param1, int param2, int param3, int param4); u32 ov16_0223EBEC(BattleSystem * param0); int ov16_0223EBF8(BattleSystem * param0); @@ -543,26 +543,26 @@ int BattleSystem_Partner (BattleSystem *battleSys, int battler) return i; } -int ov16_0223E2A4 (BattleSystem * param0, int param1, int param2) +int BattleSystem_EnemyInSlot(BattleSystem *battleSys, int attacker, int slot) { - int v0; - int v1; - u32 v2; - - v1 = BattleSystem_MaxBattlers(param0); - v2 = BattleSystem_BattleType(param0); + int maxBattlers = BattleSystem_MaxBattlers(battleSys); + u32 battleType = BattleSystem_BattleType(battleSys); - if ((v2 & 0x2) == 0) { - return param1 ^ 1; + // In double battles, return the singular opponent + if ((battleType & BATTLE_TYPE_DOUBLES) == FALSE) { + return attacker ^ 1; } - for (v0 = 0; v0 < v1; v0++) { - if ((v0 != param1) && ((BattleSystem_BattlerSlot(param0, v0) & 2) == param2) && (Battler_Side(param0, v0) != Battler_Side(param0, param1))) { + int battler; + for (battler = 0; battler < maxBattlers; battler++) { + if (battler != attacker + && (BattleSystem_BattlerSlot(battleSys, battler) & 2) == slot + && Battler_Side(battleSys, battler) != Battler_Side(battleSys, attacker)) { break; } } - return v0; + return battler; } BOOL ov16_0223E30C (BattleSystem * param0, int param1, int param2, int param3, int param4) diff --git a/src/overlay016/ov16_0225177C.c b/src/overlay016/ov16_0225177C.c index 69360db469..2325d390d8 100644 --- a/src/overlay016/ov16_0225177C.c +++ b/src/overlay016/ov16_0225177C.c @@ -113,7 +113,7 @@ int BattleSystem_CheckImmunityAbilities(BattleContext * param0, int param1, int BOOL BattleSystem_TriggerTurnEndAbility(BattleSystem * param0, BattleContext * param1, int param2); int BattleSystem_Divide(int param0, int param1); int BattleSystem_ShowMonChecks(BattleSystem * param0, BattleContext * param1); -int BattleSystem_RandomOpponent(BattleSystem * param0, BattleContext * param1, int param2); +int BattleSystem_RandomOpponent(BattleSystem *battleSys, BattleContext *battleCtx, int attacker); BOOL BattleSystem_TriggerAbilityOnHit(BattleSystem *battleSys, BattleContext *battleCtx, int *nextSeq); BOOL BattleSystem_RecoverStatusByAbility(BattleSystem * param0, BattleContext * param1, int param2, int param3); BOOL ov16_022577A4(BattleContext * param0, int param1, int param2); @@ -1628,170 +1628,136 @@ BOOL BattleSystem_TriggerSecondaryEffect(BattleSystem *battleSys, BattleContext return result; } -int BattleSystem_Defender (BattleSystem * param0, BattleContext * param1, int param2, u16 param3, int param4, int param5) +int BattleSystem_Defender(BattleSystem *battleSys, BattleContext *battleCtx, int attacker, u16 move, BOOL randomize, int inRange) { - int v0; - int v1; + int defender = BATTLER_NONE; - v0 = 0xff; - - if (param3) { - v1 = param1->aiContext.moveTable[param3].range; + int range; + if (move) { + range = MOVE_DATA(move).range; } else { - v1 = param5; + range = inRange; } - if (v1 == 0x4) { - { - int v2; - int v3 = BattleSystem_MaxBattlers(param0); - BattlerData * v4 = BattleSystem_BattlerData(param0, param2); - u8 v5 = Battler_Type(v4); - - for (param1->battlerCounter = 0; param1->battlerCounter < v3; param1->battlerCounter++) { - v2 = param1->monSpeedOrder[param1->battlerCounter]; + if (range == RANGE_ADJACENT_OPPONENTS) { // e.g., Acid, Blizzard + int maxBattlers = BattleSystem_MaxBattlers(battleSys); + BattlerData *battlerData = BattleSystem_BattlerData(battleSys, attacker); + u8 attackerType = Battler_Type(battlerData); - if (param1->battleMons[v2].curHP != 0) { - v4 = BattleSystem_BattlerData(param0, v2); + // Assign the first possible living target based on speed order + for (battleCtx->battlerCounter = 0; battleCtx->battlerCounter < maxBattlers; battleCtx->battlerCounter++) { + int battler = battleCtx->monSpeedOrder[battleCtx->battlerCounter]; - if (((v5 & 0x1) && ((Battler_Type(v4) & 0x1) == 0)) || ((v5 & 0x1) == 0) && (Battler_Type(v4) & 0x1)) { - v0 = v2; - break; - } + // Check that this battler is an enemy of the attacker + if (battleCtx->battleMons[battler].curHP != 0) { + battlerData = BattleSystem_BattlerData(battleSys, battler); + if (((attackerType & BATTLER_TYPE_SOLO_ENEMY) && (Battler_Type(battlerData) & BATTLER_TYPE_SOLO_ENEMY) == FALSE) + || ((attackerType & BATTLER_TYPE_SOLO_ENEMY) == FALSE) && (Battler_Type(battlerData) & BATTLER_TYPE_SOLO_ENEMY)) { + defender = battler; + break; } } - - if (param1->battlerCounter != v3) { - param1->battlerCounter++; - } } - } else if (v1 == 0x8) { - { - int v6; - int v7; - - v7 = BattleSystem_MaxBattlers(param0); - - for (param1->battlerCounter = 0; param1->battlerCounter < v7; param1->battlerCounter++) { - v6 = param1->monSpeedOrder[param1->battlerCounter]; - - if (param1->battleMons[v6].curHP != 0) { - if (v6 != param2) { - v0 = v6; - break; - } - } - } - if (param1->battlerCounter != v7) { - param1->battlerCounter++; - } + if (battleCtx->battlerCounter != maxBattlers) { + battleCtx->battlerCounter++; } - } else if ((v1 == 0x200) && (param4 == 1)) { - { - int v8; + } else if (range == RANGE_ALL_ADJACENT) { // e.g., Earthquake, Surf + int maxBattlers = BattleSystem_MaxBattlers(battleSys); - v8 = BattleSystem_BattleType(param0); + // Assign the first possible living target based on speed order + for (battleCtx->battlerCounter = 0; battleCtx->battlerCounter < maxBattlers; battleCtx->battlerCounter++) { + int battler = battleCtx->monSpeedOrder[battleCtx->battlerCounter]; - if ((v8 & 0x2) && ((BattleSystem_RandNext(param0) % 2) == 0)) { - v0 = BattleSystem_Partner(param0, param2); - - if (param1->battleMons[v0].curHP == 0) { - v0 = param2; - } - } else { - v0 = param2; + // Only care that this battler is not the attacker + if (battleCtx->battleMons[battler].curHP != 0 && battler != attacker) { + defender = battler; + break; } } - } else if ((v1 == 0x400) && (param4 == 1)) { - v0 = BattleSystem_RandomOpponent(param0, param1, param2); - } else if (v1 == 0x80) { - v0 = BattleSystem_RandomOpponent(param0, param1, param2); - } else if ((v1 == 0x10) || (v1 == 0x20) || (v1 == 0x1) || (v1 == 0x40)) { - v0 = param2; - } else if (v1 == 0x100) { - { - int v9; - v9 = BattleSystem_BattleType(param0); - - if (v9 & 0x2) { - v0 = BattleSystem_Partner(param0, param2); - } else { - v0 = param2; - } + if (battleCtx->battlerCounter != maxBattlers) { + battleCtx->battlerCounter++; } - } else if (v1 == 0x200) { - { - int v10; - - v10 = BattleSystem_BattleType(param0); - - if (v10 & 0x2) { - v0 = param1->battlerActions[param2][1]; - - if (param1->battleMons[v0].curHP == 0) { - v0 = param2; - } - } else { - v0 = param2; + } else if (range == RANGE_USER_OR_ALLY && randomize == TRUE) { // e.g., Acupressure + if ((BattleSystem_BattleType(battleSys) & BATTLE_TYPE_DOUBLES) + && BattleSystem_RandNext(battleSys) % 2 == 0) { + defender = BattleSystem_Partner(battleSys, attacker); + if (battleCtx->battleMons[defender].curHP == 0) { + defender = attacker; } + } else { + defender = attacker; + } + } else if (range == RANGE_SINGLE_TARGET_ME_FIRST && randomize == TRUE) { // e.g., Me First + defender = BattleSystem_RandomOpponent(battleSys, battleCtx, attacker); + } else if (range == RANGE_OPPONENT_SIDE) { // e.g., Spikes, Stealth Rock + defender = BattleSystem_RandomOpponent(battleSys, battleCtx, attacker); + } else if (range == RANGE_USER // e.g., Swords Dance + || range == RANGE_USER_SIDE // e.g., Light Screen, Reflect + || range == RANGE_SINGLE_TARGET_SPECIAL // e.g., Counter, Mirror Coat + || range == RANGE_FIELD) { // e.g., Sunny Day + defender = attacker; + } else if (range == RANGE_ALLY) { // e.g., Helping Hand + if (BattleSystem_BattleType(battleSys) & BATTLE_TYPE_DOUBLES) { + defender = BattleSystem_Partner(battleSys, attacker); + } else { + defender = attacker; } - } else if ((v1 == 0x2) || (param4 == 1)) { - { - int v11; - int v12; - int v13[2]; - - v11 = BattleSystem_BattleType(param0); - v12 = Battler_Side(param0, param2) ^ 1; - v13[0] = ov16_0223E2A4(param0, param2, 0); - v13[1] = ov16_0223E2A4(param0, param2, 2); - - if (v11 & 0x2) { - if ((param1->sideConditions[v12].followMe) && (param1->battleMons[param1->sideConditions[v12].followMeUser].curHP)) { - v0 = param1->sideConditions[v12].followMeUser; - } else if ((param1->battleMons[v13[0]].curHP) && (param1->battleMons[v13[1]].curHP)) { - v12 = BattleSystem_RandNext(param0) & 1; - v0 = v13[v12]; - } else if (param1->battleMons[v13[0]].curHP) { - v0 = v13[0]; - } else if (param1->battleMons[v13[1]].curHP) { - v0 = v13[1]; - } - } else { - if (param1->battleMons[param2 ^ 1].curHP) { - v0 = param2 ^ 1; - } + } else if (range == RANGE_USER_OR_ALLY) { // e.g., Acupressure + if (BattleSystem_BattleType(battleSys) & BATTLE_TYPE_DOUBLES) { + defender = battleCtx->battlerActions[attacker][BATTLE_ACTION_CHOOSE_TARGET]; + if (battleCtx->battleMons[defender].curHP == 0) { + defender = attacker; } - } - } else { - { - int v14; - int v15; - int v16; - - v14 = Battler_Side(param0, param2) ^ 1; - v15 = param1->battlerActions[param2][1]; - v16 = BattleSystem_MaxBattlers(param0); - - if ((param1->sideConditions[v14].followMe) && (param1->battleMons[param1->sideConditions[v14].followMeUser].curHP)) { - v0 = param1->sideConditions[v14].followMeUser; - } else { - if (param1->battleMons[v15].curHP) { - v0 = v15; - } else { - v15 = BattleSystem_RandomOpponent(param0, param1, param2); - - if (param1->battleMons[v15].curHP) { - v0 = v15; - } - } + } else { + defender = attacker; + } + } else if (range == RANGE_RANDOM_OPPONENT || randomize == TRUE) { // e.g., Outrage, Thrash, any other reason the move should be randomly targeted + int opponents[2]; + int battleType = BattleSystem_BattleType(battleSys); + int enemySide = Battler_Side(battleSys, attacker) ^ 1; + + opponents[0] = BattleSystem_EnemyInSlot(battleSys, attacker, ENEMY_IN_SLOT_RIGHT); + opponents[1] = BattleSystem_EnemyInSlot(battleSys, attacker, ENEMY_IN_SLOT_LEFT); + + if (battleType & BATTLE_TYPE_DOUBLES) { + if (battleCtx->sideConditions[enemySide].followMe + && battleCtx->battleMons[battleCtx->sideConditions[enemySide].followMeUser].curHP) { + // If Follow Me is active and the user is still alive, re-point all targets toward them + defender = battleCtx->sideConditions[enemySide].followMeUser; + } else if (battleCtx->battleMons[opponents[0]].curHP + && battleCtx->battleMons[opponents[1]].curHP) { + defender = opponents[BattleSystem_RandNext(battleSys) & 1]; + } else if (battleCtx->battleMons[opponents[0]].curHP) { + defender = opponents[0]; + } else if (battleCtx->battleMons[opponents[1]].curHP) { + defender = opponents[1]; + } + } else if (battleCtx->battleMons[attacker ^ 1].curHP) { + defender = attacker ^ 1; + } + } else { // the usual single-target moves, e.g., Flamethrower, Thunderbolt + int enemySide = Battler_Side(battleSys, attacker) ^ 1; + int target = battleCtx->battlerActions[attacker][BATTLE_ACTION_CHOOSE_TARGET]; + int maxBattlers = BattleSystem_MaxBattlers(battleSys); + + if (battleCtx->sideConditions[enemySide].followMe + && battleCtx->battleMons[battleCtx->sideConditions[enemySide].followMeUser].curHP) { + // If Follow Me is active and the user is still alive, re-point all targets toward them + defender = battleCtx->sideConditions[enemySide].followMeUser; + } else if (battleCtx->battleMons[target].curHP) { + defender = target; + } else { + // If the original target is no longer alive, try to target their partner instead + target = BattleSystem_RandomOpponent(battleSys, battleCtx, attacker); + if (battleCtx->battleMons[target].curHP) { + defender = target; } } } - return v0; + return defender; } void BattleSystem_RedirectTarget (BattleSystem * param0, BattleContext * param1, int param2, u16 param3) @@ -3703,8 +3669,8 @@ int BattleSystem_ShowMonChecks (BattleSystem * param0, BattleContext * param1) for (v0 = 0; v0 < v5; v0++) { v4 = param1->monSpeedOrder[v0]; - v6 = ov16_0223E2A4(param0, v4, 0); - v7 = ov16_0223E2A4(param0, v4, 2); + v6 = BattleSystem_EnemyInSlot(param0, v4, 0); + v7 = BattleSystem_EnemyInSlot(param0, v4, 2); param1->msgDefender = ov16_0225B840(param0, param1, v6, v7); @@ -3973,8 +3939,8 @@ int BattleSystem_ShowMonChecks (BattleSystem * param0, BattleContext * param1) { int v21[2]; - v21[0] = ov16_0223E2A4(param0, v4, 0); - v21[1] = ov16_0223E2A4(param0, v4, 2); + v21[0] = BattleSystem_EnemyInSlot(param0, v4, 0); + v21[1] = BattleSystem_EnemyInSlot(param0, v4, 2); if ((param1->battleMons[v21[0]].curHP) && (param1->battleMons[v21[0]].heldItem) && (param1->battleMons[v21[1]].curHP) && (param1->battleMons[v21[1]].heldItem)) { param1->msgItemTemp = param1->battleMons[v21[BattleSystem_RandNext(param0) & 1]].heldItem; @@ -4123,29 +4089,27 @@ int BattleSystem_ShowMonChecks (BattleSystem * param0, BattleContext * param1) return v2; } -int BattleSystem_RandomOpponent (BattleSystem * param0, BattleContext * param1, int param2) +int BattleSystem_RandomOpponent(BattleSystem *battleSys, BattleContext *battleCtx, int attacker) { - u32 v0; - int v1; - int v2[2]; - int v3; - - v0 = BattleSystem_BattleType(param0); + int opponents[2]; + u32 battleType = BattleSystem_BattleType(battleSys); - if (v0 & 0x2) { - v2[0] = ov16_0223E2A4(param0, param2, 0); - v2[1] = ov16_0223E2A4(param0, param2, 2); - v3 = BattleSystem_RandNext(param0) & 1; - v1 = v2[v3]; + int chosen; + if (battleType & BATTLE_TYPE_DOUBLES) { + opponents[0] = BattleSystem_EnemyInSlot(battleSys, attacker, ENEMY_IN_SLOT_RIGHT); + opponents[1] = BattleSystem_EnemyInSlot(battleSys, attacker, ENEMY_IN_SLOT_LEFT); + + int rnd = BattleSystem_RandNext(battleSys) & 1; + chosen = opponents[rnd]; - if (param1->battleMons[v1].curHP == 0) { - v1 = v2[v3 ^ 1]; + if (battleCtx->battleMons[chosen].curHP == 0) { + chosen = opponents[rnd ^ 1]; } } else { - v1 = param2 ^ 1; + chosen = attacker ^ 1; } - return v1; + return chosen; } BOOL BattleSystem_TriggerAbilityOnHit(BattleSystem *battleSys, BattleContext *battleCtx, int *nextSeq) diff --git a/src/overlay016/ov16_0226485C.c b/src/overlay016/ov16_0226485C.c index 2be9d56cfd..0541bf14f9 100644 --- a/src/overlay016/ov16_0226485C.c +++ b/src/overlay016/ov16_0226485C.c @@ -698,7 +698,7 @@ void BattleIO_SetCommandSelection (BattleSystem *battleSys, BattleContext *battl } } } else { - v2 = ov16_0223E2A4(battleSys, battler, 2); + v2 = BattleSystem_EnemyInSlot(battleSys, battler, 2); v7 = BattleSystem_Party(battleSys, v2); v6 = 0; From 6a8bddc6ca95e879d3ce13402a95ae5b811e057e Mon Sep 17 00:00:00 2001 From: lhearachel Date: Tue, 10 Oct 2023 23:54:44 -0700 Subject: [PATCH 2/3] Document BtlCmd_JumpToMove --- src/overlay016/battle_script.c | 59 ++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/overlay016/battle_script.c b/src/overlay016/battle_script.c index 468ab1cbc3..df8b85c161 100644 --- a/src/overlay016/battle_script.c +++ b/src/overlay016/battle_script.c @@ -139,7 +139,7 @@ static BOOL BtlCmd_IfMonData(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL BtlCmd_FadeOut(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL BtlCmd_JumpToSub(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL BtlCmd_JumpToBattleEffect(BattleSystem *battleSys, BattleContext *battleCtx); -static BOOL ov16_02241C28(BattleSystem * param0, BattleContext * param1); +static BOOL BtlCmd_JumpToMove(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL ov16_02241CD0(BattleSystem * param0, BattleContext * param1); static BOOL ov16_02241D34(BattleSystem * param0, BattleContext * param1); static BOOL ov16_02241EB0(BattleSystem * param0, BattleContext * param1); @@ -399,7 +399,7 @@ static const BtlCmd sBattleCommands[] = { BtlCmd_FadeOut, BtlCmd_JumpToSub, BtlCmd_JumpToBattleEffect, - ov16_02241C28, + BtlCmd_JumpToMove, ov16_02241CD0, ov16_02241D34, ov16_02241EB0, @@ -2318,32 +2318,51 @@ static BOOL BtlCmd_JumpToBattleEffect(BattleSystem *battleSys, BattleContext *ba return FALSE; } -static BOOL ov16_02241C28 (BattleSystem * param0, BattleContext * param1) +/** + * @brief Jump to the move sequence for the chosen move. + * + * This is specifically for moves which call other moves (e.g., Assist, Me + * First, Metronome, Sleep Talk) by setting battleCtx->msgMoveTemp. + * + * Inputs: + * 1. A flag indicating if the target is set on input. In practice, this is + * always FALSE in vanilla. + * + * Side effects: + * 1. The system control flag to skip the attack message is turned off. + * 2. The system control flag signalling that the move's animation has played + * is turned off. + * 3. battleCtx->moveCur is reassigned to battleCtx->msgMoveTemp. + * 4. The defender is assigned for the move. + * 5. The battler's chosen target is reassigned to the assigned defender. + * + * @param battleSys + * @param battleCtx + * @return FALSE + */ +static BOOL BtlCmd_JumpToMove(BattleSystem *battleSys, BattleContext *battleCtx) { - int v0; - - BattleScript_Iter(param1, 1); - - v0 = BattleScript_Read(param1); + BattleScript_Iter(battleCtx, 1); + BOOL targetIsSet = BattleScript_Read(battleCtx); - param1->battleStatusMask &= (0x1 ^ 0xffffffff); - param1->battleStatusMask &= (0x4000 ^ 0xffffffff); - param1->moveCur = param1->msgMoveTemp; + battleCtx->battleStatusMask &= ~SYSCTL_SKIP_ATTACK_MESSAGE; + battleCtx->battleStatusMask &= ~SYSCTL_PLAYED_MOVE_ANIMATION; + battleCtx->moveCur = battleCtx->msgMoveTemp; - if (v0 == 0) { - param1->defender = BattleSystem_Defender(param0, param1, param1->attacker, param1->msgMoveTemp, 1, 0); - BattleSystem_RedirectTarget(param0, param1, param1->attacker, param1->msgMoveTemp); - param1->battlerActions[param1->attacker][1] = param1->defender; + if (targetIsSet == FALSE) { + battleCtx->defender = BattleSystem_Defender(battleSys, battleCtx, battleCtx->attacker, battleCtx->msgMoveTemp, TRUE, 0); + BattleSystem_RedirectTarget(battleSys, battleCtx, battleCtx->attacker, battleCtx->msgMoveTemp); + battleCtx->battlerActions[battleCtx->attacker][BATTLE_ACTION_CHOOSE_TARGET] = battleCtx->defender; } - if (param1->defender == 0xff) { - param1->commandNext = 38; - BattleScript_Jump(param1, 1, (0 + 281)); + if (battleCtx->defender == BATTLER_NONE) { + battleCtx->commandNext = BATTLE_CONTROL_UPDATE_MOVE_BUFFERS; + BattleScript_Jump(battleCtx, NARC_INDEX_BATTLE__SKILL__SUB_SEQ, BATTLE_SUBSEQ_NO_TARGET); } else { - BattleScript_Jump(param1, 0, param1->moveCur); + BattleScript_Jump(battleCtx, NARC_INDEX_BATTLE__SKILL__WAZA_SEQ, battleCtx->moveCur); } - return 0; + return FALSE; } static BOOL ov16_02241CD0 (BattleSystem * param0, BattleContext * param1) From 5ba21a49a849f138b8be2da5318786c85f1f9378 Mon Sep 17 00:00:00 2001 From: lhearachel Date: Tue, 10 Oct 2023 23:59:27 -0700 Subject: [PATCH 3/3] Document BtlCmd_CheckCritical --- src/overlay016/battle_script.c | 46 ++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/overlay016/battle_script.c b/src/overlay016/battle_script.c index df8b85c161..c7d2b55be5 100644 --- a/src/overlay016/battle_script.c +++ b/src/overlay016/battle_script.c @@ -140,7 +140,7 @@ static BOOL BtlCmd_FadeOut(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL BtlCmd_JumpToSub(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL BtlCmd_JumpToBattleEffect(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL BtlCmd_JumpToMove(BattleSystem *battleSys, BattleContext *battleCtx); -static BOOL ov16_02241CD0(BattleSystem * param0, BattleContext * param1); +static BOOL BtlCmd_CheckCritical(BattleSystem *battleSys, BattleContext *battleCtx); static BOOL ov16_02241D34(BattleSystem * param0, BattleContext * param1); static BOOL ov16_02241EB0(BattleSystem * param0, BattleContext * param1); static BOOL ov16_02241EF0(BattleSystem * param0, BattleContext * param1); @@ -400,7 +400,7 @@ static const BtlCmd sBattleCommands[] = { BtlCmd_JumpToSub, BtlCmd_JumpToBattleEffect, BtlCmd_JumpToMove, - ov16_02241CD0, + BtlCmd_CheckCritical, ov16_02241D34, ov16_02241EB0, ov16_02241EF0, @@ -2329,12 +2329,12 @@ static BOOL BtlCmd_JumpToBattleEffect(BattleSystem *battleSys, BattleContext *ba * always FALSE in vanilla. * * Side effects: - * 1. The system control flag to skip the attack message is turned off. - * 2. The system control flag signalling that the move's animation has played + * - The system control flag to skip the attack message is turned off. + * - The system control flag signalling that the move's animation has played * is turned off. - * 3. battleCtx->moveCur is reassigned to battleCtx->msgMoveTemp. - * 4. The defender is assigned for the move. - * 5. The battler's chosen target is reassigned to the assigned defender. + * - battleCtx->moveCur is reassigned to battleCtx->msgMoveTemp. + * - The defender is assigned for the move. + * - The battler's chosen target is reassigned to the assigned defender. * * @param battleSys * @param battleCtx @@ -2365,17 +2365,37 @@ static BOOL BtlCmd_JumpToMove(BattleSystem *battleSys, BattleContext *battleCtx) return FALSE; } -static BOOL ov16_02241CD0 (BattleSystem * param0, BattleContext * param1) +/** + * @brief Check if a critical hit should occur. + * + * If the battle is either the catching tutorial or the player's first battle, + * then this will always flag no critical hits. + * + * Side effects: + * - battleCtx->criticalMul is set to the multiplier to be applied to the total + * move damage for a critical hit. + * + * @param battleSys + * @param battleCtx + * @return FALSE + */ +static BOOL BtlCmd_CheckCritical(BattleSystem *battleSys, BattleContext *battleCtx) { - BattleScript_Iter(param1, 1); + BattleScript_Iter(battleCtx, 1); - if ((BattleSystem_BattleType(param0) & 0x400) || (BattleSystem_BattleStatus(param0) & 0x1)) { - param1->criticalMul = 1; + if ((BattleSystem_BattleType(battleSys) & BATTLE_TYPE_CATCH_TUTORIAL) + || (BattleSystem_BattleStatus(battleSys) & BATTLE_STATUS_FIRST_BATTLE)) { + battleCtx->criticalMul = 1; } else { - param1->criticalMul = BattleSystem_CalcCriticalMulti(param0, param1, param1->attacker, param1->defender, param1->criticalBoosts, BattleContext_Get(param0, param1, 0, param1->defender)); + battleCtx->criticalMul = BattleSystem_CalcCriticalMulti(battleSys, + battleCtx, + battleCtx->attacker, + battleCtx->defender, + battleCtx->criticalBoosts, + BattleContext_Get(battleSys, battleCtx, BATTLECTX_SIDE_CONDITIONS_MASK, battleCtx->defender)); } - return 0; + return FALSE; } static BOOL ov16_02241D34 (BattleSystem * param0, BattleContext * param1)