From a4370ffcdef9250ed55ec716c8eb065c514d5998 Mon Sep 17 00:00:00 2001 From: DavidJCobb <831497+DavidJCobb@users.noreply.github.com> Date: Sat, 26 Apr 2025 00:45:03 -0400 Subject: [PATCH] gBattleControllerExecFlags bits now identified via helper macros Created "exposition" macros to describe operations performed on gBattleControllerExecFlags. Updated the battle engine internals to use them, to more clearly document how battle link communications actually work. --- include/battle_controllers.h | 41 ++++++++++++++++++++++++++++++++++++ src/battle_controllers.c | 11 ++++++++-- src/battle_main.c | 18 ++++++---------- src/battle_util.c | 22 ++++++++++++++----- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 91b4ab1e56..2706fbf284 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -64,6 +64,47 @@ enum { REQUEST_TOUGH_RIBBON_BATTLE, }; +// Accessors for gBattleControllerExecFlags. +// +// These are provided for documentation purposes, to make the battle +// controller internals and the link communication internals more +// legible. Several of these have functions that you should call +// (e.g. MarkBattlerForControllerExec) instead of using these macros +// directly. + +#define MARK_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battlerId) \ + do { gBattleControllerExecFlags |= gBitTable[battlerId] } while (0) + +#define MARK_BATTLE_CONTROLLER_IDLE_ON_LOCAL(battlerId) \ + do { gBattleControllerExecFlags &= ~gBitTable(battlerId) } while (0) + +#define IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battlerId) \ + (gBattleControllerExecFlags & gBitTable[battlerId]) + +#define MARK_BATTLE_CONTROLLER_MESSAGE_OUTBOUND_OVER_LINK(battlerId) \ + do { gBattleControllerExecFlags |= gBitTable[battlerId] << 28; } while (0) + +#define MARK_BATTLE_CONTROLLER_MESSAGE_SYNCHRONIZED_OVER_LINK(battlerId) \ + do { gBattleControllerExecFlags &= ~((1 << 28) << (battlerId)); } + +#define MARK_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battlerId, playerId) \ + do { gBattleControllerExecFlags |= gBitTable[battlerId] << ((playerId) << 2); } while (0) + +#define MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battlerId, playerId) \ + do { gBattleControllerExecFlags &= ~(gBitTable[battlerId] << ((playerId) * 4)); } while (0) + +#define IS_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battlerId, playerId) \ + (gBattleControllerExecFlags & (gBitTable[battlerId] << ((playerId) * 4))) + +#define IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(battlerId) \ + (gBattleControllerExecFlags & ( \ + (gBitTable[battlerId]) \ + | (0xF << 28) \ + | (gBitTable[battlerId] << 4) \ + | (gBitTable[battlerId] << 8) \ + | (gBitTable[battlerId] << 12) \ + ) + // Special arguments for Battle Controller functions. enum { diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 8247e278bf..40eb22770b 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -855,6 +855,13 @@ static void Task_HandleSendLinkBuffersData(u8 taskId) } // We have received a message. Place it into the "receive" buffer. +// +// Counterintuitively, we also "receive" the outbound messages that +// we send to other players. The GBA basically stores communicated +// data for all four players, so inbound and outbound data can be +// handled uniformly unless a game specifically decides to do +// otherwise. Pokemon, evidently, did not specifically decide to do +// otherwise. void TryReceiveLinkBattleData(void) { u8 i; @@ -916,7 +923,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) switch (BYTE_TO_RECEIVE(0)) { case BATTLELINKMSGTYPE_ENGINE_TO_CONTROLLER: - if (gBattleControllerExecFlags & gBitTable[battlerId]) + if (IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battlerId)) return; memcpy(gBattleBufferA[battlerId], &BYTE_TO_RECEIVE(LINK_BUFF_DATA), blockSize); @@ -935,7 +942,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) break; case BATTLELINKMSGTYPE_CONTROLLER_BECOMING_IDLE: playerId = BYTE_TO_RECEIVE(LINK_BUFF_DATA); - gBattleControllerExecFlags &= ~(gBitTable[battlerId] << (playerId * 4)); + MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battlerId, playerId); break; } diff --git a/src/battle_main.c b/src/battle_main.c index 46f163586f..eb5fdbf43d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4173,7 +4173,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action. - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { RecordedBattle_SetBattlerAction(gActiveBattler, gBattleBufferB[gActiveBattler][1]); gChosenActionByBattler[gActiveBattler] = gBattleBufferB[gActiveBattler][1]; @@ -4352,7 +4352,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CASE_CHOSEN: - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { switch (gChosenActionByBattler[gActiveBattler]) { @@ -4456,11 +4456,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CONFIRMED_STANDBY: - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) - | (0xF << 28) - | (gBitTable[gActiveBattler] << 4) - | (gBitTable[gActiveBattler] << 8) - | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { if (AllAtActionConfirmed()) i = TRUE; @@ -4482,7 +4478,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CONFIRMED: - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { gBattleCommunication[ACTIONS_CONFIRMED_COUNT]++; } @@ -4496,7 +4492,7 @@ static void HandleTurnActionSelectionState(void) { gBattlerAttacker = gActiveBattler; gBattlescriptCurrInstr = gSelectionBattleScripts[gActiveBattler]; - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } @@ -4504,7 +4500,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_SET_BEFORE_ACTION: - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; } @@ -4528,7 +4524,7 @@ static void HandleTurnActionSelectionState(void) { gBattlerAttacker = gActiveBattler; gBattlescriptCurrInstr = gSelectionBattleScripts[gActiveBattler]; - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) + if (!IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(gActiveBattler)) { gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } diff --git a/src/battle_util.c b/src/battle_util.c index 46a2dba310..31cd5026ef 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -839,26 +839,38 @@ static void UNUSED MarkAllBattlersForControllerExec(void) else { for (i = 0; i < gBattlersCount; i++) - gBattleControllerExecFlags |= gBitTable[i]; + MARK_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(i); } } +// Called when the battle engine dispatches a message to a battle controller. +// +// During a singleplayer battle, we just immediately mark the controller as +// active. During a multiplayer link, we do things a little differently. We +// set a bit indicating that we're sending a message over the link. That +// message will be received by all other players... *and* by us, the player +// sending it, at which point we'll invoke MarkBattlerReceivedLinkData, +// below, to clear the "we're sending a message" bit and set the "controller +// is now active" bit. void MarkBattlerForControllerExec(u8 battlerId) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) - gBattleControllerExecFlags |= gBitTable[battlerId] << (32 - MAX_BATTLERS_COUNT); + MARK_BATTLE_CONTROLLER_MESSAGE_OUTBOUND_OVER_LINK(battlerId); else - gBattleControllerExecFlags |= gBitTable[battlerId]; + MARK_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battlerId); } +// Called when a message dispatched from the battle engine to a battle +// controller is received over link communications. All players assume +// that if they've received the message, everyone else has as well. void MarkBattlerReceivedLinkData(u8 battlerId) { s32 i; for (i = 0; i < GetLinkPlayerCount(); i++) - gBattleControllerExecFlags |= gBitTable[battlerId] << (i << 2); + MARK_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battlerId, i); - gBattleControllerExecFlags &= ~((1 << 28) << battlerId); + MARK_BATTLE_CONTROLLER_MESSAGE_SYNCHRONIZED_OVER_LINK(battlerId); } void CancelMultiTurnMoves(u8 battler)