Alchemy
Loading...
Searching...
No Matches

link.c

Go to the documentation of this file.
1/// @file
2/// Link battles: the parties and Djinn exchanged, and the turn handshake.
3/// Part of battle.c, the battle object, compiled only through it.
4
6
7// The longest "Enemy" label the opponent's names take before them; the
8// European editions also put a space after it.
9#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH) || \
10 (LANGUAGE == LANGUAGE_FRENCH) || (LANGUAGE == LANGUAGE_ITALIAN)
11#define NAME_LIMIT 7
12#define APPEND_NAME_SPACE 1
13#else
14#define NAME_LIMIT 4
15#define APPEND_NAME_SPACE 0
16#endif
17
18/// What a link battle sends of each party member, and of the Djinn recovery
19/// list.
23};
24
25/// Link battle: receives the opponent's party into the enemy units, one
26/// member at a time, and puts the "Enemy" label (MsgEnemyLabel) before each
27/// name; then receives the opponent's Djinn recovery list. Stops early if a
28/// transfer cannot start. Returns how many of the received units are
29/// members (a unit sent with side 0 is an empty place). Nothing in the
30/// source calls it. Overwrites enemy units 128 to 130 and the opponent's
31/// recovery table.
33 u16 label[24];
34 void *buffer;
35 struct BattleUnit *opponent;
36 s32 memberCount;
37 s32 partySlot;
38 s32 labelLength;
39 s32 i;
40
42 memberCount = 0;
43 partySlot = 0;
44 while (partySlot < LINK_PARTY_SIZE) {
45 opponent = UnitGetFar(partySlot + FIRST_ENEMY_UNIT);
46 if (SerialRuntime_BeginReceive(opponent) == -1) {
47 break;
48 }
50 if (opponent->side != 0) {
51 memberCount += 1;
52 }
53 WaitFrames(2);
54
55 // Measure the label (at most NAME_LIMIT + 1 characters), shift the name
56 // right to make room for it, then write it in; the name's end is cut.
57 Ui_AdjustValueWithoutLimitFar(MsgEnemyLabel, label);
58 i = 0;
59 if (label[i] != 0) {
60 do {
61 i += 1;
62 if (i > NAME_LIMIT) {
63 break;
64 }
65 } while (label[i] != 0);
66 }
67#if APPEND_NAME_SPACE
68 label[i] = ' ';
69 i += 1;
70#endif
71 labelLength = i;
72 for (i = UNIT_NAME_LENGTH; i >= labelLength; i--) {
73 opponent->name[i] = opponent->name[i - labelLength];
74 }
75 for (i = 0; i < labelLength; i++) {
76 opponent->name[i] = label[i];
77 }
78 opponent->name[UNIT_NAME_LENGTH] = 0;
79 partySlot += 1;
80 }
81 MemFree(buffer);
82
83 // The recovery packet follows all three party places.
88 WaitFrames(2);
89 }
90 MemFree(buffer);
91 return memberCount;
92}
93
94/// Link battle: sends the party, one member at a time and three places in
95/// all (an empty place has side 0), then the Djinn recovery list with each
96/// owner given the unit id the opponent knows them by. Records that id of
97/// each member in gBattleWork->ownerSlots (0xff for none). Nothing in the
98/// source calls it.
99#ifdef UBFIX
100void LinkBattle_SendParty(void) {
101#else
103#endif
104 u8 *buffer;
105 struct BattleWork *work;
106 s32 j;
107 struct DjinnRecoveryList *list;
108 s32 size;
109 s32 count;
110 s32 i;
111 s32 noSlot;
112 u16 owners[8];
113
115 buffer = (u8 *)Runtime_BumpAllocateAlternatePool(size);
116 noSlot = 0xff;
117 work = gBattleWork;
118 for (i = 7; i >= 0; i--)
119 work->ownerSlots[i] = noSlot;
120
121 // The members, each marked as the opponent's.
123 for (i = 0; i < count; i++) {
124 Iwram_CopyWords(buffer, UnitGetFar(owners[i]), size);
125 ((struct BattleUnit *)buffer)->side = BATTLE_UNIT_PARTY;
126 // FIRST_ENEMY_UNIT + i, in a byte.
127 work->ownerSlots[owners[i]] = i - FIRST_ENEMY_UNIT;
129 break;
131 WaitFrames(2);
132 }
133 // Empty places up to three.
134 for (; i < LINK_PARTY_SIZE; i++) {
135 ((struct BattleUnit *)buffer)->side = BATTLE_UNIT_ABSENT;
137 break;
139 WaitFrames(2);
140 }
141
142 // The Djinn recovery list, its owners renumbered for the opponent.
144 MemFree(buffer);
145 buffer = (u8 *)Runtime_BumpAllocateAlternatePool(size);
146 /* FAKEMATCH(one-pass): the copy in a one-pass loop, a scheduling barrier
147 * that keeps the list pointer's copy ahead of the count's load; as the plain
148 * call, 1 of 152 instructions differ in tbs-en. */
149 do {
151 size);
152 } while (0);
153 list = &((struct DjinnRecoveryTable *)buffer)->list;
154 for (j = 0; j < list->count; j++)
158 WaitFrames(1);
159 WaitFrames(2);
160 }
161 MemFree(buffer);
162}
163
164/// Copies four characters of a name and a terminator, an underscore for each
165/// missing character, and returns a pointer to the terminator.
166/// UBFIX keeps the private scratch buffer valid until the next call; the
167/// original returns a pointer to expired stack storage. The first two
168/// arguments are ignored.
169/// Nothing calls it.
171 u16 *source) {
172#ifdef UBFIX
173 static u8 sBuffer[5];
174 u8 *base = sBuffer;
175#else
176 u8 buffer[5];
177 u8 *base = buffer;
178#endif
179 u32 fill = '_';
180 u8 *current = base;
181 u8 *dst = current;
182 s32 count = 3;
183
184 do {
185 u32 value = *source;
186
187 *dst = value;
188 source++;
189 dst++;
190 if ((u8)value == 0)
191 *current = fill;
192 current++;
193 count--;
194 } while (count >= 0);
195 base[4] = 0;
196 // BUG: the original buffer is on the stack, gone once this returns.
197 // UBFIX keeps the result until the next call in private scratch storage.
198 return dst;
199}
200
201/// In a link battle, shows the "EXEC" signature to the other console and
202/// waits up to 30 frames for it to show the same. Returns 0 once both do (or
203/// outside a link battle), and -1 if the battle is paused, the link is lost
204/// for more than LINK_MAX_MISSED_FRAMES frames or the signatures never meet.
205/// Called by BattlePresentation_BuildActions. Writes the first eight bytes
206/// of gSerialTransfer.payload.
208 struct BattleWork *work = gBattleWork;
209 u16 *peer;
210 u16 *signature;
211 s32 missed = 0;
212 s32 i;
213
214 if (work->twoSided != 0) {
215 u32 other = work->linkSide ^ 1;
216
217 // gLinkPeerSignatures[other]: a peer's payload holds four bytes before
218 // its signature.
219 peer = (u16 *)(gSerialPeerPayloads[other] + 4);
220 signature = (u16 *)gSerialTransfer.payload;
221 if (work->linkPaused == 0) {
222 // The other console is in step once its second pair reads "EC".
223 signature[0] = 'E';
224 signature[1] = 'X';
225 signature[2] = 'E';
226 signature[3] = 'C';
227
228 for (i = 0; i < 30; i++) {
230 missed++;
231 if (missed > LINK_MAX_MISSED_FRAMES)
232 return -1;
233 } else {
234 missed = 0;
235 if (signature[2] == peer[2] && signature[3] == peer[3])
236 return 0;
237 }
238 WaitFrames(1);
239 }
240 }
241 return -1;
242 }
243 return 0;
244}
245
246/// In a link battle, after each action and after the end of each round,
247/// steps both consoles through the same handshake: "ex" "TU", then "rn",
248/// "EXEC", "tu" and finally "RN". Each step shows its signature and waits
249/// for the other console to show it too, failing if the other console
250/// shows a different earlier step. Returns -1 if the battle is paused, the
251/// link is lost for more than LINK_MAX_MISSED_FRAMES frames, or the steps
252/// disagree; 0 once in step, or outside a link battle. Called by
253/// Battle_RunEncounter. Writes the first sixteen bytes of
254/// gSerialTransfer.payload.
256 struct BattleWork *work = gBattleWork;
257 u16 *peer;
258 u16 *signature;
259 s32 missed = 0;
260 /* FAKEMATCH(dead): an unused array for the twenty bytes of frame no
261 * instruction touches; without it, 8 of 232 instructions differ in tbs-en. */
262 UNUSED u16 spare[10];
263
264 if (work->twoSided != 0) {
265 peer = gLinkPeerSignatures[work->linkSide ^ 1];
266 signature = (u16 *)gSerialTransfer.payload;
267 if (work->linkPaused != 0)
268 return -1;
269
270 // Publish "ex"/"TU" while both sides still agree on the earlier phase.
271 signature[0] = 'e';
272 signature[1] = 'x';
273 signature[4] = 'T';
274 signature[5] = 'U';
275 WaitFrames(1);
276 for (;;) {
278 if (++missed > LINK_MAX_MISSED_FRAMES)
279 return -1;
280 } else {
281 missed = 0;
282 if (signature[2] != peer[2] || signature[3] != peer[3])
283 return -1;
284 if (signature[0] == peer[0] && signature[1] == peer[1] &&
285 signature[4] == peer[4] && signature[5] == peer[5])
286 break;
287 }
288 WaitFrames(1);
289 }
290
291 // Advance to "rn" only after the peer has reached "TU".
292 signature[6] = 'r';
293 signature[7] = 'n';
294 for (;;) {
296 if (++missed > LINK_MAX_MISSED_FRAMES)
297 return -1;
298 } else {
299 missed = 0;
300 if (signature[4] != peer[4] || signature[5] != peer[5])
301 return -1;
302 if (signature[6] == peer[6] && signature[7] == peer[7])
303 break;
304 }
305 WaitFrames(1);
306 }
307
308 // Confirm "rn" before publishing the execution signature.
309 signature[0] = 'E';
310 signature[1] = 'X';
311 signature[2] = 'E';
312 signature[3] = 'C';
313 for (;;) {
315 if (++missed > LINK_MAX_MISSED_FRAMES)
316 return -1;
317 } else {
318 missed = 0;
319 if (signature[6] != peer[6] || signature[7] != peer[7])
320 return -1;
321 if (signature[0] == peer[0] && signature[1] == peer[1] &&
322 signature[2] == peer[2] && signature[3] == peer[3])
323 break;
324 }
325 WaitFrames(1);
326 }
327
328 // Confirm the execution signature before acknowledging with "tu".
329 signature[4] = 't';
330 signature[5] = 'u';
331 for (;;) {
333 if (++missed > LINK_MAX_MISSED_FRAMES)
334 return -1;
335 } else {
336 missed = 0;
337 if (signature[0] != peer[0] || signature[1] != peer[1] ||
338 signature[2] != peer[2] || signature[3] != peer[3])
339 return -1;
340 if (signature[4] == peer[4] && signature[5] == peer[5])
341 break;
342 }
343 WaitFrames(1);
344 }
345
346 // "RN": wait for the other console to move on from "rn".
347 signature[6] = 'R';
348 signature[7] = 'N';
349 for (;;) {
351 if (++missed > LINK_MAX_MISSED_FRAMES)
352 return -1;
353 } else {
354 missed = 0;
355 if (peer[6] != 'r' || peer[7] != 'n')
356 break;
357 }
358 WaitFrames(1);
359 }
360 }
361 return 0;
362}
363
364/// Records, for each party member, the unit id the link opponent knows them
365/// by (FIRST_ENEMY_UNIT plus their place in the party), in
366/// gBattleWork->ownerSlots. Called by Battle_RunEncounter as a link battle
367/// is set up.
368#ifdef UBFIX
370#else
372#endif
373 u16 activeMembers[8];
374 struct BattleWork *battleState = gBattleWork;
375 s32 partySize = BattleParty_PrepareActiveOwners(activeMembers);
376 s32 memberSlot;
377 s32 unitId;
378
379 for (memberSlot = 0; memberSlot < partySize; memberSlot++) {
380 unitId = activeMembers[memberSlot];
381 // FIRST_ENEMY_UNIT + memberSlot, in a byte.
382 battleState->ownerSlots[unitId] = memberSlot - FIRST_ENEMY_UNIT;
383 }
384}
385
386/// Clears the link transfer's payload, the signatures included. Called by
387/// Battle_RunEncounter on each way out of the battle but fleeing.
390}
s32 BattleParty_PrepareActiveOwners(u16 *owners)
Lists the party members who fight (the first four of the party, three in a link battle) and marks eac...
Definition encounter.c:371
What every part of the battle object shares: its includes, in their original order,...
#define UNIT_NAME_LENGTH
The length of a unit's name, its terminator excluded.
#define DJINN_RECOVERY_OPPONENT
#define FIRST_ENEMY_UNIT
Unit ids from 128 are the enemies'; in a link battle the opponent's party takes the first three.
#define LINK_PARTY_SIZE
#define DJINN_RECOVERY_PARTY
The Djinn recovery list's two tables (Djinn_GetRecoveryTableFar): the party's and the link opponent's...
#define LINK_MAX_MISSED_FRAMES
The frames a link battle tolerates without a connection before it gives up.
#define LINK_BOTH_CONNECTED
gLinkStatus: both consoles are connected.
@ BATTLE_UNIT_ABSENT
@ BATTLE_UNIT_PARTY
struct BattleWork * gBattleWork
struct BattleUnit * UnitGetFar(u32 owner)
void * Djinn_GetRecoveryTableFar(s32 arg0)
void Ui_AdjustValueWithoutLimitFar(s32 message, u16 *buf)
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
u16 gLinkPeerSignatures[][12]
24 bytes per peer
void * Runtime_BumpAllocateAlternatePool(s32 size)
As MemAlloc, but tries EWRAM first.
Definition heap.c:140
static s32 Iwram_CopyWords(volatile void *destination, const volatile void *source, s32 size)
Definition iwram_call.h:85
static s32 Iwram_ClearWords(void *destination, s32 size)
Definition iwram_call.h:55
void MemFree(void *buffer)
void SerialRuntime_WaitForSend(void)
Definition serial.c:504
s32 SerialRuntime_BeginReceive(void *destination)
Starts receiving the partner's blocks into destination: sets the receive destination and clears the r...
Definition serial.c:474
s32 SerialRuntime_BeginSend(void *source, s32 size)
Starts sending size bytes from source to the partner in 20-byte blocks: sets the send source,...
Definition serial.c:454
void SerialRuntime_WaitForReceive(void)
Definition serial.c:505
volatile u16 gLinkStatus
Link status, updated by the VBlank exchange handler.
u8 gSerialPeerPayloads[4][24]
struct SerialTransferState gSerialTransfer
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 side
BATTLE_UNIT_ABSENT (0), BATTLE_UNIT_ENEMY (1) or BATTLE_UNIT_PARTY (2).
u8 name[0x0f]
The name, one byte a character, ending in 0.
The battle's own work: its enemies, units and their objects, the turn's actions, the spoils,...
Definition battle_work.h:71
u8 twoSided
1 in a link battle
Definition battle_work.h:90
u8 ownerSlots[8]
For each party unit, its slot as a link opponent's enemy, minus FIRST_ENEMY_UNIT; 0xff for none.
Definition battle_work.h:97
u8 linkPaused
set when the link drops or the console's id changes
u8 linkSide
this console's multiplayer id
Definition battle_work.h:98
u8 unitId
its owner
One side's Djinn recovery list: up to 64 entries and their count.
struct DjinnRecoveryEntry entries[64]
s32 count
entries in use
One side's Djinn recovery table, as Djinn_GetRecoveryTable returns it.
struct DjinnRecoveryList list
u8 payload[20]
one block of the data sent
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
unsigned short u16
Definition types.h:13
#define UNUSED
Definition types.h:22
unsigned char u8
Definition types.h:11