Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/hud.c
7858 views
1
#include <PR/ultratypes.h>
2
3
#include "sm64.h"
4
#include "actors/common1.h"
5
#include "gfx_dimensions.h"
6
#include "game_init.h"
7
#include "level_update.h"
8
#include "camera.h"
9
#include "print.h"
10
#include "ingame_menu.h"
11
#include "hud.h"
12
#include "segment2.h"
13
#include "area.h"
14
#include "save_file.h"
15
#include "print.h"
16
17
#include "text_strings.h"
18
19
#include "settings.h"
20
21
/* @file hud.c
22
* This file implements HUD rendering and power meter animations.
23
* That includes stars, lives, coins, camera status, power meter, timer
24
* cannon reticle, and the unused keys.
25
**/
26
27
struct PowerMeterHUD {
28
s8 animation;
29
s16 x;
30
s16 y;
31
f32 unused;
32
};
33
34
struct UnusedHUDStruct {
35
u32 unused1;
36
u16 unused2;
37
u16 unused3;
38
};
39
40
struct CameraHUD {
41
s16 status;
42
};
43
44
// Stores health segmented value defined by numHealthWedges
45
// When the HUD is rendered this value is 8, full health.
46
static s16 sPowerMeterStoredHealth;
47
48
static struct PowerMeterHUD sPowerMeterHUD = {
49
POWER_METER_HIDDEN,
50
140,
51
166,
52
1.0,
53
};
54
55
// Power Meter timer that keeps counting when it's visible.
56
// Gets reset when the health is filled and stops counting
57
// when the power meter is hidden.
58
s32 sPowerMeterVisibleTimer = 0;
59
60
float sStarGetAlpha = 0.0f;
61
float sStarGetBounce = 0.0f;
62
float sStarGetSpeed = 0.0f;
63
64
// Custom left and right snapping functions
65
s32 get_left(s32 value) {
66
if (config4by3Hud || configAspectRatio == 1) {
67
return value;
68
}
69
else {
70
return GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(value);
71
}
72
}
73
s32 get_right(s32 value) {
74
if (config4by3Hud || configAspectRatio == 1) {
75
return SCREEN_WIDTH-value;
76
}
77
else {
78
return GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(value);
79
}
80
}
81
82
f32 get_power_meter_x() {
83
if (configHudLayout == 2) {
84
return get_right(46);
85
}
86
else if (configHudLayout == 1) {
87
return SCREEN_WIDTH/2;
88
} else {
89
return sPowerMeterHUD.x;
90
}
91
}
92
93
UNUSED static struct UnusedHUDStruct sUnusedHUDValues = { 0x00, 0x0A, 0x00 };
94
95
static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE };
96
97
static u32 sPowerMeterLastRenderTimestamp;
98
static s16 sPowerMeterLastY;
99
static Gfx *sPowerMeterDisplayListPos;
100
101
static Gfx *sStarGetDisplayListPos;
102
103
void patch_interpolated_hud(void) {
104
if (sPowerMeterDisplayListPos != NULL) {
105
Mtx *mtx = alloc_display_list(sizeof(Mtx));
106
guTranslate(mtx, get_power_meter_x(), (f32) sPowerMeterHUD.y, 0);
107
gSPMatrix(sPowerMeterDisplayListPos, VIRTUAL_TO_PHYSICAL(mtx),
108
G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
109
sPowerMeterDisplayListPos = NULL;
110
}
111
}
112
113
/**
114
* Renders a rgba16 16x16 glyph texture from a table list.
115
*/
116
void render_hud_tex_lut(s32 x, s32 y, u8 *texture) {
117
gDPPipeSync(gDisplayListHead++);
118
gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture);
119
gSPDisplayList(gDisplayListHead++, &dl_hud_img_load_tex_block);
120
gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 15) << 2, (y + 15) << 2,
121
G_TX_RENDERTILE, 0, 0, 4 << 10, 1 << 10);
122
}
123
124
/**
125
* Renders a rgba16 8x8 glyph texture from a table list.
126
*/
127
void render_hud_small_tex_lut(s32 x, s32 y, u8 *texture) {
128
gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0,
129
G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD);
130
gDPTileSync(gDisplayListHead++);
131
gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0,
132
G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD);
133
gDPSetTileSize(gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC);
134
gDPPipeSync(gDisplayListHead++);
135
gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture);
136
gDPLoadSync(gDisplayListHead++);
137
gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES));
138
gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 7) << 2, (y + 7) << 2, G_TX_RENDERTILE,
139
0, 0, 4 << 10, 1 << 10);
140
}
141
142
/**
143
* Renders power meter health segment texture using a table list.
144
*/
145
void render_power_meter_health_segment(s16 numHealthWedges) {
146
u8 *(*healthLUT)[];
147
148
healthLUT = segmented_to_virtual(&power_meter_health_segments_lut);
149
150
gDPPipeSync(gDisplayListHead++);
151
gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1,
152
(*healthLUT)[numHealthWedges - 1]);
153
gDPLoadSync(gDisplayListHead++);
154
gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 32 * 32 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES));
155
gSP1Triangle(gDisplayListHead++, 0, 1, 2, 0);
156
gSP1Triangle(gDisplayListHead++, 0, 2, 3, 0);
157
}
158
159
/**
160
* Renders power meter display lists.
161
* That includes the "POWER" base and the colored health segment textures.
162
*/
163
void render_dl_power_meter(s16 numHealthWedges) {
164
Mtx *mtx;
165
f32 interpolatedY;
166
167
mtx = alloc_display_list(sizeof(Mtx));
168
169
if (mtx == NULL) {
170
return;
171
}
172
173
if (gGlobalTimer == sPowerMeterLastRenderTimestamp + 1) {
174
interpolatedY = (sPowerMeterLastY + sPowerMeterHUD.y) / 2.0f;
175
} else {
176
interpolatedY = sPowerMeterHUD.y;
177
}
178
179
guTranslate(mtx, get_power_meter_x(), interpolatedY, 0);
180
181
sPowerMeterLastY = sPowerMeterHUD.y;
182
sPowerMeterLastRenderTimestamp = gGlobalTimer;
183
sPowerMeterDisplayListPos = gDisplayListHead;
184
185
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++),
186
G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
187
gSPDisplayList(gDisplayListHead++, &dl_power_meter_base);
188
189
if (numHealthWedges != 0) {
190
gSPDisplayList(gDisplayListHead++, &dl_power_meter_health_segments_begin);
191
render_power_meter_health_segment(numHealthWedges);
192
gSPDisplayList(gDisplayListHead++, &dl_power_meter_health_segments_end);
193
}
194
195
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
196
}
197
198
/**
199
* Power meter animation called when there's less than 8 health segments
200
* Checks its timer to later change into deemphasizing mode.
201
*/
202
void animate_power_meter_emphasized(void) {
203
s16 hudDisplayFlags;
204
hudDisplayFlags = gHudDisplay.flags;
205
206
if (!(hudDisplayFlags & HUD_DISPLAY_FLAG_EMPHASIZE_POWER)) {
207
if (sPowerMeterVisibleTimer == 45.0) {
208
sPowerMeterHUD.animation = POWER_METER_DEEMPHASIZING;
209
}
210
} else {
211
sPowerMeterVisibleTimer = 0;
212
}
213
}
214
215
/**
216
* Power meter animation called after emphasized mode.
217
* Moves power meter y pos speed until it's at 200 to be visible.
218
*/
219
static void animate_power_meter_deemphasizing(void) {
220
s16 speed = 5;
221
222
if (sPowerMeterHUD.y >= 181) {
223
speed = 3;
224
}
225
226
if (sPowerMeterHUD.y >= 191) {
227
speed = 2;
228
}
229
230
if (sPowerMeterHUD.y >= 196) {
231
speed = 1;
232
}
233
234
sPowerMeterHUD.y += speed;
235
236
if (sPowerMeterHUD.y >= 201) {
237
sPowerMeterHUD.y = 200;
238
sPowerMeterHUD.animation = POWER_METER_VISIBLE;
239
}
240
}
241
242
/**
243
* Power meter animation called when there's 8 health segments.
244
* Moves power meter y pos quickly until it's at 301 to be hidden.
245
*/
246
static void animate_power_meter_hiding(void) {
247
sPowerMeterHUD.y += 20;
248
if (sPowerMeterHUD.y >= 301) {
249
sPowerMeterHUD.animation = POWER_METER_HIDDEN;
250
sPowerMeterVisibleTimer = 0;
251
}
252
}
253
254
/**
255
* Handles power meter actions depending of the health segments values.
256
*/
257
void handle_power_meter_actions(s16 numHealthWedges) {
258
// Show power meter if health is not full, less than 8
259
if (numHealthWedges < 8 && sPowerMeterStoredHealth == 8 && sPowerMeterHUD.animation == POWER_METER_HIDDEN) {
260
sPowerMeterHUD.animation = POWER_METER_EMPHASIZED;
261
sPowerMeterHUD.y = 166;
262
}
263
264
// Show power meter if health is full, has 8
265
if (numHealthWedges == 8 && sPowerMeterStoredHealth == 7) {
266
sPowerMeterVisibleTimer = 0;
267
}
268
269
// After health is full, hide power meter
270
if (numHealthWedges == 8 && sPowerMeterVisibleTimer > 45.0) {
271
sPowerMeterHUD.animation = POWER_METER_HIDING;
272
}
273
274
// Update to match health value
275
sPowerMeterStoredHealth = numHealthWedges;
276
277
// If Mario is swimming, keep power meter visible
278
if (gPlayerCameraState->action & ACT_FLAG_SWIMMING) {
279
if (sPowerMeterHUD.animation == POWER_METER_HIDDEN
280
|| sPowerMeterHUD.animation == POWER_METER_EMPHASIZED) {
281
sPowerMeterHUD.animation = POWER_METER_DEEMPHASIZING;
282
sPowerMeterHUD.y = 166;
283
}
284
sPowerMeterVisibleTimer = 0;
285
}
286
}
287
288
/**
289
* Renders the power meter that shows when Mario is in underwater
290
* or has taken damage and has less than 8 health segments.
291
* And calls a power meter animation function depending of the value defined.
292
*/
293
void render_hud_power_meter(void) {
294
s16 shownHealthWedges = gHudDisplay.wedges;
295
296
if ((configHudLayout < 2) && (!configAlwaysShowHealth)) {
297
if (sPowerMeterHUD.animation != POWER_METER_HIDING) {
298
handle_power_meter_actions(shownHealthWedges);
299
}
300
301
if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) {
302
return;
303
}
304
305
switch (sPowerMeterHUD.animation) {
306
case POWER_METER_EMPHASIZED:
307
animate_power_meter_emphasized();
308
break;
309
case POWER_METER_DEEMPHASIZING:
310
animate_power_meter_deemphasizing();
311
break;
312
case POWER_METER_HIDING:
313
animate_power_meter_hiding();
314
break;
315
default:
316
break;
317
}
318
}
319
else {
320
sPowerMeterHUD.y = 200;
321
}
322
323
render_dl_power_meter(shownHealthWedges);
324
325
if ((configHudLayout < 2) && (!configAlwaysShowHealth)) {
326
sPowerMeterVisibleTimer += 1;
327
}
328
}
329
330
#ifdef VERSION_JP
331
#define HUD_TOP_Y 210
332
#else
333
#define HUD_TOP_Y 209
334
#endif
335
336
#define HUD_TOP_Y_NEW 212
337
#define HUD_LEFT_X 22
338
339
/**
340
* Renders the amount of lives Mario has.
341
*/
342
void render_hud_mario_lives(void) {
343
s32 x;
344
s32 y;
345
if (configHudLayout == 2) {
346
x = get_left(HUD_LEFT_X)+64;
347
y = HUD_TOP_Y_NEW;
348
}
349
else if (configHudLayout == 1) {
350
x = get_left(HUD_LEFT_X);
351
y = HUD_TOP_Y_NEW;
352
}
353
else {
354
x = get_left(22);
355
y = HUD_TOP_Y;
356
}
357
print_text(x, y, ","); // 'Mario Head' glyph
358
if (configHudLayout > 0) {
359
print_text(x+17, y, "*"); // 'X' glyph
360
print_text_fmt_int(x+33, y, configAddZeroes ? "%02d" : "%d", gHudDisplay.lives);
361
}
362
else {
363
print_text(x+16, y, "*"); // 'X' glyph
364
print_text_fmt_int(x+32, y, "%d", gHudDisplay.lives);
365
}
366
}
367
368
#define HUD_COIN_X 168
369
370
/**
371
* Renders the amount of coins collected.
372
*/
373
void render_hud_coins(void) {
374
if (configHudLayout > 0) {
375
print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW-18, "+"); // 'Coin' glyph
376
if (configAddZeroes) {
377
print_text_fmt_int(get_left(HUD_LEFT_X)+17, HUD_TOP_Y_NEW-18, "%03d", gHudDisplay.coins);
378
}
379
else {
380
print_text(get_left(HUD_LEFT_X)+17, HUD_TOP_Y_NEW-18, "*"); // 'X' glyph
381
print_text_fmt_int(get_left(HUD_LEFT_X)+33, HUD_TOP_Y_NEW-18, "%d", gHudDisplay.coins);
382
}
383
}
384
else {
385
print_text(HUD_COIN_X, HUD_TOP_Y, "+"); // 'Coin' glyph
386
if (configAddZeroes) {
387
print_text_fmt_int(HUD_COIN_X+16, HUD_TOP_Y, "%03d", gHudDisplay.coins);
388
}
389
else {
390
print_text(HUD_COIN_X+16, HUD_TOP_Y, "*"); // 'X' glyph
391
print_text_fmt_int(HUD_COIN_X+30, HUD_TOP_Y, "%d", gHudDisplay.coins);
392
}
393
}
394
}
395
396
#ifdef VERSION_JP
397
#define HUD_STARS_X 73
398
#else
399
#define HUD_STARS_X 78
400
#endif
401
402
#define HUD_STARS_X_NEW 76
403
404
/**
405
* Renders the amount of stars collected.
406
* Disables "X" glyph when Mario has 100 stars or more.
407
*/
408
void render_hud_stars(void) {
409
s8 showX = 0;
410
411
if (gHudFlash == 1 && gGlobalTimer & 0x08) {
412
return;
413
}
414
415
if (configHudLayout == 2) {
416
if (!configAddZeroes && gHudDisplay.stars < 10) {
417
print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW, "-"); // 'Star' glyph
418
print_text(get_left(HUD_LEFT_X) + 17, HUD_TOP_Y_NEW, "*"); // 'X' glyph
419
print_text_fmt_int(get_left(HUD_LEFT_X) + 33, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars);
420
}
421
else if (!configAddZeroes && gHudDisplay.stars < 100) {
422
print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW, "-"); // 'Star' glyph
423
print_text(get_left(HUD_LEFT_X) + 17, HUD_TOP_Y_NEW, "*"); // 'X' glyph
424
print_text_fmt_int(get_left(HUD_LEFT_X) + 33, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars);
425
}
426
else {
427
print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW, "-"); // 'Star' glyph
428
print_text_fmt_int(get_left(HUD_LEFT_X) + 17, HUD_TOP_Y_NEW, configAddZeroes ? "%03d" : "%d", gHudDisplay.stars);
429
}
430
}
431
else if (configHudLayout == 1) {
432
if (!configAddZeroes && gHudDisplay.stars < 10) {
433
print_text(get_right(HUD_STARS_X_NEW) + 6, HUD_TOP_Y_NEW, "-"); // 'Star' glyph
434
print_text(get_right(HUD_STARS_X_NEW) + 23, HUD_TOP_Y_NEW, "*"); // 'X' glyph
435
print_text_fmt_int(get_right(HUD_STARS_X_NEW) + 39, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars);
436
}
437
else if (!configAddZeroes && gHudDisplay.stars < 100) {
438
print_text(get_right(HUD_STARS_X_NEW) - 4, HUD_TOP_Y_NEW, "-"); // 'Star' glyph
439
print_text(get_right(HUD_STARS_X_NEW) + 11, HUD_TOP_Y_NEW, "*"); // 'X' glyph
440
print_text_fmt_int(get_right(HUD_STARS_X_NEW) + 27, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars);
441
}
442
else {
443
print_text(get_right(HUD_STARS_X_NEW), HUD_TOP_Y_NEW, "-"); // 'Star' glyph
444
print_text_fmt_int(get_right(HUD_STARS_X_NEW) + 15, HUD_TOP_Y_NEW, configAddZeroes ? "%03d" : "%d", gHudDisplay.stars);
445
}
446
}
447
else {
448
if (!configAddZeroes && gHudDisplay.stars < 100) {
449
showX = 1;
450
}
451
452
print_text(get_right(HUD_STARS_X), HUD_TOP_Y, "-"); // 'Star' glyph
453
if (showX == 1) {
454
print_text(get_right(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph
455
}
456
print_text_fmt_int((showX * 14) + get_right(HUD_STARS_X - 16),
457
HUD_TOP_Y, configAddZeroes ? "%03d" : "%d", gHudDisplay.stars);
458
}
459
}
460
461
/**
462
* Unused function that renders the amount of keys collected.
463
* Leftover function from the beta version of the game.
464
*/
465
void render_hud_keys(void) {
466
s16 i;
467
468
for (i = 0; i < gHudDisplay.keys; i++) {
469
print_text((i * 16) + 220, 142, "/"); // unused glyph - beta key
470
}
471
}
472
473
/**
474
* Renders the timer when Mario start sliding in PSS.
475
*/
476
void render_hud_timer(void) {
477
u8 *(*hudLUT)[58];
478
u16 timerValFrames;
479
u16 timerMins;
480
u16 timerSecs;
481
u16 timerFracSecs;
482
483
hudLUT = segmented_to_virtual(&main_hud_lut);
484
timerValFrames = gHudDisplay.timer;
485
#ifdef VERSION_EU
486
switch (eu_get_language()) {
487
case LANGUAGE_ENGLISH:
488
print_text(get_right(150), 185, "TIME");
489
break;
490
case LANGUAGE_FRENCH:
491
print_text(get_right(155), 185, "TEMPS");
492
break;
493
case LANGUAGE_GERMAN:
494
print_text(get_right(150), 185, "ZEIT");
495
break;
496
}
497
#endif
498
timerMins = timerValFrames / (30 * 60);
499
timerSecs = (timerValFrames - (timerMins * 1800)) / 30;
500
501
timerFracSecs = ((timerValFrames - (timerMins * 1800) - (timerSecs * 30)) & 0xFFFF) / 3;
502
503
if (configHudLayout != 0) {
504
s32 add;
505
if (configHudLayout == 2)
506
add = 157;
507
else
508
add = 0;
509
print_text(get_right(151), HUD_TOP_Y_NEW-18-add, "TIME");
510
print_text_fmt_int(get_right(92), HUD_TOP_Y_NEW-18-add, "%0d", timerMins);
511
print_text_fmt_int(get_right(72), HUD_TOP_Y_NEW-18-add, "%02d", timerSecs);
512
print_text_fmt_int(get_right(38), HUD_TOP_Y_NEW-18-add, "%d", timerFracSecs);
513
gSPDisplayList(gDisplayListHead++, dl_hud_img_begin);
514
render_hud_tex_lut(get_right(82), 23+add, (*hudLUT)[GLYPH_APOSTROPHE]);
515
render_hud_tex_lut(get_right(47), 23+add, (*hudLUT)[GLYPH_DOUBLE_QUOTE]);
516
}
517
else {
518
#ifndef VERSION_EU
519
print_text(get_right(150), 185, "TIME");
520
#endif
521
print_text_fmt_int(get_right(91), 185, "%0d", timerMins);
522
print_text_fmt_int(get_right(71), 185, "%02d", timerSecs);
523
print_text_fmt_int(get_right(37), 185, "%d", timerFracSecs);
524
gSPDisplayList(gDisplayListHead++, dl_hud_img_begin);
525
render_hud_tex_lut(get_right(81), 32, (*hudLUT)[GLYPH_APOSTROPHE]);
526
render_hud_tex_lut(get_right(46), 32, (*hudLUT)[GLYPH_DOUBLE_QUOTE]);
527
}
528
gSPDisplayList(gDisplayListHead++, dl_hud_img_end);
529
}
530
531
/**
532
* Sets HUD status camera value depending of the actions
533
* defined in update_camera_status.
534
*/
535
void set_hud_camera_status(s16 status) {
536
sCameraHUD.status = status;
537
}
538
539
/**
540
* Renders camera HUD glyphs using a table list, depending of
541
* the camera status called, a defined glyph is rendered.
542
*/
543
void render_hud_camera_status(void) {
544
u8 *(*cameraLUT)[6];
545
s32 x;
546
s32 y;
547
548
cameraLUT = segmented_to_virtual(&main_hud_camera_lut);
549
if (configHudLayout == 1) {
550
x = get_right(53);
551
y = 208;
552
}
553
else {
554
x = get_right(54);
555
y = 205;
556
}
557
558
if (sCameraHUD.status == CAM_STATUS_NONE) {
559
return;
560
}
561
562
gSPDisplayList(gDisplayListHead++, dl_hud_img_begin);
563
render_hud_tex_lut(x, y, (*cameraLUT)[GLYPH_CAM_CAMERA]);
564
565
switch (sCameraHUD.status & CAM_STATUS_MODE_GROUP) {
566
case CAM_STATUS_MARIO:
567
render_hud_tex_lut(x + 16, y, (*cameraLUT)[GLYPH_CAM_MARIO_HEAD]);
568
break;
569
case CAM_STATUS_LAKITU:
570
render_hud_tex_lut(x + 16, y, (*cameraLUT)[GLYPH_CAM_LAKITU_HEAD]);
571
break;
572
case CAM_STATUS_FIXED:
573
render_hud_tex_lut(x + 16, y, (*cameraLUT)[GLYPH_CAM_FIXED]);
574
break;
575
}
576
577
switch (sCameraHUD.status & CAM_STATUS_C_MODE_GROUP) {
578
case CAM_STATUS_C_DOWN:
579
render_hud_small_tex_lut(x + 4, y + 16, (*cameraLUT)[GLYPH_CAM_ARROW_DOWN]);
580
break;
581
case CAM_STATUS_C_UP:
582
render_hud_small_tex_lut(x + 4, y - 8, (*cameraLUT)[GLYPH_CAM_ARROW_UP]);
583
break;
584
}
585
586
gSPDisplayList(gDisplayListHead++, dl_hud_img_end);
587
}
588
589
void render_hud_level_stars(s16 starCount) {
590
s32 i;
591
592
u8 flag = 1;
593
u8 starFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
594
595
u8 starText[] = { GLYPH_STAR, GLYPH_SPACE };
596
597
for (i = 0; i < starCount; i++, flag <<= 1) {
598
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
599
if (starFlags & flag) {
600
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255 * (1-sStarGetAlpha));
601
}
602
else {
603
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 63 * (1-sStarGetAlpha));
604
}
605
print_hud_lut_string(HUD_LUT_GLOBAL, get_left(HUD_LEFT_X) + i*16, SCREEN_HEIGHT-35, starText);
606
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
607
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
608
}
609
}
610
611
void hide_you_got_a_star() {
612
gHudDisplay.starGet = 0;
613
sStarGetSpeed = 0.0f;
614
sStarGetAlpha = 0.0f;
615
sStarGetBounce = 0.0f;
616
}
617
618
void render_you_got_a_star(u32 secondFrame) {
619
620
if (!gHudDisplay.starGet)
621
return;
622
623
u8 youGotAStar[] = { TEXT_YOU_GOT_A_STAR };
624
625
// Set the bounce speed at the start
626
if (sStarGetAlpha == 0.0f)
627
sStarGetSpeed = -4.0f;
628
629
// Gravity
630
sStarGetSpeed += 0.25f;
631
632
// Add the speed to the position and limit it
633
sStarGetBounce = MIN(sStarGetBounce + sStarGetSpeed, 0.0f);
634
635
Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx));
636
if (matrix) {
637
if (stay_in_level() || restart_level_after_star()) {
638
create_dl_translation_matrix(MENU_MTX_PUSH, (get_left(0)*2) / 2, 88.0f - (1.0f - sStarGetAlpha) * 32.0f, 0);
639
guScale(matrix, (SCREEN_WIDTH + get_right(0)*2) / 130.0f, 64.0f * sStarGetAlpha / 80.0f, 1.f);
640
}
641
else {
642
create_dl_translation_matrix(MENU_MTX_PUSH, (get_left(0)*2) / 2, 88.0f - (1.0f - sStarGetAlpha) * 16.0f, 0);
643
guScale(matrix, (SCREEN_WIDTH + get_right(0)*2) / 130.0f, 32.0f * sStarGetAlpha / 80.0f, 1.f);
644
}
645
}
646
647
// Rendering
648
if (secondFrame == 1) {
649
if (sStarGetDisplayListPos != NULL) {
650
gDPSetEnvColor(sStarGetDisplayListPos++, 255, 255, 255, 255 * sStarGetAlpha);
651
if (stay_in_level() || restart_level_after_star())
652
print_hud_lut_string_to_displaylist(HUD_LUT_GLOBAL, SCREEN_WIDTH / 2 - 78, 159 + sStarGetBounce, youGotAStar, sStarGetDisplayListPos);
653
else
654
print_hud_lut_string_to_displaylist(HUD_LUT_GLOBAL, SCREEN_WIDTH / 2 - 78, 160 + sStarGetBounce, youGotAStar, sStarGetDisplayListPos);
655
sStarGetDisplayListPos = NULL;
656
}
657
}
658
if (secondFrame == 0) {
659
660
// Black box
661
if (matrix) {
662
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
663
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255 * sStarGetAlpha / 2);
664
gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
665
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
666
}
667
668
// You got a star!
669
670
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
671
sStarGetDisplayListPos = gDisplayListHead;
672
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255 * sStarGetAlpha);
673
if (stay_in_level() || restart_level_after_star())
674
print_hud_lut_string(HUD_LUT_GLOBAL, SCREEN_WIDTH / 2 - 78, 159 + sStarGetBounce, youGotAStar);
675
else
676
print_hud_lut_string(HUD_LUT_GLOBAL, SCREEN_WIDTH / 2 - 78, 160 + sStarGetBounce, youGotAStar);
677
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
678
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
679
680
// Course and act name
681
if (stay_in_level() || restart_level_after_star()) {
682
void **courseNameTbl;
683
u8 *courseName;
684
void **actNameTbl;
685
u8 courseIndex;
686
687
// Gonna keep these ifdefs in case I decide to add compatibility with other region ROMs.
688
#ifndef VERSION_EU
689
courseNameTbl = segmented_to_virtual(seg2_course_name_table);
690
actNameTbl = segmented_to_virtual(seg2_act_name_table);
691
#endif
692
693
courseIndex = gCurrCourseNum - 1;
694
695
#ifdef VERSION_EU
696
switch (gInGameLanguage) {
697
case LANGUAGE_ENGLISH:
698
actNameTbl = segmented_to_virtual(act_name_table_eu_en);
699
courseNameTbl = segmented_to_virtual(course_name_table_eu_en);
700
break;
701
case LANGUAGE_FRENCH:
702
actNameTbl = segmented_to_virtual(act_name_table_eu_fr);
703
courseNameTbl = segmented_to_virtual(course_name_table_eu_fr);
704
break;
705
case LANGUAGE_GERMAN:
706
actNameTbl = segmented_to_virtual(act_name_table_eu_de);
707
courseNameTbl = segmented_to_virtual(course_name_table_eu_de);
708
break;
709
}
710
#endif
711
712
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
713
714
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255 * sStarGetAlpha);
715
716
courseName = segmented_to_virtual(courseNameTbl[courseIndex]);
717
718
if (courseIndex < COURSE_STAGES_COUNT && gCollectedStar < 6) {
719
u8 *actName = segmented_to_virtual(actNameTbl[(gCurrCourseNum - 1) * 6 + gCollectedStar]);
720
print_generic_string(get_str_x_pos_from_center(SCREEN_WIDTH/2, actName, 1.0f), 28, actName);
721
}
722
else {
723
u8 actName[] = { TEXT_POWER_STAR_PLACEHOLDER };
724
print_generic_string(get_str_x_pos_from_center(SCREEN_WIDTH/2, actName, 1.0f), 28, actName);
725
}
726
print_generic_string(get_str_x_pos_from_center(SCREEN_WIDTH/2, &courseName[3], 1.0f), 44, &courseName[3]);
727
728
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
729
}
730
}
731
732
// Using the speed as a timer since we never actually stop the speed from increasing
733
if (sStarGetSpeed > 31.0f) {
734
735
// Reduce the alpha, no one likes suddenly disappearing UI elements
736
sStarGetAlpha -= 0.0625f;
737
738
// If finally fully transparent, reset stuff
739
if (sStarGetAlpha <= 0.0f) {
740
hide_you_got_a_star();
741
}
742
}
743
// Fade in
744
else if (sStarGetAlpha < 1.0f)
745
sStarGetAlpha += 0.0625f;
746
}
747
748
/**
749
* Render HUD strings using hudDisplayFlags with it's render functions,
750
* excluding the cannon reticle which detects a camera preset for it.
751
*/
752
void render_hud(void) {
753
s16 hudDisplayFlags;
754
#ifdef VERSION_EU
755
Mtx *mtx;
756
#endif
757
758
hudDisplayFlags = gHudDisplay.flags;
759
760
if (hudDisplayFlags == HUD_DISPLAY_NONE) {
761
sPowerMeterHUD.animation = POWER_METER_HIDDEN;
762
sPowerMeterStoredHealth = 8;
763
sPowerMeterVisibleTimer = 0;
764
} else {
765
#ifdef VERSION_EU
766
// basically create_dl_ortho_matrix but guOrtho screen width is different
767
768
mtx = alloc_display_list(sizeof(*mtx));
769
if (mtx == NULL) {
770
return;
771
}
772
create_dl_identity_matrix();
773
guOrtho(mtx, -16.0f, SCREEN_WIDTH + 16, 0, SCREEN_HEIGHT, -10.0f, 10.0f, 1.0f);
774
gSPPerspNormalize(gDisplayListHead++, 0xFFFF);
775
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx),
776
G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH);
777
#else
778
create_dl_ortho_matrix();
779
#endif
780
781
if (gCurrentArea != NULL && gCurrentArea->camera->mode == CAMERA_MODE_INSIDE_CANNON) {
782
render_hud_cannon_reticle();
783
}
784
785
if (hudDisplayFlags & HUD_DISPLAY_FLAG_LIVES && !(save_file_get_flags() & SAVE_FLAG_HARDCORE_MODE) && configLifeMode != 1) {
786
render_hud_mario_lives();
787
}
788
789
if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT) {
790
render_hud_coins();
791
}
792
793
if (hudDisplayFlags & HUD_DISPLAY_FLAG_STAR_COUNT) {
794
render_hud_stars();
795
}
796
797
if (hudDisplayFlags & HUD_DISPLAY_FLAG_KEYS) {
798
render_hud_keys();
799
}
800
801
if (hudDisplayFlags & HUD_DISPLAY_FLAG_CAMERA_AND_POWER) {
802
if (!(save_file_get_flags() & SAVE_FLAG_DAREDEVIL_MODE))
803
render_hud_power_meter();
804
render_hud_camera_status();
805
}
806
807
if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) {
808
render_hud_timer();
809
}
810
811
if (configHudStars && gCurrDemoInput == NULL
812
&& gCurrLevelNum != LEVEL_CASTLE_GROUNDS && gCurrLevelNum != LEVEL_CASTLE && gCurrLevelNum != LEVEL_CASTLE_COURTYARD
813
&& gCurrLevelNum != LEVEL_BOWSER_1 && gCurrLevelNum != LEVEL_BOWSER_2 && gCurrLevelNum != LEVEL_BOWSER_3 && gCurrLevelNum != LEVEL_ENDING)
814
{
815
if (gCurrLevelNum == LEVEL_BITDW || gCurrLevelNum == LEVEL_BITFS || gCurrLevelNum == LEVEL_BITS ||
816
gCurrLevelNum == LEVEL_COTMC || gCurrLevelNum == LEVEL_TOTWC || gCurrLevelNum == LEVEL_VCUTM ||
817
gCurrLevelNum == LEVEL_WMOTR || gCurrLevelNum == LEVEL_SA) {
818
render_hud_level_stars(1);
819
}
820
else if (gCurrLevelNum == LEVEL_PSS) {
821
render_hud_level_stars(2);
822
}
823
else {
824
render_hud_level_stars(6 + (configShow100CoinStar || (save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1) & (1 << 6))));
825
}
826
}
827
828
if (gMarioWillDie) {
829
print_text_centered(SCREEN_WIDTH / 2, 160, "NOW YOU WILL DIE");
830
}
831
832
if (configStayInCourse && gCurrLevelNum != LEVEL_BOWSER_1 && gCurrLevelNum != LEVEL_BOWSER_2 && gCurrLevelNum != LEVEL_BOWSER_3) {
833
render_you_got_a_star(0);
834
}
835
}
836
}
837