Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/geo_misc.c
7858 views
1
#include <PR/ultratypes.h>
2
3
#include "sm64.h"
4
#include "geo_misc.h"
5
6
#include "area.h"
7
#include "engine/math_util.h"
8
#include "level_update.h"
9
#include "levels/castle_inside/header.h"
10
#include "levels/ending/header.h"
11
#include "levels/rr/header.h"
12
#include "mario.h"
13
#include "mario_actions_cutscene.h"
14
#include "memory.h"
15
#include "object_list_processor.h"
16
#include "rendering_graph_node.h"
17
#include "save_file.h"
18
#include "segment2.h"
19
20
/**
21
* @file geo_misc.c
22
* This file contains miscellaneous geo_asm scripts.
23
*
24
* In particular, it builds:
25
* - the light that shows the player where to look for Tower of the Wing Cap,
26
* - the flying carpets seen in Rainbow Ride, and
27
* - the end screen displaying Peach's delicious cake.
28
*/
29
30
#define NUM_FLYING_CARPET_VERTICES 21
31
extern const s16 flying_carpet_static_vertex_data[NUM_FLYING_CARPET_VERTICES];
32
33
static s16 sCurAreaTimer = 1;
34
static s16 sPrevAreaTimer = 0;
35
static s16 sFlyingCarpetRippleTimer = 0;
36
37
s8 gFlyingCarpetState;
38
39
/**
40
* Create a vertex with the given parameters and insert it into `vtx` at
41
* position `n`.
42
*
43
* Texture coordinates are s10.5 fixed-point, which means you should left-shift the actual coordinates by 5.
44
*/
45
#ifndef GBI_FLOATS
46
void make_vertex(Vtx *vtx, s32 n, s16 x, s16 y, s16 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a) {
47
#else
48
void make_vertex(Vtx *vtx, s32 n, f32 x, f32 y, f32 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a) {
49
#endif
50
vtx[n].v.ob[0] = x;
51
vtx[n].v.ob[1] = y;
52
vtx[n].v.ob[2] = z;
53
54
vtx[n].v.flag = 0;
55
56
vtx[n].v.tc[0] = tx;
57
vtx[n].v.tc[1] = ty;
58
59
vtx[n].v.cn[0] = r;
60
vtx[n].v.cn[1] = g;
61
vtx[n].v.cn[2] = b;
62
vtx[n].v.cn[3] = a;
63
}
64
65
/**
66
* Round `num` to the nearest `s16`.
67
*/
68
s16 round_float(f32 num) {
69
// Note that double literals are used here, rather than float literals.
70
if (num >= 0.0) {
71
return num + 0.5;
72
} else {
73
return num - 0.5;
74
}
75
}
76
77
/**
78
* Create a display list for the light in the castle lobby that shows the
79
* player where to look to enter Tower of the Wing Cap.
80
*/
81
Gfx *geo_exec_inside_castle_light(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
82
s32 flags;
83
struct GraphNodeGenerated *generatedNode;
84
Gfx *displayListHead = NULL;
85
Gfx *displayList = NULL;
86
87
if (callContext == GEO_CONTEXT_RENDER) {
88
flags = save_file_get_flags();
89
if (gHudDisplay.stars >= 10 && !(flags & SAVE_FLAG_HAVE_WING_CAP)) {
90
displayList = alloc_display_list(2 * sizeof(*displayList));
91
92
if (displayList == NULL) {
93
return NULL;
94
} else {
95
displayListHead = displayList;
96
}
97
98
generatedNode = (struct GraphNodeGenerated *) node;
99
generatedNode->fnNode.node.flags = (generatedNode->fnNode.node.flags & 0xFF) | 0x500;
100
101
gSPDisplayList(displayListHead++, dl_castle_lobby_wing_cap_light);
102
gSPEndDisplayList(displayListHead);
103
}
104
}
105
106
return displayList;
107
}
108
109
/**
110
* Update static timer variables that control the flying carpets' ripple effect.
111
*/
112
Gfx *geo_exec_flying_carpet_timer_update(s32 callContext, UNUSED struct GraphNode *node,
113
UNUSED f32 mtx[4][4]) {
114
if (callContext != GEO_CONTEXT_RENDER) {
115
sFlyingCarpetRippleTimer = 0;
116
sPrevAreaTimer = gAreaUpdateCounter - 1;
117
sCurAreaTimer = gAreaUpdateCounter;
118
gFlyingCarpetState = FLYING_CARPET_IDLE;
119
} else {
120
sPrevAreaTimer = sCurAreaTimer;
121
sCurAreaTimer = gAreaUpdateCounter;
122
if (sPrevAreaTimer != sCurAreaTimer) {
123
sFlyingCarpetRippleTimer += 0x400;
124
}
125
}
126
127
return NULL;
128
}
129
130
/**
131
* Create a display list for a flying carpet with dynamic ripples.
132
*/
133
Gfx *geo_exec_flying_carpet_create(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
134
s16 n, row, col, x, y, z, tx, ty;
135
Vtx *verts;
136
struct GraphNodeGenerated *generatedNode = (struct GraphNodeGenerated *) node;
137
138
s16 *sp64 = segmented_to_virtual(&flying_carpet_static_vertex_data);
139
Gfx *displayList = NULL;
140
Gfx *displayListHead = NULL;
141
struct Object *curGraphNodeObject;
142
143
if (callContext == GEO_CONTEXT_RENDER) {
144
verts = alloc_display_list(NUM_FLYING_CARPET_VERTICES * sizeof(*verts));
145
displayList = alloc_display_list(7 * sizeof(*displayList));
146
displayListHead = displayList;
147
148
if (verts == NULL || displayList == NULL) {
149
return NULL;
150
}
151
152
generatedNode->fnNode.node.flags = (generatedNode->fnNode.node.flags & 0xFF) | 0x100;
153
154
for (n = 0; n <= 20; n++) {
155
row = n / 3;
156
col = n % 3;
157
158
x = sp64[n * 4 + 0];
159
y = round_float(sins(sFlyingCarpetRippleTimer + (row << 12) + (col << 14)) * 20.0);
160
z = sp64[n * 4 + 1];
161
tx = sp64[n * 4 + 2];
162
ty = sp64[n * 4 + 3];
163
164
make_vertex(verts, n, x, y, z, tx, ty, 0, 127, 0, 255);
165
}
166
167
gSPDisplayList(displayListHead++, dl_flying_carpet_begin);
168
169
// The forward half.
170
gSPVertex(displayListHead++, verts, 12, 0);
171
gSPDisplayList(displayListHead++, dl_flying_carpet_model_half);
172
173
// The back half.
174
gSPVertex(displayListHead++, verts + 9, 12, 0);
175
gSPDisplayList(displayListHead++, dl_flying_carpet_model_half);
176
177
gSPDisplayList(displayListHead++, dl_flying_carpet_end);
178
gSPEndDisplayList(displayListHead);
179
180
curGraphNodeObject = (struct Object *) gCurGraphNodeObject;
181
if (gMarioObject->platform == curGraphNodeObject) {
182
gFlyingCarpetState = FLYING_CARPET_MOVING_WITH_MARIO;
183
} else if (curGraphNodeObject->oForwardVel != 0.0) {
184
gFlyingCarpetState = FLYING_CARPET_MOVING_WITHOUT_MARIO;
185
} else {
186
gFlyingCarpetState = FLYING_CARPET_IDLE;
187
}
188
}
189
190
return displayList;
191
}
192
193
/**
194
* Create a display list for the end screen with Peach's delicious cake.
195
*/
196
Gfx *geo_exec_cake_end_screen(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) {
197
struct GraphNodeGenerated *generatedNode = (struct GraphNodeGenerated *) node;
198
Gfx *displayList = NULL;
199
Gfx *displayListHead = NULL;
200
201
if (callContext == GEO_CONTEXT_RENDER) {
202
displayList = alloc_display_list(3 * sizeof(*displayList));
203
displayListHead = displayList;
204
205
generatedNode->fnNode.node.flags = (generatedNode->fnNode.node.flags & 0xFF) | 0x100;
206
#ifdef VERSION_EU
207
gSPDisplayList(displayListHead++, dl_cake_end_screen);
208
#else
209
gSPDisplayList(displayListHead++, dl_proj_mtx_fullscreen);
210
#endif
211
#ifdef VERSION_EU
212
switch (eu_get_language()) {
213
case LANGUAGE_ENGLISH:
214
gSPDisplayList(displayListHead++, dl_cake_end_screen_eu_070296F8);
215
break;
216
case LANGUAGE_FRENCH:
217
gSPDisplayList(displayListHead++, dl_cake_end_screen_eu_07029768);
218
break;
219
case LANGUAGE_GERMAN:
220
gSPDisplayList(displayListHead++, dl_cake_end_screen_eu_070297D8);
221
break;
222
}
223
#else
224
gSPDisplayList(displayListHead++, dl_cake_end_screen);
225
#endif
226
gSPEndDisplayList(displayListHead);
227
}
228
229
return displayList;
230
}
231
232