Alchemy
Loading...
Searching...
No Matches

targets.c

Go to the documentation of this file.
1/// @file
2/// Choosing a battle target.
3/// Part of battle_command.c, the menu object, compiled only through it.
4
6
7#if LANGUAGE == LANGUAGE_JAPANESE
8#define TARGET_MYSTERY_NAME_COPY UiText_DecodeMessage
9#define TARGET_MYSTERY_NAME_CAPACITY 10
10#else
11#define TARGET_MYSTERY_NAME_COPY UiText_CopyMessageString
12#define TARGET_MYSTERY_NAME_CAPACITY 14
13#endif
14
15/// A target marker's track: where it is, its state flags and the target it
16/// follows.
17struct CursorSlot {
18 u8 x; ///< screen pixels
19 u8 y; ///< screen pixels
22};
23
24/// Chooses a party/enemy target and animates its selection markers. Return the
25/// encoded side and party position, or -1 on cancel. Healing and status-cure
26/// categories choose an appropriate initial ally and supply condition help.
27s32 BattleTarget_RunSelection(s32 selection, s32 mode, u32 spread, u32 kind) {
28 u16 candidateIds[8];
29 struct SelectionSprite cursorSprites[6];
30 struct CursorSlot cursorTracks[6];
31 struct EffectPosition actorPosition;
32 u16 spreadActorIds[14];
33 u8 spreadTrackIndices[8];
34 struct EffectPosition selectedPosition;
35 u16 targetName[15];
36 struct EffectPosition namePosition;
37 struct EffectPosition markerPosition;
38 struct AffineTransform effect;
39 struct BattleUnit *unit;
40 struct BattleWork *battle;
41 struct UiWindow *window;
42#if TBS_PATCH_4
43 struct UiWindow *conditionWindow;
44#else
45 /* FAKEMATCH(pin): the condition window in r9, where the native keeps it; as
46 * an ordinary local it is spilled, the frame shrinks to 320 bytes and 969
47 * of 1585 instructions differ in tbs-en. */
48 register struct UiWindow *conditionWindow asm("r9");
49#endif
50 s32 targetIndex;
51 s32 targetCount;
52 s32 candidateCount;
53 s32 redraw;
54 s32 cursorSlot;
55 s32 drawCursors;
56 s32 matrix;
57 s32 found;
58 s32 helpColumn;
59 s32 helpRow;
60 s32 count;
61 s32 i;
62 s32 j;
63 s32 n;
64 s32 cursorX;
65 s32 cursorY;
66 s32 pressed;
67 s32 y;
68 s32 width;
69 s32 repeated;
70 s32 result;
71 struct CursorSlot *track;
72 struct SelectionSprite *marker;
73 struct SelectionSprite *firstSprite;
74 struct BattleUnit *target;
75
76 battle = gBattleWork;
77 targetCount = 0;
78 redraw = 0xFFFF;
79 cursorSlot = VramCache_AllocEntry(256);
80#if TBS_PATCH_4
81 /* FAKEMATCH: this value-preserving null initializer keeps infoWin in r10;
82 * as infoWin = 0, 94 instructions differ in tbs-es. */
83 conditionWindow = (struct UiWindow *)((u32)(u16)selection >> 16);
84#else
85 conditionWindow = 0;
86#endif
87 if (spread == 0)
88 spread = 1;
89 // UB: slot 57 is outside the scalar gBattleWork pointer. Named accesses
90 // add 12 bytes here in EN; using gWorkSlot adds 16 bytes.
91#ifdef UBFIX
92 if (mode == 2 || mode == 4)
94 else
96#else
97 if (mode == 2 || mode == 4)
98 ((struct BattleMenuWork **)&gBattleWork)[48]->targetSceneOffset = -2;
99 else
100 ((struct BattleMenuWork **)&gBattleWork)[48]->targetSceneOffset = 16;
101#endif
102 for (i = 0; i < 6; i++)
103 cursorTracks[i].flags = 0;
104 targetIndex = -1;
105 if (mode == 2) {
106 for (i = 0; i <= 5 && battle->partyUnits[i] != 0xFF; i++) {
107 candidateIds[targetCount] = battle->partyUnits[i];
108 targetCount++;
109 }
110 } else if (mode == 4) {
111 candidateIds[0] = (u16)selection;
112 targetCount = 1;
113 } else {
114 for (i = 0; i <= 5 && battle->enemyIds[i] != 0xFF; i++) {
115 candidateIds[targetCount] = battle->enemyIds[i];
116 targetCount++;
117 }
118 }
119 // Keep the candidate list separate from the markers drawn for a spread.
120 candidateIds[targetCount] = 0xFF;
121 candidateCount = targetCount;
122 // Restorative actions prefer an ally who benefits from their effect.
123 if (mode == 2) {
124 if (spread != ABILITY_RANGE_ALL && kind != 0) {
125 found = 0;
126 for (j = 0; j < targetCount; j++) {
127 struct BattleUnit *candidate;
128
129 if (candidateIds[j] == 0xFE)
130 continue;
131 candidate = UnitGetFar(candidateIds[j]);
132 switch (kind) {
133 case 5:
134 if (candidate->hp == 0) {
135 selection = candidateIds[j];
136 found = 1;
137 }
138 break;
139 case 3:
140 if (candidate->poison != 0)
141 found = 1;
142 break;
143 case 4:
144 if (candidate->delusion != 0 || candidate->stun != 0 ||
145 candidate->sleep != 0 || candidate->psySeal != 0 ||
146 candidate->grimReaper != 0)
147 found = 1;
148 break;
149 case 6:
150 if (candidate->delusion != 0 || candidate->stun != 0 ||
151 candidate->sleep != 0 || candidate->psySeal != 0 ||
152 candidate->grimReaper != 0 || candidate->poison != 0 ||
153 candidate->haunt != 0)
154 found = 1;
155 break;
156 }
157 if (found != 0) {
158 selection = candidateIds[j];
159 break;
160 }
161 }
162 }
163 for (j = 0; j < targetCount; j++) {
164 if (candidateIds[j] == selection)
165 break;
166 }
167 if (j != targetCount)
168 targetIndex = j;
169 }
170 if (targetIndex < 0)
171 targetIndex = (targetCount - 1) / 2;
172 while (candidateIds[targetIndex] == 0xFE ||
173 (GameFlag_TestFar(FLAG_LINK_BATTLE) && mode == 1 &&
174 UnitGetFar(candidateIds[targetIndex])->hp == 0)) {
175 targetIndex = targetIndex + targetCount - 1;
176 targetIndex %= targetCount;
177 }
178 if (mode != 2) {
180 selection, EffectPosition_Coordinates(&actorPosition));
181 cursorTracks[0].flags = 8;
182 cursorTracks[0].x = (u8)actorPosition.x;
183 cursorTracks[0].y = 0x80;
184 }
185 window = UiWindow_Create(0, 12, 30, 4, 74);
186 while (1) {
187 firstSprite = cursorSprites;
188 drawCursors = 0;
189 {
190 s32 t = targetIndex * 2;
191
192 /* FAKEMATCH: the separate switch arms, which emit no branch, end CSE
193 * before the call and keep the offset reload ahead of the ids load; as
194 * ids[cursor], 2 instructions differ in tbs-en. */
195 switch (t) {
196 case 0:
197 t = targetIndex * 2;
198 break;
199 default:
200 t = targetIndex * 2;
201 break;
202 }
204 *(u16 *)((u8 *)candidateIds + t),
205 EffectPosition_Coordinates(&selectedPosition));
206 }
207 ((s32 *)firstSprite)[1] = 0x40002000;
208 ((s32 *)firstSprite)[2] = 0;
209 firstSprite->tile =
210 VramCache_Reload(cursorSlot, (((gFrameCount >> 2) & 31) << 8) +
212 selectedPosition.y += Trig_Sin(gFrameCount << 12) / 32768;
213 y = selectedPosition.y;
214 if (cursorTracks[0].flags & 1) {
215 cursorX = (selectedPosition.x + cursorTracks[0].x) / 2;
216 cursorY = (y + cursorTracks[0].y) / 2;
217 if ((selectedPosition.x - cursorX < 0
218 ? -(selectedPosition.x - cursorX)
219 : selectedPosition.x - cursorX) <= 7)
220 drawCursors = 1;
221 selectedPosition.x = cursorX;
222 cursorTracks[0].x = (u8)cursorX;
223 cursorTracks[0].y = (u8)cursorY;
224 selectedPosition.y = cursorY;
225 } else if ((u8)cursorTracks[0].flags <= 3) {
226 cursorTracks[0].x = (u8)selectedPosition.x;
227 selectedPosition.y = y;
228 cursorTracks[0].y = (u8)y;
229 cursorTracks[0].flags = 1;
230 } else {
231 selectedPosition.x = cursorTracks[0].x;
232 selectedPosition.y = cursorTracks[0].y;
233 cursorTracks[0].flags = (u8)(cursorTracks[0].flags - 4);
234 if (cursorTracks[0].flags <= 3)
235 cursorTracks[0].flags = 1;
236 }
237 firstSprite->x = selectedPosition.x - 8;
238 firstSprite->y = (u8)(selectedPosition.y - 16);
239 Runtime_PushSlotEntry((s32 *)firstSprite, 240);
240 if (spread == ABILITY_RANGE_ALL) {
241 effect.scaleX = 256;
242 effect.scaleY = 256;
243 } else {
244 effect.scaleX = 176;
245 effect.scaleY = 176;
246 }
247 effect.angle = 0;
248#if !(LANGUAGE == LANGUAGE_SPANISH) && !(LANGUAGE == LANGUAGE_ITALIAN)
249 /* FAKEMATCH(dead): seventeen dead stores restore the native frame's spill
250 * slots at sp+36 to sp+24; without them, 65 of 1585 instructions differ
251 * in tbs-en, each a spill offset. */
252 effect.angle = 0;
253 effect.angle = 0;
254 effect.angle = 0;
255 effect.angle = 0;
256 effect.angle = 0;
257 effect.angle = 0;
258 effect.angle = 0;
259 effect.angle = 0;
260 effect.angle = 0;
261 effect.angle = 0;
262 effect.angle = 0;
263 effect.angle = 0;
264 effect.angle = 0;
265 effect.angle = 0;
266 effect.angle = 0;
267 effect.angle = 0;
268 effect.angle = 0;
269#endif
270 matrix = AffineMatrix_BuildForEffect(&effect);
271 if (redraw & 1) {
272 targetCount = 0;
273 for (i = 0; i < 6; i++)
274 cursorTracks[i].flags &= (u8)~2;
275 for (i = 0; (u32)i < spread; i++) {
276 {
277 n = targetIndex + i;
278 if (n < candidateCount && candidateIds[n] != 0xFE) {
279 spreadActorIds[targetCount] = candidateIds[n];
280 cursorTracks[i].flags |= 2;
281 if (cursorTracks[i].index != i) {
282 cursorTracks[i].flags &= (u8)~1;
283 cursorTracks[i].index = (s8)i;
284 }
285 spreadTrackIndices[targetCount] = (u8)i;
286 targetCount++;
287 }
288 if (i != 0 && (n = targetIndex - i) >= 0 && candidateIds[n] != 0xFE) {
289 spreadActorIds[targetCount] = candidateIds[n];
290 cursorTracks[6 - i].flags |= 2;
291 if (cursorTracks[6 - i].index != -i) {
292 cursorTracks[6 - i].flags &= (u8)~1;
293 cursorTracks[6 - i].index = (s8)-i;
294 }
295 spreadTrackIndices[targetCount] = (u8)(6 - i);
296 targetCount++;
297 }
298 }
299 }
300 for (i = 0; i < 6; i++) {
301 if ((cursorTracks[i].flags & 2) == 0)
302 cursorTracks[i].index = 6;
303 }
304 spreadActorIds[targetCount] = 0xFF;
305 BattlePres_SetActorModesFar(spreadActorIds, 1);
306 if (candidateIds[targetIndex] <= 7) {
307 if (spread != ABILITY_RANGE_ALL && kind != 0) {
308 unit = UnitGetFar(candidateIds[targetIndex]);
310 candidateIds[targetIndex],
311 EffectPosition_Coordinates(&selectedPosition));
312 if (conditionWindow != 0)
313 UiWork_Finalize(conditionWindow, 1);
314 switch (kind) {
315 case 7: {
316 s32 anchor;
317
318 anchor = selectedPosition.x / 8;
319 helpColumn = anchor - 4;
320#if (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_ITALIAN)
321 if (System_GetLanguage() == 4) {
322 if (anchor + 6 > 29)
323 helpColumn = 20;
324 conditionWindow = UiWindow_Create(helpColumn, 8, 10, 3, 6);
325 } else if (System_GetLanguage() == 3) {
326 if (anchor + 7 > 29)
327 helpColumn = 19;
328 conditionWindow = UiWindow_Create(helpColumn, 8, 11, 3, 6);
329 } else {
330 if (anchor + 5 > 29)
331 helpColumn = 21;
332 conditionWindow = UiWindow_Create(helpColumn, 8, 9, 3, 6);
333 }
334#elif (LANGUAGE == LANGUAGE_FRENCH)
335 if (anchor + 5 > 29)
336 helpColumn = 21;
337 conditionWindow = UiWindow_Create(helpColumn, 8, 9, 3, 6);
338#elif (LANGUAGE == LANGUAGE_JAPANESE)
339 if (anchor + 4 > 29)
340 helpColumn = 22;
341 conditionWindow = UiWindow_Create(helpColumn, 8, 8, 3, 6);
342#else
343 if (anchor + 4 > 29)
344 helpColumn = 22;
345 conditionWindow = UiWindow_Create(helpColumn, 8, 9, 3, 6);
346#endif
347 }
349 UiText_DrawCharacterAtOffset(MsgTargetSetDjinni, conditionWindow, 0,
350 0);
352 break;
353 case 1: {
354 s32 anchor;
355
356 anchor = selectedPosition.x / 8;
357 helpColumn = anchor - 7;
358 if (anchor + 6 > 29)
359 helpColumn = 17;
360 conditionWindow = UiWindow_Create(helpColumn, 8, 13, 3, 6);
361 }
362#if (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_ITALIAN)
363 switch (System_GetLanguage()) {
364 case 1:
365 UiText_DrawStringAtOffset((u8 *)gOwnerStatus_KpString,
366 conditionWindow, 0, 0);
367 break;
368 case 2:
369 UiText_DrawStringAtOffset((u8 *)gOwnerStatus_PvString,
370 conditionWindow, 0, 0);
371 break;
372 case 3:
373 UiText_DrawStringAtOffset((u8 *)gOwnerStatus_PvString,
374 conditionWindow, 0, 0);
375 break;
376 case 4:
378 conditionWindow, 0, 0);
379 break;
380 }
381#else
383 conditionWindow, 0, 0);
384#endif
385 UiText_DrawNumberInWindow(unit->hp, 4, conditionWindow, 16, 0);
387 conditionWindow, 48, 0);
388 UiText_DrawNumberInWindow(unit->maxHp, 4, conditionWindow, 56, 0);
389 break;
390 case 2: {
391 s32 anchor;
392
393 anchor = selectedPosition.x / 8;
394 helpColumn = anchor - 7;
395 if (anchor + 6 > 29)
396 helpColumn = 17;
397 conditionWindow = UiWindow_Create(helpColumn, 8, 13, 3, 6);
398 }
400 conditionWindow, 0, 0);
401 UiText_DrawNumberInWindow(unit->pp, 4, conditionWindow, 16, 0);
403 conditionWindow, 48, 0);
404 UiText_DrawNumberInWindow(unit->maxPp, 4, conditionWindow, 56, 0);
405 break;
406 case 5: {
407 s32 anchor;
408
409 anchor = selectedPosition.x / 8;
410 helpColumn = anchor - 7;
411 if (anchor + 5 > 29)
412 helpColumn = 18;
413 conditionWindow = UiWindow_Create(helpColumn, 8, 12, 3, 6);
414 }
415 if (unit->hp != 0)
416 goto no_condition;
417 UiText_DrawCharacterAtOffset(MsgTargetDowned, conditionWindow, 0,
418 0);
419 break;
420 case 3: {
421 s32 anchor;
422
423 anchor = selectedPosition.x / 8;
424 helpColumn = anchor - 7;
425 if (anchor + 5 > 29)
426 helpColumn = 18;
427 conditionWindow = UiWindow_Create(helpColumn, 8, 12, 3, 6);
428 }
429 if (unit->poison == 0)
430 goto no_condition;
431 UiText_DrawCharacterAtOffset(MsgTargetCurePoison, conditionWindow,
432 0, 0);
433 break;
434 case 4:
435 count = 0;
436 if (unit->delusion != 0)
437 count = 1;
438 if (unit->stun != 0)
439 count++;
440 if (unit->sleep != 0)
441 count++;
442 if (unit->psySeal != 0)
443 count++;
444 if (unit->grimReaper != 0)
445 count++;
446 if (count == 0)
447 count = 1;
448 helpRow = 9 - count;
449 if (helpRow <= 3)
450 helpRow = 4;
451 {
452 s32 anchor;
453
454 anchor = selectedPosition.x / 8;
455 helpColumn = anchor - 7;
456#if (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_ITALIAN)
457 if (System_GetLanguage() == 3) {
458 if (anchor + 10 > 29)
459 helpColumn = 13;
460 conditionWindow =
461 UiWindow_Create(helpColumn, helpRow, 17, count + 2, 6);
462 } else {
463 if (anchor + 9 > 29)
464 helpColumn = 14;
465 conditionWindow =
466 UiWindow_Create(helpColumn, helpRow, 16, count + 2, 6);
467 }
468#elif (LANGUAGE == LANGUAGE_JAPANESE)
469 if (anchor + 7 > 29)
470 helpColumn = 16;
471 conditionWindow =
472 UiWindow_Create(helpColumn, helpRow, 14, count + 2, 6);
473#else
474 if (anchor + 9 > 29)
475 helpColumn = 14;
476 conditionWindow =
477 UiWindow_Create(helpColumn, helpRow, 16, count + 2, 6);
478#endif
479 }
480 count = 0;
481 if (unit->delusion != 0) {
482 UiText_DrawCharacterAtOffset(MsgTargetCureDelusion,
483 conditionWindow, 0, 0);
484 count = 1;
485 }
486 if (unit->stun != 0) {
487 UiText_DrawCharacterAtOffset(MsgTargetCureStun, conditionWindow,
488 0, count * 8);
489 count++;
490 }
491 if (unit->sleep != 0) {
492 UiText_DrawCharacterAtOffset(MsgTargetCureSleep, conditionWindow,
493 0, count * 8);
494 count++;
495 }
496 if (unit->psySeal != 0) {
497 UiText_DrawCharacterAtOffset(MsgTargetCureSeal, conditionWindow,
498 0, count * 8);
499 ++count;
500 }
501 if (unit->grimReaper != 0) {
502 UiText_DrawCharacterAtOffset(MsgTargetCureCurse, conditionWindow,
503 0, count * 8);
504 count++;
505 }
506 if (count == 0)
507 goto no_condition;
508 break;
509 default:
510 break;
511 case 6:
512 count = 0;
513 if (unit->poison != 0)
514 count = 1;
515 if (unit->delusion != 0)
516 count++;
517 if (unit->stun != 0)
518 count++;
519 if (unit->sleep != 0)
520 count++;
521 if (unit->psySeal != 0)
522 count++;
523 if (unit->grimReaper != 0)
524 count++;
525 if (unit->haunt != 0)
526 count++;
527 if (count == 0)
528 count = 1;
529 helpRow = 9 - count;
530 if (helpRow <= 3)
531 helpRow = 4;
532 {
533 s32 anchor;
534
535 anchor = selectedPosition.x / 8;
536 helpColumn = anchor - 7;
537#if (LANGUAGE == LANGUAGE_SPANISH) || (LANGUAGE == LANGUAGE_ITALIAN)
538 if (System_GetLanguage() == 3) {
539 if (anchor + 10 > 29)
540 helpColumn = 13;
541 conditionWindow =
542 UiWindow_Create(helpColumn, helpRow, 17, count + 2, 6);
543 } else {
544 if (anchor + 9 > 29)
545 helpColumn = 14;
546 conditionWindow =
547 UiWindow_Create(helpColumn, helpRow, 16, count + 2, 6);
548 }
549#elif (LANGUAGE == LANGUAGE_JAPANESE)
550 if (anchor + 7 > 29)
551 helpColumn = 16;
552 conditionWindow =
553 UiWindow_Create(helpColumn, helpRow, 14, count + 2, 6);
554#else
555 if (anchor + 9 > 29)
556 helpColumn = 14;
557 conditionWindow =
558 UiWindow_Create(helpColumn, helpRow, 16, count + 2, 6);
559#endif
560 }
561 count = 0;
562 if (unit->poison != 0) {
563 UiText_DrawCharacterAtOffset(MsgTargetCurePoison, conditionWindow,
564 0, 0);
565 count = 1;
566 }
567 if (unit->delusion != 0) {
568 UiText_DrawCharacterAtOffset(MsgTargetCureDelusion,
569 conditionWindow, 0, count * 8);
570 count++;
571 }
572 if (unit->stun != 0) {
573 UiText_DrawCharacterAtOffset(MsgTargetCureStun, conditionWindow,
574 0, count * 8);
575 count++;
576 }
577 if (unit->sleep != 0) {
578 UiText_DrawCharacterAtOffset(MsgTargetCureSleep, conditionWindow,
579 0, count * 8);
580 count++;
581 }
582 if (unit->psySeal != 0) {
583 UiText_DrawCharacterAtOffset(MsgTargetCureSeal, conditionWindow,
584 0, count * 8);
585 count++;
586 }
587 if (unit->grimReaper != 0) {
588 UiText_DrawCharacterAtOffset(MsgTargetCureCurse, conditionWindow,
589 0, count * 8);
590 count++;
591 }
592 if (unit->haunt != 0) {
593 UiText_DrawCharacterAtOffset(MsgTargetCureHaunt, conditionWindow,
594 0, 8 * count);
595 count++;
596 }
597 if (count != 0)
598 break;
599 no_condition:
601 UiText_DrawCharacterAtOffset(MsgTargetNoEffect, conditionWindow, 0,
602 0);
604 break;
605 }
606 }
607 } else if (spread != ABILITY_RANGE_ALL) {
608 target = UnitGetFar(candidateIds[targetIndex]);
610 candidateIds[targetIndex],
611 EffectPosition_Coordinates(&namePosition));
612 namePosition.y += Trig_Sin(gFrameCount << 12) / 32768;
613 if (target->classId == 125 || target->classId == 122) {
614 width = MsgMysteryPersonName;
615 if (target->classId == 125)
616 width++;
617 TARGET_MYSTERY_NAME_COPY(width, targetName,
619 } else {
620 for (i = 0; i <= 13; i++) {
621 targetName[i] = n = target->name[i];
622 if (!n)
623 break;
624 }
625 targetName[i] = 0;
626 }
627 width = UiText_GetWideStringWidth(targetName);
628 namePosition.x -= width / 2;
629 namePosition.x -= 8;
630 if (namePosition.x + width > 224)
631 namePosition.x = 224 - width;
632 if (namePosition.x < 0)
633 namePosition.x = 0;
635 UiText_RenderWideStringAtOffset(targetName, window, namePosition.x, 4);
636 }
637 redraw &= ~1;
638 }
639 if (drawCursors != 0) {
640 for (i = 1; i < targetCount; i++) {
641 marker = &firstSprite[i];
642 track = &cursorTracks[spreadTrackIndices[i]];
644 spreadActorIds[i], EffectPosition_Coordinates(&markerPosition));
645 markerPosition.y =
646 markerPosition.y + Trig_Sin(gFrameCount << 12) / 32768;
647 *marker = *firstSprite;
648 if (track->flags & 1) {
649 s32 x = (markerPosition.x + track->x) / 2;
650 s32 y = (markerPosition.y + track->y) / 2;
651
652 markerPosition.x = x;
653 track->x = x;
654 markerPosition.y = y;
655 track->y = y;
656 } else {
657 s32 x = marker->x;
658 s32 y = marker->y + 8;
659
660 track->flags = 1;
661 markerPosition.x = x;
662 track->x = x;
663 markerPosition.y = y;
664 track->y = y;
665 }
666 marker->mode = 1;
667 marker->x = markerPosition.x - 8;
668 marker->y = markerPosition.y - 12;
669 if (spread == ABILITY_RANGE_ALL)
670 marker->affine = 0;
671 else
672 marker->affine = 1;
673 marker->param = matrix;
674 Runtime_PushSlotEntry((s32 *)marker, 240);
675 }
676 }
677 pressed = gKeysPressed;
678 repeated = gKeysRepeat;
680 repeated = 0;
681 pressed = 0;
684 repeated = 1;
685 pressed = 1;
686 } else {
688 }
689 }
690 if (pressed & KEY_A) {
691 s32 id;
692 s32 k;
693
694 id = candidateIds[targetIndex];
695 redraw = 0;
696 result = -1;
697 for (k = 0; k <= 5 && battle->partyUnits[k] != 0xFF; k++) {
698 if (battle->partyUnits[k] == id) {
699 result = 0x100 | k;
700 break;
701 }
702 }
703 if (result < 0) {
704 for (k = 0; k <= 5 && battle->enemyIds[k] != 0xFF; k++) {
705 if (battle->enemyIds[k] == id) {
706 result = 0x180 | k;
707 break;
708 }
709 }
710 }
711 targetIndex = result;
712 } else if (spread != ABILITY_RANGE_ALL) {
713 if (repeated & (KEY_DOWN | KEY_RIGHT)) {
715 do {
716 targetIndex++;
717 targetIndex %= candidateCount;
718 } while (candidateIds[targetIndex] == 0xFE);
719 redraw |= 1;
720 }
721 if (repeated & (KEY_UP | KEY_LEFT)) {
723 do {
724 targetIndex = targetIndex + candidateCount - 1;
725 targetIndex %= candidateCount;
726 } while (candidateIds[targetIndex] == 0xFE);
727 redraw |= 1;
728 }
729 }
730 if (gLinkCountdownWork->timer == 0 || (pressed & KEY_B)) {
732 targetIndex = -1;
733 break;
734 }
735 WaitFrames(1);
736 if (redraw == 0)
737 break;
738 }
739 WaitFrames(1);
740 Resource_ResetEntry(cursorSlot);
741 if (conditionWindow != 0)
742 UiWork_Finalize(conditionWindow, 1);
743 UiWork_Finalize(window, 1);
744 BattlePres_SetActorModesFar(candidateIds, 0);
746 WaitFrames(1);
747 return targetIndex;
748}
What every part of the menu object shares: its includes, edition values, macros and the link round st...
#define ABILITY_RANGE_ALL
BattleAct.range of an ability that reaches every unit on the side, each as if aimed at.
Definition battle_unit.h:46
struct BattleWork * gBattleWork
static s32 * EffectPosition_Coordinates(struct EffectPosition *position)
Returns the position's three coordinates as an array.
Definition effect_step.h:68
void BattlePres_SetActorModesFar(u16 *actors, s32 mode)
s32 BattleMotion_ProjectConditionalPositionFar(s32 id, s32 *projected)
struct BattleUnit * UnitGetFar(u32 owner)
s32 GameFlag_TestFar(s32 flag)
void AudioCommand_PlayFar(s32 id)
s32 Trig_Sin(s32 angle)
#define FLAG_LINK_BATTLE
Definition flags.h:68
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
volatile u32 gFrameCount
Frames WaitFrames has finished.
s32 UiText_GetWideStringWidth(u16 *text)
Returns the width in pixels of a zero-terminated string of 16-bit character codes: 4 for a space,...
Definition glyphs.c:218
void UiText_RenderWideStringAtOffset(u16 *text, struct UiWindow *window, s32 x, s32 y)
Draws a zero-terminated string of 16-bit character codes into a text canvas window at pixel (x,...
Definition glyphs.c:258
@ KEY_UP
Definition io_reg.h:249
@ KEY_B
Definition io_reg.h:244
@ KEY_LEFT
Definition io_reg.h:248
@ KEY_RIGHT
Definition io_reg.h:247
@ KEY_A
Definition io_reg.h:243
@ KEY_DOWN
Definition io_reg.h:250
volatile u32 gKeysPressed
Keys that went down this frame.
volatile u32 gKeysRepeat
Pressed keys, repeating while held: after 19 frames, then every 6.
struct BattleMenuWork * gLinkCountdownWork
const u8 gMenu_AnimatedCursorTiles[]
s32 AffineMatrix_BuildForEffect(struct AffineTransform *source)
Hands out the next OAM affine matrix for a sprite drawn with source's scale and rotation and fills it...
Definition render.c:27
void Runtime_PushSlotEntry(void *slotEntry, s32 slot)
Links a sprite entry into depth slot slot (clamped to 0..255) for this frame.
Definition render.c:79
#define SE_MENU_CANCEL
Definition songs.h:106
#define SE_MENU_CURSOR_MOVE
Definition songs.h:104
A sprite's rotation and scale: scales in 8.8 fixed point (0x100 is full size), the angle in 1/65536 t...
Definition affine.h:13
u16 angle
1/65536 turns
Definition affine.h:16
u16 scaleY
8.8 fixed point, read signed
Definition affine.h:15
u16 scaleX
8.8 fixed point, read signed: negative mirrors the sprite
Definition affine.h:14
The battle command menus' work, heap slot 57 (0x104 bytes), reached as gLinkCountdownWork.
Definition menu_list.h:107
s32 tutorialDelay
frames until the tutorial's next key press
Definition menu_list.h:142
s32 timer
Frames left to choose commands in a link battle; -1 outside link battles.
Definition menu_list.h:131
s32 targetSceneOffset
Where the battle scene slides to (Camera_ConfigureScene's vertical offset): -2 or 16 by the target me...
Definition menu_list.h:115
s32 tutorialStep
The Djinn tutorial's step: 0 off, 1 from the start of each round while FLAG_DJINN_TUTORIAL is set,...
Definition menu_list.h:141
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 grimReaper
The Grim Reaper's countdown: 7 when cursed, down one at each turn's end; at 0 the unit falls.
u8 psySeal
bits: 7..3 Luff (16 when cast, minus 8 a turn) | 2..0 Bind's turns (7 when cast).
s16 maxPp
0 to 1999 for a character, 9999 for an enemy (UnitCalc)
s8 poison
1 poisoned, 2 deadly poison
u8 name[0x0f]
The name, one byte a character, ending in 0.
u8 haunt
set while an evil spirit grips the unit
s16 hp
current HP, 0 when downed
s16 pp
current PP
u8 classId
Who the unit is: a character's id (ISAAC to JENNA, below 8) or an enemy's id (BattleUnitSet).
s16 maxHp
0 to 1999 for a character, 9999 for an enemy (UnitCalc)
u8 delusion
Delusion to sleep: the turns each status has left, 7 when it lands; each may end sooner at the end of...
The battle's own work: its enemies, units and their objects, the turn's actions, the spoils,...
Definition battle_work.h:71
s16 partyUnits[7]
ends with BATTLE_UNIT_LIST_END
s16 enemyIds[BATTLE_ENEMY_CAPACITY+1]
ends with BATTLE_UNIT_LIST_END
A target marker's track: where it is, its state flags and the target it follows.
Definition targets.c:17
u8 y
screen pixels
Definition targets.c:19
u8 x
screen pixels
Definition targets.c:18
A point projected onto the screen, with its depth.
Definition effect_step.h:46
s32 y
screen pixels
Definition effect_step.h:59
s32 x
screen pixels
Definition effect_step.h:58
A selection screen sprite: a list link and its OAM attributes.
Definition select.h:13
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
u16 y
tiles
Definition uiwindow.h:16
u16 width
tiles
Definition uiwindow.h:13
void UiText_DrawStringAtOffset(const u8 *text, const struct UiWindow *work, s32 offsetX, s32 offsetY)
Draws a byte string as tiles in window work at pixel (offsetX, offsetY) of its interior.
Definition text_tiles.c:614
void UiText_DrawNumberInWindow(s32 value, s32 width, struct UiWindow *window, s32 x, s32 y)
Draws a number (width as UiText_FormatNumber) into a window's tilemap at pixel (x,...
Definition text_tiles.c:678
void UiWork_SetParamNibble(s32 param)
Sets the text colour (0 to 15) of drawn text.
Definition text_tiles.c:524
void UiText_DrawCharacterAtOffset(s32 message, const struct UiWindow *window, u32 offsetX, u32 offsetY)
Decodes a message and draws it as tiles in a window at pixel (offsetX, offsetY) of its interior.
Definition text_tiles.c:552
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
unsigned short u16
Definition types.h:13
unsigned char u8
Definition types.h:11
const u8 gOwnerStatus_HpString[]
const u8 gOwnerStatus_PpString[]
const u8 gOwnerStatus_SlashString[]
s32 System_GetLanguage(void)
s32 VramCache_AllocEntry(s32 size)
Takes a free cache entry, gives it size bytes of sprite VRAM and returns its number.
Definition vram_cache.c:227
s32 VramCache_Reload(s32 no, s32 src)
Copies src again into entry no's VRAM, keeping its size.
Definition vram_cache.c:238
s32 Resource_ResetEntry(u32 no)
Frees cache entry no: its blocks go back and the entry is marked free (offset 0xffff).
Definition vram_cache.c:106
#define TARGET_MYSTERY_NAME_CAPACITY
Definition targets.c:12
s32 BattleTarget_RunSelection(s32 selection, s32 mode, u32 spread, u32 kind)
Chooses a party/enemy target and animates its selection markers.
Definition targets.c:27
#define TARGET_MYSTERY_NAME_COPY
Definition targets.c:11
s32 Ui_ClearVramBlock(void)
Clears the text canvas to transparent.
Definition windows.c:614
void UiWork_Finalize(struct UiWindow *window, s32 instant)
Closes a window: clears its contents and remembers its last rectangle.
Definition windows.c:349
struct UiWindow * UiWindow_Create(s32 x, s32 y, s32 width, s32 height, s32 attrs)
Opens a window at tile (x, y) of width by height tiles in the first free window slot,...
Definition windows.c:279