Alchemy
Loading...
Searching...
No Matches

djinn_preview.c

Go to the documentation of this file.
1/// @file
2/// The preview of what setting or removing a Djinni changes.
3/// Part of battle_command.c, the menu object, compiled only through it.
4
6
7/// The decimal formatter writes eleven characters plus a terminator. Keep
8/// the original two trailing bytes in the widened copy as well.
9#define NUMBER_TEXT_WIDE_LENGTH 14
10
11// Formats a decimal number, then widens the first 14 shared-buffer bytes
12// into halfwords. The caller supplies space for 14 output halfwords.
13// Void changes pop {r1}; bx r1 to r0 and reorders three preview argument pairs.
14#ifdef UBFIX
15void UiText_FormatNumberToHalfwords(s16 *out, s32 value) {
16#else
18#endif
19 s16 *destination;
20 s32 remaining;
21 u8 *source;
22
23 destination = out;
25 source = gNumberTextBuffer;
26 for (remaining = 0; remaining < NUMBER_TEXT_WIDE_LENGTH; remaining++) {
27 *destination++ = *source++;
28 }
29}
30
31// Preview marks are separate from the action ID in the low fourteen bits.
32#define DJINN_PREVIEW_ABILITY_GAINED 0x8000
33#define DJINN_PREVIEW_ABILITY_LOST 0x4000
34#define DJINN_PREVIEW_ABILITY_CAPACITY 48
35
36/// List Psynergy in the new grid, marking newly gained entries with bit 15,
37/// then append lost entries with bit 14. Return the number of entries and
38/// report the gained/lost counts separately.
40 const struct OwnerActionSlot *newGrid, u16 *out,
41 s32 *gained, s32 *lost) {
42 s32 i;
43 s32 j;
44 s32 count;
45 s32 gainedCount;
46 s32 lostCount;
47
48 gainedCount = 0;
49 lostCount = 0;
50 count = 0;
51 for (i = 0; i < 32 && newGrid[i].encodedAction != 0; i++) {
52 if (BattleActGet(newGrid[i].encodedAction)->flags & 0x80) {
53 out[count] = newGrid[i].encodedAction & ACTION_ID_MASK;
54 count++;
55 for (j = 0; j < 32; j++) {
56 if (((newGrid[i].encodedAction ^ oldGrid[j].encodedAction) &
57 ACTION_ID_MASK) == 0)
58 break;
59 }
60 if (j == 32) {
61 gainedCount++;
62 out[count - 1] |= DJINN_PREVIEW_ABILITY_GAINED;
63 }
64 }
65 }
66 for (i = 0; i < 32 && oldGrid[i].encodedAction != 0; i++) {
67 if (BattleActGet(oldGrid[i].encodedAction)->flags & 0x80) {
68 for (j = 0; j < 32; j++) {
69 if (((oldGrid[i].encodedAction ^ newGrid[j].encodedAction) &
70 ACTION_ID_MASK) == 0)
71 break;
72 }
73 if (j == 32) {
74 lostCount++;
75 out[count] = (oldGrid[i].encodedAction & ACTION_ID_MASK) |
77 count++;
78 }
79 }
80 }
81 *gained = gainedCount;
82 *lost = lostCount;
83 return count;
84}
85
86// Loads an ability icon into the first free VRAM cache slot and requests a
87// 16x16 sprite at pixel (x, y) inside the window. Skips a full VRAM cache.
88// The matching return type keeps pop {r1}; bx r1; void uses r0 instead.
89#ifdef UBFIX
90void Ui_CreateOutputFromResourceSlot(struct UiWindow *window, s32 x, s32 y,
91 s32 abilityId) {
92#else
94 s32 abilityId) {
95#endif
96 s32 iconSlot;
97
98 iconSlot = Resource_FindFreeEntry();
99 if (iconSlot != VRAM_CACHE_ENTRY_COUNT) {
100 Resource_LoadIndexedEntryToBuffer(abilityId, iconSlot);
101 // If the output pool is full, the creator releases iconSlot itself.
102 RenderOutput_Create(iconSlot, OUTPUT_SIZE_16X16, window, x, y);
103 }
104}
105
106/// Temporarily toggle a Djinni and recalculate its owner's stats to show the
107/// change. Page 0 compares stats; later pages show gained and lost Psynergy.
108/// Restore the complete unit snapshot before returning the preview window.
110 s32 encodedDjinn, s32 page,
111 s32 *pageCount) {
112 struct BattleUnit *unit;
113 struct BattleUnit *savedUnit;
114 struct BattleAct *ability;
115 s16 *numberText;
116 s32 oldHp;
117 s32 oldPp;
118 s32 oldAtk;
119 s32 oldDef;
120 s32 oldAgi;
121 u16 *changedAbilities;
122 s32 abilityCount;
123 s32 visibleRow;
124 s32 summaryRows;
125 s32 visibleRows;
126 s32 element;
127 s32 djinnIndex;
128 s32 pages;
129 s32 gained;
130 s32 lost;
131 s32 rangePattern;
132 s32 rising;
133 s32 i;
134
135 // Attach the rising/falling stat arrow to the preview window captured here.
136 void DjinnMenu_DrawStatArrow(s32 x, s32 y, s32 rising) {
137 struct RenderOutput *output = RenderOutput_AcquireFree();
138 struct PreviewSprite *entry;
139 u32 zero;
140 if (output) {
141 output->active = 1;
142 output->kind = 1;
143 /* FAKEMATCH: this char store and the two typed stores below keep the
144 index store ahead of the attribute word; as plain stores, 2
145 instructions differ in tbs-en. */
146 *(s8 *)&output->index = VramCache_AllocEntry(128);
147 entry = (struct PreviewSprite *)((u8 *)output + 16);
148 output->sentinel = 240;
149 output->x = 120;
150 output->y = 120;
151 // UB: the two stores below write the attribute and tile words through
152 // pointer types; UBFIX writes the unions' words.
153#ifdef UBFIX
154 entry->attributes.word = 0x40000400;
155 zero = 0;
156 entry->tile.value = zero;
157#else
158 /* FAKEMATCH: typed like the window pointer, the word store waits for the
159 * window load but not for the x and y stores, so sched2 places the entry
160 * copy before them */
161 *(struct UiWindow **)&entry->attributes = (struct UiWindow *)0x40000400;
162 zero = 0;
163 /* FAKEMATCH: a pointer-typed clear does not wait for the x and y stores
164 * (sched2 order) */
165 *(void **)&entry->tile = (void *)zero;
166#endif
167 entry->attributes.bits.x = win->x * 8 + x;
168 entry->attributes.bits.y = win->y * 8 + y;
169 /* FAKEMATCH(dead): advancing and restoring the captured pointer keeps
170 * the window chain in r9; without the pair, 8 of 80 instructions of
171 * the nested DjinnMenu_DrawStatArrow differ in tbs-en. */
172 win++;
173 win--;
174 entry->tile.bits.index = VramCache_Reload(
175 (u8)output->index,
178 }
179 }
180
181 unit = UnitGetFar(owner);
182 if (win != 0)
183 UiWork_Finalize(win, 1);
184
185 if (page == 0)
186 win = UiWindow_Create(0, 8, 21, 11, 6);
187 else
188 win = UiWindow_Create(0, 5, 21, 14, 6);
189 if (win == 0)
190 return 0;
191
192 // Save the unit before the temporary Djinn toggle changes its class/stats.
193 // Fourteen widened characters fit well inside the 64-halfword text buffer.
194 // With a valid refreshed old list, class rebuilding can remove at most
195 // sixteen actions, including any equipment actions displaced by the class.
196 // Its new 32-slot list and these lost entries fit the 48-entry allocation.
197 numberText = MemAlloc(64 * sizeof(*numberText));
198 savedUnit = MemAlloc(sizeof(*savedUnit));
199 changedAbilities = Runtime_BumpAllocateAlternatePool(
200 DJINN_PREVIEW_ABILITY_CAPACITY * sizeof(*changedAbilities));
201 Iwram_CopyWords(savedUnit, unit, sizeof(*savedUnit));
202
203 element = (encodedDjinn >> 8) & 15;
204 djinnIndex = encodedDjinn & 255;
205 if (Djinn_IsActiveFar(owner, element, djinnIndex) != 0)
206 Djinn_DeactivateFar(owner, element, djinnIndex);
207 else
208 Djinn_ActivateFar(owner, element, djinnIndex);
209 UnitCalcFar(owner);
210
211 abilityCount =
213 changedAbilities, &gained, &lost);
214 pages = ((abilityCount - 1) / 5) + 1;
215 *pageCount = pages;
216 if (page * 5 - 5 >= abilityCount)
217 page = pages;
218
219 // The first page compares stats; the remaining pages compare Psynergy.
220 if (page == 0) {
221 s32 label = MsgPreviewStatLabel;
222
223 UiText_DrawCharacterAtOffset(label, win, 0, 8);
224 UiText_DrawCharacterAtOffset(label + 1, win, 0, 16);
225 UiText_DrawCharacterAtOffset(label + 2, win, 0, 24);
226 UiText_DrawCharacterAtOffset(label + 3, win, 0, 32);
227 UiText_DrawCharacterAtOffset(label + 4, win, 0, 40);
228 UiText_DrawCharacterAtOffset(label + 5, win, 0, 48);
229
230 oldHp = savedUnit->hp;
231 UiText_FormatNumberToHalfwords(numberText, oldHp);
233 1);
234 oldPp = savedUnit->pp;
235 UiText_FormatNumberToHalfwords(numberText, oldPp);
237 2);
238 oldAtk = savedUnit->atk;
239 UiText_FormatNumberToHalfwords(numberText, oldAtk);
242 win, DJINN_PREVIEW_STAT_X, 3);
243 oldDef = savedUnit->def;
244 UiText_FormatNumberToHalfwords(numberText, oldDef);
247 win, DJINN_PREVIEW_STAT_X, 4);
248 oldAgi = savedUnit->agi;
249 UiText_FormatNumberToHalfwords(numberText, oldAgi);
252 win, DJINN_PREVIEW_STAT_X, 5);
253 UiText_FormatNumberToHalfwords(numberText, savedUnit->luck);
254 UiText_RenderWideStringInWindow(numberText + 8, win, 5, 6);
255
256 UiWindow_DrawDividerLine(win, 0, 8, 19, 8);
257 if (gained != 0 || lost != 0)
259 UiText_DrawCharacterAtOffset(MsgBattleAdditionalPsynergyHelp, win,
262 }
263
264 if (page > 0) {
265 summaryRows = 0;
266 visibleRows = abilityCount;
267 if (abilityCount > 4)
268 visibleRows = 5;
269 i = page * 5 - 5;
270 for (visibleRow = 0; visibleRow < visibleRows && i < abilityCount;
271 visibleRow++, i++) {
272 ability = BattleActGet(changedAbilities[i]);
273 if (ability->element != ELEMENT_NONE)
274 UiWindow_SetTilemapEntry(win, 0x5001 + ability->element, 15,
275 visibleRow * 2, 0);
276
277 rangePattern = ability->range;
278 if (rangePattern == ABILITY_RANGE_ALL)
279 rangePattern = 11;
280 else
281 rangePattern--;
282 UiWindow_DrawThreeTileColumn(win, 16, visibleRow * 2, rangePattern, 0);
283
284 Ui_CreateOutputFromResourceSlot(win, 0, visibleRow * 16 - 4,
285 changedAbilities[i] & ACTION_ID_MASK);
286 if ((changedAbilities[i] & DJINN_PREVIEW_ABILITY_GAINED) != 0)
288 else if ((changedAbilities[i] & DJINN_PREVIEW_ABILITY_LOST) != 0)
290 else
292
293 UiText_DrawCharacterAtOffset((changedAbilities[i] & ACTION_ID_MASK) +
294 MsgAbilityName,
295 win, 16, visibleRow * 16);
296 UiWindow_SetTilemapEntry(win, 0xf01f, 11, visibleRow * 2, 0);
298 visibleRow * 2, 0);
299 UiText_DrawNumberInWindow(BattleActGet(changedAbilities[i])->ppCost, 2,
300 win, 104, visibleRow * 16);
301 }
302
303 if (gained != 0) {
305 UiText_DrawCharacterAtOffset(MsgPsynergyGained, win, 32, 80);
306 summaryRows = 1;
307 }
308 if (lost != 0) {
310 UiText_DrawCharacterAtOffset(MsgPsynergyLost, win, 32,
311 summaryRows * 8 + 80);
312 summaryRows++;
313 }
314 if (summaryRows == 0)
315 UiText_DrawCharacterAtOffset(MsgPsynergyUnchanged, win, 32, 80);
318 UiWindow_DrawDividerLine(win, 0, 10, 19, 10);
319 }
320
321 if (page == 0) {
322 UiText_DrawCharacterAtOffset(MsgClassName + savedUnit->classIndex, win, 0,
323 0);
324 UiText_DrawCharacterAtOffset(MsgClassName + unit->classIndex, win, 80, 0);
325 if (savedUnit->classIndex != unit->classIndex)
326 UiWindow_SetTilemapEntry(win, 0xf728, 9, 0, page);
327 else
328 UiWindow_SetTilemapEntry(win, 0xf729, 9, 0, page);
329
330 UiText_FormatNumberToHalfwords(numberText, unit->hp);
331 UiText_RenderWideStringInWindow(numberText + 7, win, 11, 1);
332 if (unit->hp != oldHp) {
333 rising = 0;
334 if (unit->hp > oldHp)
335 rising = 1;
336 DjinnMenu_DrawStatArrow(80, 14, rising);
337 }
338
339 UiText_FormatNumberToHalfwords(numberText, unit->pp);
340 UiText_RenderWideStringInWindow(numberText + 7, win, 11, 2);
341 if (unit->pp != oldPp) {
342 rising = 0;
343 if (unit->pp > oldPp)
344 rising = 1;
345 DjinnMenu_DrawStatArrow(80, 22, rising);
346 }
347
348 UiText_FormatNumberToHalfwords(numberText, unit->atk);
349 UiText_RenderWideStringInWindow(numberText + 7, win, 11, 3);
350 if (unit->atk != oldAtk) {
351 rising = 0;
352 if (unit->atk > oldAtk)
353 rising = 1;
354 DjinnMenu_DrawStatArrow(80, 30, rising);
355 }
356
357 UiText_FormatNumberToHalfwords(numberText, unit->def);
358 UiText_RenderWideStringInWindow(numberText + 7, win, 11, 4);
359 if (unit->def != oldDef) {
360 rising = 0;
361 if (unit->def > oldDef)
362 rising = 1;
363 DjinnMenu_DrawStatArrow(80, 38, rising);
364 }
365
366 UiText_FormatNumberToHalfwords(numberText, unit->agi);
367 UiText_RenderWideStringInWindow(numberText + 7, win, 11, 5);
368 if (unit->agi != oldAgi) {
369 rising = 0;
370 if (unit->agi > oldAgi)
371 rising = 1;
372 DjinnMenu_DrawStatArrow(80, 46, rising);
373 }
374
375 UiText_FormatNumberToHalfwords(numberText, unit->luck);
376 UiText_RenderWideStringInWindow(numberText + 8, win, 12, 6);
377 if (unit->luck != savedUnit->luck) {
378 rising = 0;
379 if (unit->luck > savedUnit->luck)
380 rising = 1;
381 DjinnMenu_DrawStatArrow(80, 54, rising);
382 }
383 }
384
385 // Previewing must not change the owner's Djinn, class or battle stats.
386 Iwram_CopyWords(unit, savedUnit, sizeof(*savedUnit));
387 MemFree(changedAbilities);
388 MemFree(savedUnit);
389 MemFree(numberText);
390 return win;
391}
What every part of the menu object shares: its includes, edition values, macros and the link round st...
#define DJINN_PREVIEW_HELP_X
#define PSYNERGY_COST_SECOND_TILE
#define DJINN_PREVIEW_VITALS_X
#define DJINN_PREVIEW_STAT_DIGIT_OFFSET
#define OUTPUT_SIZE_16X16
OAM attribute bits a sprite output is created with: the size field of attribute 1 (bits 30-31 of the ...
#define DJINN_PREVIEW_STAT_X
@ ELEMENT_NONE
Definition battle_unit.h:40
#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
u8 gDjinnMenu_ArrowDownTiles[]
u8 gDjinnMenu_ArrowUpTiles[]
struct BattleUnit * UnitGetFar(u32 owner)
struct BattleAct * BattleActGet(s32 actionId)
Far call to BattleAction_GetDirect.
u32 Djinn_IsActiveFar(s32 owner, s32 index, s32 bit)
u32 Djinn_DeactivateFar(s32 owner, s32 index, s32 bit)
s32 Djinn_ActivateFar(s32 owner, s32 index, s32 bit)
void UnitCalcFar(s32 owner)
void UiText_RenderWideStringInWindow(s16 *text, struct UiWindow *window, s32 x, s32 y)
Draws a zero-terminated string of 16-bit character codes into a window's tilemap at tile (x,...
Definition glyphs.c:409
void * Runtime_BumpAllocateAlternatePool(s32 size)
As MemAlloc, but tries EWRAM first.
Definition heap.c:140
void * MemAlloc(s32 size)
Allocates size bytes (rounded up to words) with no slot, from IWRAM when they fit and EWRAM otherwise...
Definition heap.c:115
static s32 Iwram_CopyWords(volatile void *destination, const volatile void *source, s32 size)
Definition iwram_call.h:85
void MemFree(void *buffer)
u8 gNumberTextBuffer[]
struct RenderOutput * RenderOutput_Create(s32 slot, s32 oamFlags, struct UiWindow *window, s32 offsetX, s32 offsetY)
Creates a sprite output for the VRAM cache slot slot and attaches it to window, placed at (offsetX,...
Definition outputs.c:13
s32 Resource_LoadIndexedEntryToBuffer(s32 resource, s32 index)
Builds the ability glyph in temporary transfer work, reloads the requested cache slot and returns its...
Definition pictures.c:325
int UiWindow_DrawThreeTileColumn(struct UiWindow *window, s32 x, s32 y, s32 pattern, s32 unused)
Definition pictures.c:179
An ability's definition, a row of gBattleAction_DefinitionTable: its targeting, kind,...
u8 range
the aimed unit and range - 1 either side, or ABILITY_RANGE_ALL
u8 element
enum Element
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 luck
0 to 99 (UnitCalc)
s16 hp
current HP, 0 when downed
s16 pp
current PP
struct OwnerActionSlot actionSlots[32]
The action list: Psynergy taught for good, then the class's and the equipment's (Unit_RefreshPsynergy...
u8 classIndex
The character's class, worked out from its Djinn (Unit_UpdateClass); 0 marks an enemy,...
u16 atk
Attack, 0 to 999 (UnitCalc).
u16 agi
Agility, 0 to 999: the order of the round's actions.
u16 def
Defense, 0 to 999 (UnitCalc).
An entry in a unit's action list: the action's id (OWNER_ACTION_ID_MASK) and marks for an action its ...
Definition battle_unit.h:19
u16 encodedAction
bits: 15 from the class | 14 from an equipped item | 13..0 action id.
Definition battle_unit.h:22
The OAM entry of a render output, as the Djinn preview places its stat arrows.
union PreviewSprite::@277316304171272134171205205340246056300000270302 attributes
union RenderTableValue tile
A list of render outputs: its first output and the link to append after.
A render output: a sprite in a window's output list (a character, an icon or a portrait),...
u16 y
screen pixels
u8 sentinel
depth slot it is drawn at (254 for glyphs)
s8 active
its UI_SPRITE_ motion; 0 while free
u16 x
screen pixels
s8 kind
0 for a glyph; otherwise it owns VRAM cache slot index, freed with it, and 2 (a portrait) also its pa...
s8 index
VRAM cache slot, or a glyph's tile after glyphTiles.
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 x
tiles
Definition uiwindow.h:15
struct RenderOutputList output
the sprites the window carries
Definition uiwindow.h:12
void NumberText_FormatDecimal(s32 number)
Writes number to the number buffer as eleven characters: spaces, a sign (a space or '-') and its deci...
Definition text.c:32
void UiWindow_DrawDividerLine(struct UiWindow *win, u32 x1, u32 y1, u32 x2, u32 y2)
Draws a straight divider line inside a window from tile (x1, y1) to (x2, y2), horizontal or vertical:...
Definition text_tiles.c:365
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
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
unsigned char u8
Definition types.h:11
struct RenderTableValue::@007120236370026233101065137137050014126142057215 bits
#define ACTION_ID_MASK
Definition window.h:269
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_FindFreeEntry(void)
Returns the number of the first free cache entry, or VRAM_CACHE_ENTRY_COUNT when all are taken.
Definition vram_cache.c:211
@ VRAM_CACHE_ENTRY_COUNT
Definition vram_tab.h:12
s32 DjinnMenu_ListChangedDjinn(const struct OwnerActionSlot *oldGrid, const struct OwnerActionSlot *newGrid, u16 *out, s32 *gained, s32 *lost)
List Psynergy in the new grid, marking newly gained entries with bit 15, then append lost entries wit...
#define NUMBER_TEXT_WIDE_LENGTH
The decimal formatter writes eleven characters plus a terminator.
#define DJINN_PREVIEW_ABILITY_LOST
#define DJINN_PREVIEW_ABILITY_CAPACITY
s32 Ui_CreateOutputFromResourceSlot(struct UiWindow *window, s32 x, s32 y, s32 abilityId)
s32 UiText_FormatNumberToHalfwords(s16 *out, s32 value)
#define DJINN_PREVIEW_ABILITY_GAINED
struct UiWindow * DjinnMenu_ShowChangePreview(struct UiWindow *win, s32 owner, s32 encodedDjinn, s32 page, s32 *pageCount)
Temporarily toggle a Djinni and recalculate its owner's stats to show the change.
void UiWindow_SetTilemapEntry(struct UiWindow *window, s32 value, s32 x, s32 y, u32 mode)
Writes value into a window's tilemap at tile (x, y) of its interior, with the palette mode names (TIL...
Definition sprites.c:345
void RenderOutput_AppendToList(struct RenderOutputList *list, struct RenderOutput *node)
Adds a render output to the end of a list; NULL lists are ignored.
Definition windows.c:478
void UiWork_Finalize(struct UiWindow *window, s32 instant)
Closes a window: clears its contents and remembers its last rectangle.
Definition windows.c:349
struct RenderOutput * RenderOutput_AcquireFree(void)
Takes the first render output from the free list, or returns NULL when every output is in use.
Definition windows.c:9
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