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)