Alchemy
Loading...
Searching...
No Matches

lucky_dice.c

Go to the documentation of this file.
3#include "battle.h"
4#include "debug.h"
5#include "field.h"
6#include "game.h"
7#include "graphics.h"
8#include "menu.h"
9#include "minigame.h"
10#include "sound.h"
11#include "system.h"
12#include "io_reg.h"
13#include "battle_display.h"
14#include "project.h"
15#include "types.h"
16#include "iwram_call.h"
17#include "scene.h"
18#include "edition.h"
19#include "callback_scheduler.h"
20#include "call.h"
21#include "frame.h"
22#include "oambuf.h"
23#include "songs.h"
24#include "macros.h"
25
26/// Starts the Lucky Dice minigame: leaves only the backdrop on screen, starts
27/// song MUS_009 and plays until the player quits. Returns 0. Reached through
28/// Runtime_BlankDisplayAndRunFar from RunLuckyDice (object/object/scene.c),
29/// which saves background palettes 14 and 15 around it and passes an
30/// entrance this function does not take.
35 return 0;
36}
37
38/// Puts the 3D camera back at its scene defaults: at the origin, unturned,
39/// pitched to 152 and 510 units away, following nothing. Recomputes the eye
40/// and sets the projection's focal length (250), near plane and far plane.
41/// Called only by LuckyDice_Run, once the camera's heap block is allocated;
42/// writes gCameraWork's fields and gProjection.
44 struct BattleCamera *camera = gCameraWork;
45 s32 offset[3];
46 u32 nearPlane;
47 u32 focalLength;
48
49 camera->pitch = 152 << 8;
50 camera->distance = 255 << 17;
51 SET_VECTOR(camera->pos, 0, 0, 0);
52 camera->yaw = 0;
53 camera->followPos = 0;
56 camera->unk18 = 0;
57
58 // The eye sits `distance` behind the target along the camera's view.
63
64 SET_VECTOR(offset, 0, 0, camera->distance);
65 Iwram_TransformVector(offset, camera->eye);
66
67 focalLength = 250;
68 focalLength <<= 16;
69 nearPlane = Iwram_RatioMulQ14(focalLength, 192 << 8);
70
71 focalLength = 250;
72 focalLength <<= 16;
73 Projection_Set(focalLength, nearPlane, 0x7fff0000);
74}
75
76/// Multiplies two 8.8 fixed-point values, rounding toward zero.
77UNUSED s32 FixedPoint_Multiply8_8(s32 leftValue, s32 rightValue) {
78 /* FAKEMATCH(temporary): the product built in its own variable from a copy
79 * of right, then rounded into another; as product = left * right adjusted
80 * in place, 4 of 14 instructions differ in tbs-en. */
81 s16 left = leftValue;
82 s16 right = rightValue;
83 s32 product;
84 s32 roundedProduct;
85 s32 multiplier;
86
87 multiplier = right;
88 product = left;
89 product *= multiplier;
90 if (product >= 0) {
91 roundedProduct = product;
92 } else {
93 roundedProduct = product + 255;
94 }
95 return (s16)(roundedProduct >> 8);
96}
97
98/// Divides two 8.8 fixed-point values.
99UNUSED s16 Math_ScaleByRatio(s16 numerator, s16 denominator) {
100// UB: a signed numerator may be negative. All s16 values times 256 fit.
101#ifdef UBFIX
102 return (numerator * 256) / denominator;
103#else
104 return (numerator << 8) / denominator;
105#endif
106}
107
108/// The reciprocal of an 8.8 fixed-point value.
109UNUSED s16 FixedPoint_Reciprocal(s16 value) { return 0x10000 / value; }
110
111#include "resource.h"
112#include "mem.h"
113#include "dma.h"
114#include "ram_buffer.h"
115#include "fixed_math.h"
116#include "ui.h"
117#include "game_state.h"
118#include "map_scroll.h"
119#include "battle_effect_work.h"
120#include "transform.h"
121#include "resource_ids.h"
122#include "text/msg_ids.h"
123#include "songs.h"
124#include "heap_state.h"
125
126// Lucky Dice window layout, in tiles (and pixels for text), by edition: the
127// coins window at the top, the help window (Throw / Quit) and the winnings
128// window at the bottom, and the result lettering's x in sprite coordinates.
129#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH) || \
130 (LANGUAGE == LANGUAGE_FRENCH)
131#define DICE_COIN_X 17
132#define DICE_COIN_W 13
133#define DICE_WIN_W 13
134#elif (LANGUAGE != LANGUAGE_JAPANESE)
135#define DICE_COIN_X 18
136#define DICE_COIN_W 12
137#define DICE_WIN_W 12
138#else
139#define DICE_COIN_X 18
140#define DICE_COIN_W 11
141#define DICE_WIN_W 10
142#endif
143
144#if (LANGUAGE == LANGUAGE_SPANISH)
145#define DICE_LABEL_X 56
146#elif (LANGUAGE == LANGUAGE_FRENCH)
147#define DICE_LABEL_X 53
148#else
149#define DICE_LABEL_X 48
150#endif
151
152#if (LANGUAGE == LANGUAGE_GERMAN) || (LANGUAGE == LANGUAGE_SPANISH)
153#define DICE_HELP_X 20
154#define DICE_HELP_W 10
155#elif (LANGUAGE == LANGUAGE_FRENCH)
156#define DICE_HELP_X 21
157#define DICE_HELP_W 9
158#elif (LANGUAGE != LANGUAGE_JAPANESE)
159#define DICE_HELP_X 22
160#define DICE_HELP_W 8
161#else
162#define DICE_HELP_X 23
163#define DICE_HELP_W 7
164#endif
165
166#if (LANGUAGE != LANGUAGE_JAPANESE)
167#define DICE_WIN_X 9
168#define DICE_WIN_NUM_X 48
169#define DICE_WIN_LABEL_X DICE_LABEL_X
170#define DICE_KIND_X 0x278
171#else
172#define DICE_WIN_X 10
173#define DICE_WIN_NUM_X 40
174#define DICE_WIN_LABEL_X 40
175#define DICE_KIND_X 0x280
176#endif
177
178/// The used prefix of Lucky Dice's 0x618-byte work block (heap slot 45):
179/// its sprites and windows. The allocation extends beyond these members.
181 u8 filler[200]; ///< Lucky Dice never touches these bytes
182 struct OamShadow obj[128]; ///< copied to OAM each frame
183 struct UiWindow *coinsWindow; ///< the player's coins
184 struct UiWindow *winningsWindow; ///< the winnings of the last throw
185 struct UiWindow *helpWindow; ///< the Throw and Quit buttons
186};
187
188#include "version.h"
189#include "keystate.h"
191
192#define DICE_COUNT 2
193#define DICE_FACE_COUNT 6
194#define DICE_RESULT_COUNT 4
195#define DICE_BET_PER_LEVEL 10
196
197/// What the Lucky Dice screen is doing.
199 LUCKY_DICE_AIM, ///< the dice wait to be thrown; Up and Down raise them
200 LUCKY_DICE_ROLL, ///< the dice fly, bounce and settle
201 LUCKY_DICE_RESULT, ///< the four numbers and the result are shown
202 LUCKY_DICE_PAYOUT ///< the winnings move into the coins
203};
204
205/// Copies count RGB555 colours from source to destination with each channel
206/// multiplied by scale (16.16: 0 is black, 0x10000 unchanged), for fades.
207/// Returns 0. Called only by LuckyDice_Run, to fade both palettes in and out.
208s32 Graphics_ScaleRgb555Buffer(u16 *source, u16 *destination, s32 scale,
209 s32 count) {
210 s32 remaining;
211 u32 redMask;
212 u32 greenMask;
213 u32 blueMask;
214 u32 pixel;
215 u32 red;
216 u32 green;
217 u32 blue;
218
219 if (count > 0) {
220 redMask = 0x1f;
221 greenMask = 0x3e0;
222 blueMask = 0x7c00;
223 remaining = count;
224 do {
225 pixel = *source;
226 red = pixel & redMask;
227 green = pixel & greenMask;
228 blue = blueMask & pixel;
229 red *= scale;
230 green *= scale;
231 blue *= scale;
232 pixel = ((red >> 16) & redMask) | ((green >> 16) & greenMask);
233 pixel |= (blue >> 16) & blueMask;
234 *destination = pixel;
235 source++;
236 destination++;
237 remaining--;
238 } while (remaining != 0);
239 }
240 return 0;
241}
242
243/// Runs the Lucky Dice minigame until the player quits or cannot afford a
244/// throw. Each throw costs ten coins per level of the party's average. Two
245/// dice are thrown onto a board of six rows; each die's face and the row it
246/// stops in give four numbers. Four of a kind pays 5 times the bet (15 or 25
247/// times when it matches one of the two lucky numbers shown at the top),
248/// three of a kind 3 times, two pairs twice and one pair the bet back.
249/// Called by Runtime_BlankDisplayAndRun. Takes the bet from and pays the
250/// winnings into gGameState.coins, resets the scheduler's task table, and
251/// frees the five heap slots it allocates before it returns.
252void LuckyDice_Run(void) {
253 u16 bgPalette[256];
254 s32 spriteDepth[DICE_RESULT_COUNT]; // the two dice, then their shadows
255 // The two faces, then the two rows the dice stopped in.
256 s32 numbers[DICE_RESULT_COUNT];
257 s32 groundPoint[3];
258 s32 screen[3];
259 /* FAKEMATCH(dead): an unused local for the 32 bytes the native frame keeps
260 * at sp+168..200, between screen and the affine halfwords; without it, 21
261 * of 2046 instructions differ in tbs-en. */
262 UNUSED s32 unused[8];
263 /* FAKEMATCH(volatile): four halfwords stored to sp+166..160 and each
264 * reloaded with ldrh, the form the bytes prove; as plain u16 locals, 463 of
265 * 2046 instructions differ in tbs-en. */
266 volatile u16 affineA;
267 volatile u16 affineB;
268 volatile u16 affineC;
269 volatile u16 affineD;
270 s32 spinRate[DICE_COUNT];
271 s32 flashTimer[DICE_COUNT]; // set on a win; nothing here reads it
272 s32 luckyNumbers[DICE_COUNT]; // the two numbers shown at the top, never equal
273 s32 drawOrder[DICE_COUNT]; // the dice, back to front
274// UB: three equal numbers produce three pairs, so the unused result list
275// needs a third slot. Keep the original frame layout in the matching build.
276#ifdef UBFIX
277 UNUSED s32 matchedNumbers[3]; // written, never read
278#else
279 UNUSED s32 matchedNumbers[2]; // written, never read
280#endif
281 u16 *spritePalette;
282 struct BattleEffectWork *diceWork;
283 /* FAKEMATCH(volatile): the OAM shadow it builds is struct OamShadow, whose
284 * words are volatile (oambuf.h); as plain words, 810 of 2046 instructions
285 * differ in tbs-en. */
286 struct LuckyDiceSprites *sprites;
287 struct BattleCamera *camera;
288 s32 frame;
289 s32 state;
290 s32 bet;
291 s32 resultTimer;
292 s32 resultKind;
293 s32 winnings;
294 s32 payoutTimer;
295 // RESULT frame 20 sets the number's digit count before A can enter PAYOUT.
296 // It persists across frames to clear the same numeric field afterward.
297 s32 digits;
298 s32 spriteCount;
299 struct EffectStep *rollingDie;
300 struct EffectStep *otherDie;
301 s32 resetDice; // put the dice back at the throwing line
302 struct UiWindow *window;
303 s32 index;
304 s32 otherIndex;
305 s32 rollAngle; // a die's roll angle: 0x400 per face tile, 0xc00 per face
306 s32 x;
307 s32 y;
308 s32 halfSize;
309 s32 faceTile; // which of a face's three tiles the roll shows
310 s32 sortedDie;
311 s32 tileX;
312 s32 tileY;
313 s32 resourceAddress;
314 s32 separationX;
315 s32 separationY;
316 s32 separationZ;
317 s32 separation;
318 s32 bounceSpeed;
319 u32 bounceAngle;
320 s32 scale;
321 s32 pairCount;
322 s16 affineScale;
323
324 // Allocate the screen's work blocks and reset the camera and tasks.
327 diceWork = Runtime_AllocateBlock(HEAP_SLOT_BATTLE_EFFECT, sizeof(*diceWork));
328 sprites = Runtime_AllocateBlock(45, 0x618);
329 camera = Runtime_AllocateBlock(HEAP_SLOT_CAMERA, sizeof(*camera));
333 gOamCopyEnabled = 0;
334
335 // Fill BG1's map (screen block 6) with a 30-tile-wide picture, then load
336 // the board and dice graphics with their palettes faded to black.
337 index = 0;
338 for (tileY = 0; tileY != 20; tileY++) {
339 for (tileX = 0; tileX != 32; tileX++) {
340 *(volatile u16 *)(BG_SCREEN_BLOCK(6) + index) = tileX + tileY * 30;
341 index += 2;
342 }
343 }
344
345 resourceAddress =
347 Dma_Set((void *)resourceAddress, (void *)BG_PLTT,
348 (DMA_ENABLE | DMA_32BIT) << 16 | 0x70, REG_DMA3);
349 Dma_Set((void *)BG_PLTT, bgPalette, (DMA_ENABLE | DMA_32BIT) << 16 | 0x80,
350 REG_DMA3);
351 resourceAddress += 0x1c0;
352 // UB: Value2 passes integers to the decoder's two pointer parameters.
353 // Plain typed calls add four bytes to LuckyDice_Run in tbs-en.
354#ifdef UBFIX
355 Resource_DecodeType01((const void *)resourceAddress, Ram_MapCellBuffer);
356#else
358#endif
360 (DMA_ENABLE | DMA_32BIT) << 16 | 0x2580, REG_DMA3);
361 resourceAddress =
363 Dma_Set((void *)resourceAddress, spritePalette,
364 (DMA_ENABLE | DMA_32BIT) << 16 | 0x80, REG_DMA3);
365 Dma_Set((void *)resourceAddress, (void *)OBJ_PLTT,
366 (DMA_ENABLE | DMA_32BIT) << 16 | 0x80, REG_DMA3);
367 resourceAddress += 0x200;
368#ifdef UBFIX
369 Resource_DecodeType01((const void *)resourceAddress, Ram_MapCellBuffer);
370#else
372#endif
374 (DMA_ENABLE | DMA_32BIT) << 16 | 0x1f00, REG_DMA3);
375 Graphics_ScaleRgb555Buffer(spritePalette, (u16 *)OBJ_PLTT, 0, 256);
376 Graphics_ScaleRgb555Buffer(bgPalette, (u16 *)BG_PLTT, 0, 256);
377
378 // Show the board on BG1, sprites and windows, with the board blended.
390 REG_BLDALPHA = BLEND_ALPHA(16, 8);
391 gBgScroll[1].x = 0;
392 gBgScroll[1].y = 0;
393 REG_BG1HOFS = 0;
394 REG_BG1VOFS = 0xff60;
395 REG_BG2HOFS = 0;
396 REG_BG2VOFS = 0xff60;
397 REG_WIN0H = WIN_RANGE(0, 240);
398 REG_WIN0V = WIN_RANGE(0, 160);
399 REG_WIN1H = WIN_RANGE(0, 240);
400 REG_WIN1V = WIN_RANGE(0, 160);
401
402 // Start aiming with both dice at the throwing line and two lucky numbers.
404 resultTimer = 0;
405 resultKind = -1;
406 winnings = 0;
407 payoutTimer = 0;
408 // Unsigned arithmetic wraps before storage as signed fixed point. Signed
409 // subtraction grows the English routine from 0x1274 to 0x1280 bytes.
410 for (index = 0; index != DICE_COUNT; index++) {
411 diceWork->particles[index].x = ((Random16() & 127) - 64) << 16;
412 diceWork->particles[index].y = (Random16() & 31) << 16;
413 diceWork->particles[index].z = ((Random16() & 127) - 64) << 16;
414 diceWork->particles[index].velocityX = ((Random16() & 127) - 64) << 14;
415 diceWork->particles[index].velocityY = ((Random16() & 31) + 32) << 12;
416 diceWork->particles[index].velocityZ = ((Random16() & 127) - 64) << 14;
417 diceWork->particles[index].variant = 0;
418 spinRate[index] = 300;
419 }
420 numbers[0] = numbers[1] = numbers[2] = numbers[3] = 0;
421 flashTimer[0] = flashTimer[1] = 0;
422 luckyNumbers[0] = Random16() % DICE_FACE_COUNT;
423 do {
424 luckyNumbers[1] = Random16() % DICE_FACE_COUNT;
425 } while (luckyNumbers[0] == luckyNumbers[1]);
426 diceWork->particles[0].x = 0x280000;
427 diceWork->particles[0].y = 0x600000;
428 diceWork->particles[0].z = -0xa00000;
429 diceWork->particles[1].x = -0x280000;
430 diceWork->particles[1].y = 0x600000;
431 diceWork->particles[1].z = -0xa00000;
432
433 // Open the coins window and the Throw / Quit help.
435 window = UiWindow_CreateFar(DICE_COIN_X, 0, DICE_COIN_W, 3, 6);
436 sprites->coinsWindow = window;
437 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceCoins, window, DICE_LABEL_X, 0);
439 window = UiWindow_CreateFar(DICE_HELP_X, 16, DICE_HELP_W, 4, 6);
440 sprites->helpWindow = window;
441 // MsgLuckyDiceThrow and MsgLuckyDiceQuit, by their offset from the coins
442 // label: written by name, they would share literals the native keeps apart.
443 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceCoins + 10, window, 0, 0);
444 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceCoins + 11, sprites->helpWindow,
445 0, 8);
446
447 frame = 0;
448 for (;;) {
449 resetDice = 0;
450 // Fade in over the first 16 frames.
451 if (frame <= 16) {
452 Graphics_ScaleRgb555Buffer(spritePalette, (u16 *)OBJ_PLTT, frame << 12,
453 256);
454 Graphics_ScaleRgb555Buffer(bgPalette, (u16 *)BG_PLTT, frame << 12, 256);
455 }
457 Transform_SetLookAt(camera->eye, camera->pos);
458
459 // The fixed sprites: the board frame and the two lucky numbers.
460 spriteCount = 0;
461 sprites->obj[spriteCount].attr01 = 0xc0006000;
462 sprites->obj[spriteCount].attr2 = 0x358;
463 spriteCount++;
464 sprites->obj[spriteCount].attr01 = 0x40102020;
465 sprites->obj[spriteCount].attr2 = 0x398;
466 spriteCount++;
467 sprites->obj[spriteCount].attr01 = 0x40102030;
468 sprites->obj[spriteCount].attr2 = 0x3a0;
469 spriteCount++;
470 sprites->obj[spriteCount].attr01 = 0x40042020;
471 sprites->obj[spriteCount].attr2 = 0x3a8;
472 spriteCount++;
473 sprites->obj[spriteCount].attr01 = 0x40042030;
474 sprites->obj[spriteCount].attr2 = 0x3a8;
475 spriteCount++;
476 for (index = 0; index != DICE_COUNT; index++) {
477 sprites->obj[spriteCount].attr01 = (32 + index * 16) | 0x40202000;
478 sprites->obj[spriteCount].attr2 = luckyNumbers[index] * 8 + 0x3b0;
479 spriteCount++;
480 }
481
482 // After a throw, reveal the four numbers five frames apart, then the
483 // result's lettering.
485 for (index = 0; index != DICE_RESULT_COUNT; index++) {
486 if (resultTimer == index * 5) {
488 }
489 if (resultTimer >= index * 5) {
490 sprites->obj[spriteCount].attr01 =
491 0x80002040 | ((56 + index * 32) << 16);
492 sprites->obj[spriteCount].attr2 = numbers[index] * 32 + 152;
493 spriteCount++;
494 }
495 }
496 if (resultTimer > 19 && resultKind >= 0) {
497 for (index = 0; index != gLuckyDice_LabelCounts[resultKind]; index++) {
498 s32 labelX;
499 s32 labelY;
500
501 labelX = DICE_KIND_X - gLuckyDice_LabelCounts[resultKind] * 16 +
502 index * 32;
503 labelY = 96;
504 sprites->obj[spriteCount].attr01 =
505 (((labelX & 0x1ff) << 16) | labelY) | 0x80002000;
506 sprites->obj[spriteCount].attr2 =
507 gLuckyDice_LabelTiles[resultKind] * 8 + 152 + index * 32;
508 spriteCount++;
509 }
510 }
511 }
512
513 // Showing the result: twenty frames in, pay the winnings and open their
514 // window; A then moves on to the payout, or back to aiming after a loss.
515 if (state == LUCKY_DICE_RESULT) {
516 if (resultTimer == 20 && winnings > 0) {
517 Party_AdjustCoinsFar(winnings);
518 if (winnings <= 9) {
519 digits = 1;
520 } else if (winnings <= 99) {
521 digits = 2;
522 } else if (winnings <= 999) {
523 digits = 3;
524 } else if (winnings <= 9999) {
525 digits = 4;
526 } else {
527 digits = 5;
528 }
529 window = UiWindow_CreateFar(DICE_WIN_X - digits / 2, 16,
530 digits + DICE_WIN_W, 3, 6);
531 sprites->winningsWindow = window;
532 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceWinnings, window, 0, 0);
533 // "Coins" after the number (MsgLuckyDiceCoins, one before).
534 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceWinnings - 1,
535 sprites->winningsWindow,
536 digits * 8 + DICE_WIN_LABEL_X, 0);
537 UiText_DrawNumberInWindowFar(winnings, digits, sprites->winningsWindow,
538 DICE_WIN_NUM_X, 0);
539 }
540 if (payoutTimer > 0) {
541 payoutTimer--;
542 }
543 if (resultTimer > 19 && (gKeysRepeat & KEY_A)) {
545 if (winnings > 0) {
547 payoutTimer = 0;
548 } else {
549 resetDice = 1;
551#if TBS_PATCH_1
552 window = UiWindow_CreateFar(DICE_HELP_X, 16, DICE_HELP_W, 4, 6);
553 sprites->helpWindow = window;
554 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceThrow, window, 0, 0);
555 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceThrow + 1,
556 sprites->helpWindow, 0, 8);
557#endif
558 }
559 }
560 resultTimer++;
561 } else if (state == LUCKY_DICE_PAYOUT) {
562 // Clear the winnings into the coins, then after 15 frames close their
563 // window and aim again.
564 if (payoutTimer == 0) {
565 winnings = 0;
566 UiText_DrawNumberInWindowFar(0, digits, sprites->winningsWindow, 40, 0);
568 0, 0);
569 }
570 if (payoutTimer == 15) {
573 window = UiWindow_CreateFar(DICE_HELP_X, 16, DICE_HELP_W, 4, 6);
574 sprites->helpWindow = window;
575 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceThrow, window, 0, 0);
576 UiText_DrawCharacterAtOffsetFar(MsgLuckyDiceThrow + 1,
577 sprites->helpWindow, 0, 8);
578 resetDice = 1;
579 }
580 payoutTimer++;
581 } else if (state == LUCKY_DICE_AIM && frame > 17) {
582 // Aiming: Up and Down raise and lower the dice between 32 and 160.
583 // B quits, as does running out of coins for a bet; A throws.
584 // DEAD: gKeysRepeat is read twice and discarded before the KEY_UP test;
585 // both loads are in the ROM.
586 (void)gKeysRepeat;
587 (void)gKeysRepeat;
588 if (gKeysHeld & KEY_UP) {
589 diceWork->particles[0].y += 0x40000;
590 diceWork->particles[1].y += 0x40000;
591 }
592 if (gKeysHeld & KEY_DOWN) {
593 diceWork->particles[0].y += -0x40000;
594 diceWork->particles[1].y += -0x40000;
595 }
596 if (diceWork->particles[0].y < 0x200000) {
597 diceWork->particles[0].y = 0x200000;
598 }
599 if (diceWork->particles[0].y > 0xa00000) {
600 diceWork->particles[0].y = 0xa00000;
601 }
602 if (diceWork->particles[1].y < 0x200000) {
603 diceWork->particles[1].y = 0x200000;
604 }
605 if (diceWork->particles[1].y > 0xa00000) {
606 diceWork->particles[1].y = 0xa00000;
607 }
608 if (gGameState.coins <
610 UiWork_FinalizeFar(sprites->helpWindow, 1);
611 break;
612 }
613 if (gKeysRepeat & KEY_B) {
615 UiWork_FinalizeFar(sprites->helpWindow, 1);
616 break;
617 }
618 if (gKeysRepeat & KEY_A) {
620 UiWork_FinalizeFar(sprites->helpWindow, 1);
623 if ((u32)bet > gGameState.coins) {
624 bet = gGameState.coins;
625 }
628 0, 0);
629 for (index = 0; index != DICE_COUNT; index++) {
630 rollingDie = &diceWork->particles[index];
631 // DEAD: never true with DICE_COUNT 2; a third die would start
632 // further back and higher up.
633 if (index > 1) {
634 rollingDie->z = -0xb40000;
635 rollingDie->y = 0x600000;
636 }
637 SET_VELOCITY(rollingDie, ((Random16() & 127) - 64) << 12,
638 ((Random16() & 15) + 48) << 13,
639 (((Random16() & 63) + 140) << 12) + rollingDie->y / 6);
640 rollingDie->variant = Random16() % 0x4800;
641 spinRate[index] = 300;
642 }
643 }
644 }
645
646 // Back to the throwing line, with two new lucky numbers.
647 if (resetDice == 1) {
648 diceWork->particles[0].x = 0x280000;
649 diceWork->particles[0].y = 0x600000;
650 diceWork->particles[0].z = -0xa00000;
651 diceWork->particles[1].x = -0x280000;
652 diceWork->particles[1].y = 0x600000;
653 diceWork->particles[1].z = -0xa00000;
654 luckyNumbers[0] = Random16() % DICE_FACE_COUNT;
655 do {
656 luckyNumbers[1] = Random16() % DICE_FACE_COUNT;
657 } while (luckyNumbers[0] == luckyNumbers[1]);
658 }
659
660 // Draw the dice back to front (an insertion sort on depth), each scaled
661 // by its distance.
662 for (index = 0; index != DICE_COUNT; index++) {
663 drawOrder[index] = index;
664 }
665 for (index = 1; index < DICE_COUNT; index++) {
666 sortedDie = drawOrder[index];
667 for (otherIndex = index - 1;
668 otherIndex >= 0 && diceWork->particles[drawOrder[otherIndex]].z >
669 diceWork->particles[sortedDie].z;
670 otherIndex--) {
671 drawOrder[otherIndex + 1] = drawOrder[otherIndex];
672 }
673 drawOrder[otherIndex + 1] = sortedDie;
674 }
675
676 for (index = 0; index != DICE_COUNT; index++) {
677 s32 dieX;
678 s32 dieY;
679
680 rollingDie = &diceWork->particles[drawOrder[index]];
681 // Projection reads the record's three coordinate words directly.
682 // Copying them to an array grows native EN from 0x1274 to 0x1294 bytes.
683 Render_ProjectPoint(rollingDie, screen);
684 // Depth is clamped to 350..650; the nearer, the larger the die.
685 if (screen[2] < 0x15e) {
686 screen[2] = 0x15e;
687 }
688 if (screen[2] > 0x28a) {
689 screen[2] = 0x28a;
690 }
691 spriteDepth[drawOrder[index]] = screen[2] - 0x15e;
692 halfSize = 0x800 / (spriteDepth[drawOrder[index]] / 2 + 128);
693 dieX = screen[0] - halfSize;
694 dieY = screen[1] - halfSize;
695 // An affine, double-size, 256-colour sprite using the die's own
696 // affine matrix (attr1 bits 9 to 13); 0x278 and 0x160 wrap in OAM's 9-
697 // and 8-bit fields to 120 across and 96 down.
698 if (dieY + 96 > -16) {
699 sprites->obj[spriteCount].attr01 =
700 ((drawOrder[index] << 25) | (((dieX + 0x278) & 0x1ff) << 16) |
701 ((dieY + 0x160) & 255)) |
702 0x40002300;
703 sprites->obj[spriteCount].attr2 =
704 (gLuckyDice_FaceTiles[rollingDie->variant / 1024] << 3) | 0x400;
705 spriteCount++;
706 }
707 }
708
709 // While aiming, a marker under each die.
710 if (state == LUCKY_DICE_AIM) {
711 for (index = 0; index != DICE_COUNT; index++) {
712 Render_ProjectPoint(&diceWork->particles[index], screen);
713 x = screen[0] - 0x800 / (spriteDepth[drawOrder[index]] / 2 + 128);
714 y = screen[1] + 0x300 / (spriteDepth[drawOrder[index]] / 2 + 128);
715 sprites->obj[spriteCount].attr01 =
716 ((((x + 0x278) & 0x1ff) << 16) | ((y + 0x160) & 255)) | 0x80002000;
717 if (index == 1) {
718 sprites->obj[spriteCount].attr01 |= 0x10000000;
719 }
720 sprites->obj[spriteCount].attr2 = 0x958;
721 spriteCount++;
722 }
723 }
724
725 for (index = 0; index != DICE_COUNT; index++) {
726 s32 shadowX;
727 s32 shadowY;
728
729 // Each die's shadow on the board below it.
730 SET_VECTOR(groundPoint, diceWork->particles[index].x, 0,
731 diceWork->particles[index].z);
732 Render_ProjectPoint(groundPoint, screen);
733 // A shadow shrinks as its die rises.
734 if (screen[2] < 0x15e) {
735 screen[2] = 0x15e;
736 }
737 if (screen[2] > 0x28a) {
738 screen[2] = 0x28a;
739 }
740 spriteDepth[2 + index] = screen[2] - 0x15e;
741 spriteDepth[2 + index] += diceWork->particles[index].y >> 16;
742 shadowX = screen[0];
743 shadowY = screen[1];
744 if (shadowY + 88 > -16) {
745 sprites->obj[spriteCount].attr01 =
746 (((index + 2) << 25) | (((shadowX + 0x270) & 0x1ff) << 16)) |
747 ((shadowY + 0x158) & 255) | 0x40002300;
748 sprites->obj[spriteCount].attr2 = 0x890;
749 spriteCount++;
750 }
751 }
752
753 // Rolling: move, collide and settle each die.
754 if (state == LUCKY_DICE_ROLL) {
755 s32 stoppedDice;
756
757 stoppedDice = 0;
758 for (index = 0; index != DICE_COUNT; index++) {
759 rollingDie = &diceWork->particles[index];
760 // Move, and push the dice apart when they touch (closer than 50).
761 rollingDie->x += rollingDie->velocityX;
762 rollingDie->y += rollingDie->velocityY;
763 rollingDie->z += rollingDie->velocityZ;
764 for (otherIndex = 0; otherIndex != DICE_COUNT; otherIndex++) {
765 otherDie = &diceWork->particles[otherIndex];
766 if (index != otherIndex) {
767 separationX = (rollingDie->x - otherDie->x) >> 16;
768 separationY = (rollingDie->y - otherDie->y) >> 16;
769 separationZ = (rollingDie->z - otherDie->z) >> 16;
770 if (separationX * separationX + separationY * separationY +
771 separationZ * separationZ <=
772 0x9c3) {
773 separation = Iwram_Sqrt(separationX * separationX +
774 separationY * separationY +
775 separationZ * separationZ);
776// UB: signed separation components may be negative; touching
777// dice are at most 49 pixels apart, so multiplication fits.
778// Coincident dice also need to avoid division by zero.
779#ifdef UBFIX
780 if (separation != 0) {
781 rollingDie->velocityX += separationX * 32768 / separation;
782 rollingDie->velocityY += separationY * 32768 / separation;
783 rollingDie->velocityZ += separationZ * 32768 / separation;
784 }
785#else
786 rollingDie->velocityX += (separationX << 15) / separation;
787 rollingDie->velocityY += (separationY << 15) / separation;
788 rollingDie->velocityZ += (separationZ << 15) / separation;
789#endif
790 }
791 }
792 }
793 // Bounce off the board's sides and back wall.
794 if (rollingDie->x < -0xc80000) {
795 rollingDie->x = -0xc80000;
796 rollingDie->velocityX = -rollingDie->velocityX * 48 / 64;
797 }
798 if (rollingDie->x > 0xc80000) {
799 rollingDie->x = 0xc80000;
800 rollingDie->velocityX = -rollingDie->velocityX * 48 / 64;
801 }
802 if (rollingDie->z < -0x960000) {
803 rollingDie->z = -0x960000;
804 rollingDie->velocityZ = -rollingDie->velocityZ * 48 / 64;
805 }
806 if (rollingDie->z > rollingDie->y + 0xb40000) {
807 rollingDie->z = rollingDie->y + 0xb40000;
808 if (rollingDie->velocityZ > 0) {
809 rollingDie->velocityY += rollingDie->velocityZ / 2;
810 rollingDie->velocityZ = -rollingDie->velocityZ * 48 / 64;
811 }
812 }
813 // Gravity; on the board a die bounces, loses speed, sometimes kicks
814 // off in a random direction, and spins less. Once it barely bounces
815 // it stops, turned to the nearest face.
816 rollingDie->velocityY += -0x8000;
817 if (rollingDie->y <= 0) {
818 rollingDie->y = 0;
819 rollingDie->velocityY = -rollingDie->velocityY * 48 / 64;
820 if ((Random16() & 3) == 0) {
821 s32 bounceVelocityX;
822 s32 bounceVelocityZ;
823
824 bounceVelocityX = rollingDie->velocityX >> 8;
825 bounceVelocityZ = rollingDie->velocityZ >> 8;
826 bounceSpeed = Iwram_Sqrt(bounceVelocityX * bounceVelocityX +
827 bounceVelocityZ * bounceVelocityZ) *
828 56 / 64;
829 bounceAngle = Random16() & 0xffff;
830 rollingDie->velocityX = Trig_Sin(bounceAngle) * bounceSpeed >> 8;
831 rollingDie->velocityZ = Trig_Cos(bounceAngle) * bounceSpeed >> 8;
832 } else {
833 rollingDie->velocityX = rollingDie->velocityX * 56 / 64;
834 rollingDie->velocityZ = rollingDie->velocityZ * 56 / 64;
835 }
836 if (spinRate[index] > 19) {
837 spinRate[index] -= 20;
838 }
839 if (rollingDie->velocityY < 0x30000) {
840 SET_VELOCITY(rollingDie, 0, 0, 0);
841 spinRate[index] = 0;
842 rollAngle = rollingDie->variant;
843 faceTile = rollAngle / 1024 % 3;
844 if (faceTile == 1) {
845 rollingDie->variant = rollAngle + 0x800;
846 } else if (faceTile == 2) {
847 rollingDie->variant = rollAngle + 0x400;
848 }
849 }
850 }
851 SET_VELOCITY(rollingDie, rollingDie->velocityX * 63 / 64,
852 rollingDie->velocityY * 63 / 64,
853 rollingDie->velocityZ * 63 / 64);
854 // The roll angle wraps at six faces of 0xc00.
855 rollingDie->variant += spinRate[index];
856 if (rollingDie->variant > 0x47ff) {
857 rollingDie->variant -= 0x4800;
858 }
859 if (spinRate[index] > 0) {
860 spinRate[index]--;
861 }
862 // At rest: read the face from the roll and the row from the depth.
863 if (rollingDie->velocityX == 0 && rollingDie->velocityY == 0 &&
864 rollingDie->velocityZ == 0 && rollingDie->y == 0) {
865 numbers[index] = rollingDie->variant / 0xc00;
866 for (otherIndex = 0; otherIndex != DICE_FACE_COUNT; otherIndex++) {
867 // Row boundaries include negative signed halfwords.
868 s32 rowDepth;
869
870#ifdef UBFIX
871 rowDepth = gLuckyDice_FaceHeights[otherIndex] * 65536;
872#else
873 rowDepth = gLuckyDice_FaceHeights[otherIndex] << 16;
874#endif
875 if (rollingDie->z > rowDepth) {
876 numbers[index + 2] = otherIndex;
877 break;
878 }
879 }
880 stoppedDice++;
881 }
882 }
883
884 // Both dice stopped: score the four numbers.
885 if (stoppedDice == DICE_COUNT) {
887 resultTimer = 0;
888 resultKind = -1;
889 winnings = 0;
890 payoutTimer = 60;
891 if (numbers[0] == numbers[1] && numbers[0] == numbers[2] &&
892 numbers[0] == numbers[3]) {
893 flashTimer[0] = flashTimer[1] = 120;
894 resultKind = 0;
895 if (numbers[0] == luckyNumbers[0]) {
896 winnings = bet * 15;
897 } else if (numbers[0] == luckyNumbers[1]) {
898 winnings = bet * 25;
899 } else {
900 winnings = bet * 5;
901 }
903 sprites->coinsWindow, 0, 0);
905 } else {
906 pairCount = 0;
907 for (index = 0; index != 3; index++) {
908 for (otherIndex = index + 1; otherIndex != DICE_RESULT_COUNT;
909 otherIndex++) {
910 if (numbers[index] == numbers[otherIndex]) {
911 matchedNumbers[pairCount] = numbers[index];
912 pairCount++;
913 }
914 }
915 }
916 if (pairCount == 1) {
917 winnings = bet;
919 sprites->coinsWindow, 0, 0);
920 resultKind = 3;
921 }
922 if (pairCount == 2) {
923 winnings = bet * 2;
925 sprites->coinsWindow, 0, 0);
926 flashTimer[Random16() & 1] = 60;
927 resultKind = 2;
929 }
930 if (pairCount == 3) {
931 winnings = bet * 3;
933 sprites->coinsWindow, 0, 0);
934 flashTimer[Random16() & 1] = 60;
935 resultKind = 1;
937 }
938 }
939 }
940 }
941
942 // Hide the unused sprites, write the four affine matrices (one per die
943 // and shadow, scaled by depth) and copy the sprites to OAM.
944 // An unused sprite is parked at x 240, just past the right edge.
945 for (; spriteCount != 128; spriteCount++) {
946 sprites->obj[spriteCount].attr01 = 0x40f02000;
947 sprites->obj[spriteCount].attr2 = 0;
948 }
949
950 // OAM keeps affine matrix n in the fourth halfwords of entries 4n to
951 // 4n + 3, which attr2's high half covers here.
952 for (index = 0; index != DICE_RESULT_COUNT; index++) {
953 affineScale = spriteDepth[index] / 2 + 128;
954 affineA = affineScale;
955 affineB = 0;
956 affineC = 0;
957 affineD = affineScale;
958 sprites->obj[index * 4 + 0].attr2 |= affineA << 16;
959 sprites->obj[index * 4 + 1].attr2 |= affineB << 16;
960 sprites->obj[index * 4 + 2].attr2 |= affineC << 16;
961 sprites->obj[index * 4 + 3].attr2 |= affineD << 16;
962 }
963
964 Dma_Set(sprites->obj, OAM, (DMA_ENABLE | DMA_32BIT) << 16 | 0x100,
965 REG_DMA3);
966 WaitFrames(1);
967 frame++;
968 }
969
970 // Fade out over 17 frames and free the work blocks.
971 for (index = 0; index != 17; index++) {
972 scale = 0x10000 - (index << 12);
973 Graphics_ScaleRgb555Buffer(spritePalette, (u16 *)OBJ_PLTT, scale, 256);
974 Graphics_ScaleRgb555Buffer(bgPalette, (u16 *)BG_PLTT, scale, 256);
975 WaitFrames(1);
976 }
982}
The battle camera and the presentation's transition, background and affine work.
struct BattleCamera * gCameraWork
BattleEffectWork, the running battle effect's work, and its animation objects.
Calls through a function pointer, by argument count.
static __inline__ s32 Value2(s32(*f)(), s32 a0, s32 a1)
Definition call.h:67
The task scheduler: tasks, their order and the scheduler's state.
The engine calls the debug menus import, and their palette swatch tiles.
DMA channel registers, and the DMA and CPU fills.
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
The edition's language, for C to compare (LANGUAGE == LANGUAGE_GERMAN).
u32 Party_GetAverageLevelFar(void)
s32 Party_AdjustCoinsFar(s32 amount)
void AudioCommand_PlayFar(s32 id)
struct UiWindow * UiWindow_CreateFar(s32 x, s32 y, s32 width, s32 height, s32 attrs)
void UiText_DrawNumberInWindowFar(s32 arg0, s32 arg1, struct UiWindow *arg2, s32 arg3, s32 arg4)
void UiText_DrawCharacterAtOffsetFar(s32 character, const struct UiWindow *position, u32 offsetX, u32 offsetY)
void UiWork_FinalizeFar(struct UiWindow *work, s32 release)
void UiWork_InitializeWithResourceCountersFar(void)
16.16 fixed-point constants, sine and cosine, and the library division.
s32 Trig_Cos(s32 angle)
s32 Trig_Sin(s32 angle)
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
The frame loop's globals: lag counts, the CPU load display and the VBlank flags.
u8 gOamCopyEnabled
The VBlank handler copies the OAM list only while it is set; scenes clear it to keep their own sprite...
GameState, the game's state: play time, coins, the scene and the leader's position,...
struct GameState gGameState
The screen blend's globals and the graphics routines.
void * Runtime_AllocateBlock(s32 kind, s32 size)
As Runtime_AllocateHeapBlock, but tries EWRAM first, for blocks that need room more than speed: the b...
Definition heap.c:80
void * Runtime_AllocateHeapBlock(s32 kind, s32 size)
Returns heap slot kind's block, first allocating size bytes for it (rounded up to words),...
Definition heap.c:47
The heap's work slots and HeapState.
@ HEAP_SLOT_BATTLE_EFFECT
Definition heap_state.h:42
@ HEAP_SLOT_BATTLE_SHEET
Definition heap_state.h:44
@ HEAP_SLOT_CAMERA
Definition heap_state.h:38
@ HEAP_SLOT_BATTLE_CANVAS
Definition heap_state.h:43
The hardware's I/O registers and their bits, as pret names them.
#define REG_WIN1H
Definition io_reg.h:75
#define WINOUT_WINOBJ_BG2
Definition io_reg.h:112
#define BGCNT_CHAR_BASE(block)
Definition io_reg.h:25
#define REG_WININ
Definition io_reg.h:81
#define OBJ_PLTT
Definition io_reg.h:159
#define BGCNT_256_COLORS
Definition io_reg.h:27
#define BGCNT_PRIORITY(n)
Definition io_reg.h:24
#define WININ_WIN0_BG1
Definition io_reg.h:87
#define BG_SCREEN_BLOCK(n)
Definition io_reg.h:167
#define WININ_WIN0_CLR
Definition io_reg.h:91
#define WINOUT_WIN01_BG1
Definition io_reg.h:104
#define REG_BG1VOFS
Definition io_reg.h:50
#define BLDCNT_TGT1_BG2
Definition io_reg.h:125
#define OAM
Definition io_reg.h:239
#define REG_WIN0H
Definition io_reg.h:74
#define WININ_WIN1_BG0
Definition io_reg.h:93
#define OBJ_VRAM0
Sprite tile memory, as pret names it: the 32 KB from 0x06010000.
Definition io_reg.h:170
#define WININ_WIN1_CLR
Definition io_reg.h:98
#define REG_BG2HOFS
Definition io_reg.h:51
#define WININ_WIN0_BG0
Definition io_reg.h:86
@ DISPCNT_OBJ_ON
Definition io_reg.h:44
@ DISPCNT_WIN0_ON
Definition io_reg.h:45
@ DISPCNT_BG0_ON
Definition io_reg.h:40
@ DISPCNT_OBJ_1D_MAP
Definition io_reg.h:39
@ DISPCNT_BG1_ON
Definition io_reg.h:41
@ KEY_UP
Definition io_reg.h:249
@ KEY_B
Definition io_reg.h:244
@ KEY_A
Definition io_reg.h:243
@ KEY_DOWN
Definition io_reg.h:250
#define DMA_32BIT
Definition io_reg.h:210
#define REG_DISPCNT
Definition io_reg.h:9
#define BLDCNT_EFFECT_BLEND
weighted by REG_BLDALPHA
Definition io_reg.h:131
#define BLDCNT_TGT2_ALL
Definition io_reg.h:140
#define REG_DMA3
Definition io_reg.h:153
#define REG_BG1HOFS
Definition io_reg.h:49
#define WINOUT_WINOBJ_BG1
Definition io_reg.h:111
#define BLEND_ALPHA(top, bottom)
A REG_BLDALPHA value: top and bottom are the two targets' weights, out of 16.
Definition io_reg.h:145
#define WININ_WIN0_OBJ
Definition io_reg.h:90
#define BGCNT_SCREEN_BASE(block)
Definition io_reg.h:28
#define WINOUT_WINOBJ_CLR
Definition io_reg.h:115
#define REG_BLDALPHA
Definition io_reg.h:142
#define REG_WINOUT
Definition io_reg.h:82
#define WIN_RANGE(start, end)
A REG_WINxH or REG_WINxV value: the window covers start up to, but not including, end.
Definition io_reg.h:80
#define WININ_WIN1_BG1
Definition io_reg.h:94
#define BG_PLTT
Definition io_reg.h:157
#define WINOUT_WIN01_BG0
Definition io_reg.h:103
#define REG_BG2VOFS
Definition io_reg.h:52
#define WININ_WIN1_OBJ
Definition io_reg.h:97
#define WINOUT_WIN01_CLR
Definition io_reg.h:108
#define WINOUT_WINOBJ_BG0
Definition io_reg.h:110
#define REG_BLDCNT
Definition io_reg.h:119
#define REG_BG1CNT
Definition io_reg.h:20
#define DMA_ENABLE
Definition io_reg.h:217
#define REG_WIN1V
Definition io_reg.h:77
#define WININ_WIN1_BG2
Definition io_reg.h:95
#define WININ_WIN0_BG2
Definition io_reg.h:88
#define REG_WIN0V
Definition io_reg.h:76
The IWRAM call layer: the routines the game keeps resident in IWRAM, each at its fixed address,...
static s32 Iwram_RatioMulQ14(s32 divisor, s32 value)
All calls go through this layer.
Definition iwram_call.h:51
static s32 Iwram_Sqrt(s32 value)
Definition iwram_call.h:63
static s32 Iwram_TransformVector(const void *vector, s32 *result)
Definition iwram_call.h:65
The keypad state the VBlank interrupt keeps.
volatile u32 gKeysHeld
Keys down now.
volatile u32 gKeysRepeat
Pressed keys, repeating while held: after 19 frames, then every 6.
#define DICE_WIN_LABEL_X
Definition lucky_dice.c:169
#define DICE_BET_PER_LEVEL
Definition lucky_dice.c:195
UNUSED s32 FixedPoint_Multiply8_8(s32 leftValue, s32 rightValue)
Multiplies two 8.8 fixed-point values, rounding toward zero.
Definition lucky_dice.c:77
#define DICE_COUNT
Definition lucky_dice.c:192
#define DICE_COIN_W
Definition lucky_dice.c:136
UNUSED s16 Math_ScaleByRatio(s16 numerator, s16 denominator)
Divides two 8.8 fixed-point values.
Definition lucky_dice.c:99
#define DICE_KIND_X
Definition lucky_dice.c:170
LuckyDiceState
What the Lucky Dice screen is doing.
Definition lucky_dice.c:198
@ LUCKY_DICE_RESULT
the four numbers and the result are shown
Definition lucky_dice.c:201
@ LUCKY_DICE_ROLL
the dice fly, bounce and settle
Definition lucky_dice.c:200
@ LUCKY_DICE_AIM
the dice wait to be thrown; Up and Down raise them
Definition lucky_dice.c:199
@ LUCKY_DICE_PAYOUT
the winnings move into the coins
Definition lucky_dice.c:202
void LuckyDice_Run(void)
Runs the Lucky Dice minigame until the player quits or cannot afford a throw.
Definition lucky_dice.c:252
#define DICE_RESULT_COUNT
Definition lucky_dice.c:194
#define DICE_WIN_NUM_X
Definition lucky_dice.c:168
s32 Runtime_BlankDisplayAndRun(void)
Starts the Lucky Dice minigame: leaves only the backdrop on screen, starts song MUS_009 and plays unt...
Definition lucky_dice.c:31
#define DICE_WIN_X
Definition lucky_dice.c:167
#define DICE_FACE_COUNT
Definition lucky_dice.c:193
#define DICE_COIN_X
Definition lucky_dice.c:135
#define DICE_LABEL_X
Definition lucky_dice.c:149
#define DICE_WIN_W
Definition lucky_dice.c:137
#define DICE_HELP_X
Definition lucky_dice.c:159
#define DICE_HELP_W
Definition lucky_dice.c:160
UNUSED s16 FixedPoint_Reciprocal(s16 value)
The reciprocal of an 8.8 fixed-point value.
Definition lucky_dice.c:109
void Camera_ResetSceneDefaults(void)
Puts the 3D camera back at its scene defaults: at the origin, unturned, pitched to 152 and 510 units ...
Definition lucky_dice.c:43
s32 Graphics_ScaleRgb555Buffer(u16 *source, u16 *destination, s32 scale, s32 count)
Copies count RGB555 colours from source to destination with each channel multiplied by scale (16....
Definition lucky_dice.c:208
Small statement macros both games use for positions, velocities and bounds.
#define SET_VELOCITY(p, vx, vy, vz)
Definition macros.h:18
#define SET_VECTOR(v, x, y, z)
Sets the x, y and z of a three-part vector: a position about to be projected, a per-frame step,...
Definition macros.h:12
The map layers' scroll work and the background scroll values.
struct BgScroll gBgScroll[]
u32 Random16(void)
Advances the game's random number generator (the C library's classic linear congruential one),...
Definition math.c:10
The heaps: allocating and freeing blocks.
void Runtime_ReleaseHeapBlock(s32 slot)
The reel game's title lettering.
OamShadow, a sprite entry a routine builds by hand and DMA copies to OAM.
The 3D projection.
struct Projection gProjection
The work pointers in the gWorkSlot table, by name.
#define Ram_MapCellBuffer
Definition ram_buffer.h:10
Resource numbers, named where their contents are known.
char ResourceId_LuckyDiceBoard
char ResourceId_LuckyDiceSprites
char ResourceId_EffectFarCalls
s32 Resource_DecodeType01(const void *source, void *destination)
Decodes a type-01 resource and returns its unpacked byte count.
Definition decompress.c:31
FIELD_AT_OFFSET and the entity and camera views older scene code reads.
void Scheduler_ResetTaskTable(void)
Empties the task table: every slot gets no routine, order 0xffff (sorted last) and no flags.
Definition scheduler.c:19
Song and sound effect ids, indices into gSound_SongTable.
#define MUS_092
Definition songs.h:90
#define SE_MENU_CANCEL
Definition songs.h:106
#define MUS_093
Definition songs.h:91
#define SE_MENU_CONFIRM
Definition songs.h:105
#define SE_303
Definition songs.h:291
#define MUS_091
Definition songs.h:89
#define SE_302
Definition songs.h:290
#define MUS_009
Definition songs.h:19
The battle camera: its eye, the focus it turns about, and its pitch, yaw and distance.
u16 pitch
1/65536 turns
s32 distance
eye to focus, 16.16 fixed-point pixels
u16 yaw
1/65536 turns
s32 * followPos
when set, the focus followed instead of pos
s32 pos[3]
the focus, 16.16 fixed-point pixels
s32 eye[3]
The eye, 16.16 fixed-point pixels: written each frame distance behind the focus along the turned view...
s32 unk18
cleared beside followPos; not read by name in TBS
The running battle effect's work: its canvas image, 64 particles, the display registers it stages and...
struct EffectStep particles[64]
free while variant is -1
u16 y
pixels
Definition map_scroll.h:65
A moving point of an effect: its position, its velocity and a value each effect uses its own way (a s...
Definition effect_step.h:13
s32 velocityX
16.16 fixed-point pixels per frame
Definition effect_step.h:28
s32 z
16.16 fixed-point pixels
Definition effect_step.h:26
s32 variant
each effect's own: a size, a countdown, -1 for idle
Definition effect_step.h:31
s32 x
16.16 fixed-point pixels
Definition effect_step.h:24
s32 y
16.16 fixed-point pixels
Definition effect_step.h:25
s32 velocityY
16.16 fixed-point pixels per frame
Definition effect_step.h:29
s32 velocityZ
16.16 fixed-point pixels per frame
Definition effect_step.h:30
The used prefix of Lucky Dice's 0x618-byte work block (heap slot 45): its sprites and windows.
Definition lucky_dice.c:180
struct UiWindow * coinsWindow
the player's coins
Definition lucky_dice.c:183
u8 filler[200]
Lucky Dice never touches these bytes.
Definition lucky_dice.c:181
struct UiWindow * winningsWindow
the winnings of the last throw
Definition lucky_dice.c:184
struct OamShadow obj[128]
copied to OAM each frame
Definition lucky_dice.c:182
struct UiWindow * helpWindow
the Throw and Quit buttons
Definition lucky_dice.c:185
OAM buffer: sprite entries a routine builds by hand and DMA copies to OAM.
Definition oambuf.h:18
volatile u32 attr2
attr2 | affine value << 16
Definition oambuf.h:20
volatile u32 attr01
attr0 | attr1 << 16
Definition oambuf.h:19
s32 centerX
screen pixels
Definition project.h:16
s32 centerY
screen pixels
Definition project.h:17
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
u16 frame
The zoom's step: counts down from 7 to 0 while opening, and up from 0 to duration while closing.
Definition uiwindow.h:28
u16 y
tiles
Definition uiwindow.h:16
u16 state
0 while a message channel is typing into the window; 1 once its text has ended or closed the window,...
Definition uiwindow.h:24
u16 x
tiles
Definition uiwindow.h:15
void * Resource_GetTableEntry(s32 index)
Returns the ROM address of resource index from the resource directory.
Definition system.c:150
void RuntimeDispatch_NoOpHook(s32 unused)
An exported stub that does nothing with its argument.
Definition system.c:146
The system's routines and globals: interrupts, BIOS sound bias, random numbers and sleep.
The Lucky Dice minigame's tables, after LuckyDice_Run in both games.
const u8 gLuckyDice_LabelTiles[]
The first tile of each kind of result's lettering.
Definition tables.h:12
const s16 gLuckyDice_FaceHeights[]
The heights that decide the face (z > value << 16).
Definition tables.h:25
const u8 gLuckyDice_LabelCounts[]
How many sprites each kind of result's lettering takes.
Definition tables.h:9
const u8 gLuckyDice_FaceTiles[]
The die face tile at each step of its roll.
Definition tables.h:21
void SceneTransform_ApplyYaw(s32 angle)
Rotates gTransform about the Y axis by angle.
Definition transform.c:113
void Projection_Set(u32 focal, u32 nearDepth, u32 farDepth)
Sets gProjection's focal length and depth range directly.
Definition transform.c:350
s32 Render_ProjectPoint(const void *point, s32 *screen)
Reads three word-aligned coordinates from point, transforms them by gTransform and projects them to t...
Definition transform.c:364
void SceneTransform_ApplyPitch(s32 angle)
Rotates gTransform about the X axis by angle.
Definition transform.c:97
void SceneTransform_ApplyPosition(const s32 *position)
Translates gTransform by position (x, y, z in 16.16).
Definition transform.c:145
void Render_ResetTransformState(void)
Allocates the one-level save area (heap slot HEAP_SLOT_TRANSFORM_STACK), clears gTransformStackDepth ...
Definition transform.c:19
void Transform_SetLookAt(s32 *eye, s32 *target)
Makes the camera at eye looking at target the whole of gTransform.
Definition transform.c:318
The 3D transform and its stack.
The integer types, TRUE, UNUSED and the fixed-address macros.
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
#define UNUSED
Definition types.h:22
unsigned char u8
Definition types.h:11
UiWork, the window system's work, and the status screen's cursors.
The patch levels: our groups of the measured changes from the Japanese release.