Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/area.c
7858 views
1
#include <PR/ultratypes.h>
2
3
#include "prevent_bss_reordering.h"
4
#include "area.h"
5
#include "sm64.h"
6
#include "gfx_dimensions.h"
7
#include "behavior_data.h"
8
#include "game_init.h"
9
#include "object_list_processor.h"
10
#include "engine/surface_load.h"
11
#include "ingame_menu.h"
12
#include "screen_transition.h"
13
#include "mario.h"
14
#include "mario_actions_cutscene.h"
15
#include "print.h"
16
#include "hud.h"
17
#include "audio/external.h"
18
#include "area.h"
19
#include "rendering_graph_node.h"
20
#include "level_update.h"
21
#include "engine/geo_layout.h"
22
#include "save_file.h"
23
#include "level_table.h"
24
#include "dialog_ids.h"
25
26
27
#include "object_helpers.h"
28
#include "settings.h"
29
30
struct SpawnInfo gPlayerSpawnInfos[1];
31
struct GraphNode *D_8033A160[0x100];
32
struct Area gAreaData[8];
33
34
struct WarpTransition gWarpTransition;
35
36
s16 gCurrCourseNum;
37
s16 gCurrActNum;
38
s16 gCurrAreaIndex;
39
s16 gSavedCourseNum;
40
s16 gMenuOptSelectIndex;
41
s16 gSaveOptSelectIndex;
42
43
struct SpawnInfo *gMarioSpawnInfo = &gPlayerSpawnInfos[0];
44
struct GraphNode **gLoadedGraphNodes = D_8033A160;
45
struct Area *gAreas = gAreaData;
46
struct Area *gCurrentArea = NULL;
47
struct CreditsEntry *gCurrCreditsEntry = NULL;
48
Vp *D_8032CE74 = NULL;
49
Vp *D_8032CE78 = NULL;
50
s16 gWarpTransDelay = 0;
51
u32 gFBSetColor = 0;
52
u32 gWarpTransFBSetColor = 0;
53
u8 gWarpTransRed = 0;
54
u8 gWarpTransGreen = 0;
55
u8 gWarpTransBlue = 0;
56
s16 gCurrSaveFileNum = 1;
57
s16 gCurrLevelNum = LEVEL_MIN;
58
59
/*
60
* The following two tables are used in get_mario_spawn_type() to determine spawn type
61
* from warp behavior.
62
* When looping through sWarpBhvSpawnTable, if the behavior function in the table matches
63
* the spawn behavior executed, the index of that behavior is used with sSpawnTypeFromWarpBhv
64
*/
65
66
const BehaviorScript *sWarpBhvSpawnTable[] = {
67
bhvDoorWarp, bhvStar, bhvExitPodiumWarp, bhvWarp,
68
bhvWarpPipe, bhvFadingWarp, bhvInstantActiveWarp, bhvAirborneWarp,
69
bhvHardAirKnockBackWarp, bhvSpinAirborneCircleWarp, bhvDeathWarp, bhvSpinAirborneWarp,
70
bhvFlyingWarp, bhvSwimmingWarp, bhvPaintingStarCollectWarp, bhvPaintingDeathWarp,
71
bhvAirborneStarCollectWarp, bhvAirborneDeathWarp, bhvLaunchStarCollectWarp, bhvLaunchDeathWarp,
72
};
73
74
u8 sSpawnTypeFromWarpBhv[] = {
75
MARIO_SPAWN_DOOR_WARP, MARIO_SPAWN_UNKNOWN_02, MARIO_SPAWN_UNKNOWN_03, MARIO_SPAWN_UNKNOWN_03,
76
MARIO_SPAWN_UNKNOWN_03, MARIO_SPAWN_TELEPORT, MARIO_SPAWN_INSTANT_ACTIVE, MARIO_SPAWN_AIRBORNE,
77
MARIO_SPAWN_HARD_AIR_KNOCKBACK, MARIO_SPAWN_SPIN_AIRBORNE_CIRCLE, MARIO_SPAWN_DEATH, MARIO_SPAWN_SPIN_AIRBORNE,
78
MARIO_SPAWN_FLYING, MARIO_SPAWN_SWIMMING, MARIO_SPAWN_PAINTING_STAR_COLLECT, MARIO_SPAWN_PAINTING_DEATH,
79
MARIO_SPAWN_AIRBORNE_STAR_COLLECT, MARIO_SPAWN_AIRBORNE_DEATH, MARIO_SPAWN_LAUNCH_STAR_COLLECT, MARIO_SPAWN_LAUNCH_DEATH,
80
};
81
82
Vp D_8032CF00 = { {
83
{ 640, 480, 511, 0 },
84
{ 640, 480, 511, 0 },
85
} };
86
87
#ifdef VERSION_EU
88
const char *gNoControllerMsg[] = {
89
"NO CONTROLLER",
90
"MANETTE DEBRANCHEE",
91
"CONTROLLER FEHLT",
92
};
93
#endif
94
95
void override_viewport_and_clip(Vp *a, Vp *b, u8 c, u8 d, u8 e) {
96
u16 sp6 = ((c >> 3) << 11) | ((d >> 3) << 6) | ((e >> 3) << 1) | 1;
97
98
gFBSetColor = (sp6 << 16) | sp6;
99
D_8032CE74 = a;
100
D_8032CE78 = b;
101
}
102
103
void set_warp_transition_rgb(u8 red, u8 green, u8 blue) {
104
u16 warpTransitionRGBA16 = ((red >> 3) << 11) | ((green >> 3) << 6) | ((blue >> 3) << 1) | 1;
105
106
gWarpTransFBSetColor = (warpTransitionRGBA16 << 16) | warpTransitionRGBA16;
107
gWarpTransRed = red;
108
gWarpTransGreen = green;
109
gWarpTransBlue = blue;
110
}
111
112
void print_intro_text(s8 in_game) {
113
#ifdef VERSION_EU
114
s32 language = eu_get_language();
115
#endif
116
if ((gGlobalTimer & 0x1F) < 20) {
117
if (gControllerBits == 0) {
118
#ifdef VERSION_EU
119
print_text_centered(SCREEN_WIDTH / 2, 20, gNoControllerMsg[language]);
120
#else
121
print_text_centered(SCREEN_WIDTH / 2, 20, "NO CONTROLLER");
122
#endif
123
} else {
124
#ifdef VERSION_EU
125
print_text(20, 20, "START");
126
#else
127
s32 left = (config4by3Hud || configAspectRatio == 1) ? 60 : GFX_DIMENSIONS_FROM_LEFT_EDGE(60);
128
129
print_text_centered(left, 38, "PRESS");
130
print_text_centered(left, 20, "START");
131
#endif
132
}
133
}
134
}
135
136
u32 get_mario_spawn_type(struct Object *o) {
137
s32 i;
138
const BehaviorScript *behavior = virtual_to_segmented(0x13, o->behavior);
139
140
for (i = 0; i < 20; i++) {
141
if (sWarpBhvSpawnTable[i] == behavior) {
142
return sSpawnTypeFromWarpBhv[i];
143
}
144
}
145
return 0;
146
}
147
148
struct ObjectWarpNode *area_get_warp_node(u8 id) {
149
struct ObjectWarpNode *node = NULL;
150
151
for (node = gCurrentArea->warpNodes; node != NULL; node = node->next) {
152
if (node->node.id == id) {
153
break;
154
}
155
}
156
return node;
157
}
158
159
struct ObjectWarpNode *area_get_warp_node_from_params(struct Object *o) {
160
u8 sp1F = (o->oBehParams & 0x00FF0000) >> 16;
161
162
return area_get_warp_node(sp1F);
163
}
164
165
void load_obj_warp_nodes(void) {
166
struct ObjectWarpNode *sp24;
167
struct Object *sp20 = (struct Object *) gObjParentGraphNode.children;
168
169
#ifdef USE_SYSTEM_MALLOC
170
if (sp20 == NULL) {
171
return;
172
}
173
#endif
174
do {
175
struct Object *sp1C = sp20;
176
177
if (sp1C->activeFlags != ACTIVE_FLAG_DEACTIVATED && get_mario_spawn_type(sp1C) != 0) {
178
sp24 = area_get_warp_node_from_params(sp1C);
179
if (sp24 != NULL) {
180
sp24->object = sp1C;
181
}
182
}
183
} while ((sp20 = (struct Object *) sp20->header.gfx.node.next)
184
!= (struct Object *) gObjParentGraphNode.children);
185
}
186
187
void clear_areas(void) {
188
s32 i;
189
190
gCurrentArea = NULL;
191
gWarpTransition.isActive = FALSE;
192
gWarpTransition.pauseRendering = FALSE;
193
gMarioSpawnInfo->areaIndex = -1;
194
195
for (i = 0; i < 8; i++) {
196
gAreaData[i].index = i;
197
gAreaData[i].flags = 0;
198
gAreaData[i].terrainType = 0;
199
gAreaData[i].unk04 = NULL;
200
gAreaData[i].terrainData = NULL;
201
gAreaData[i].surfaceRooms = NULL;
202
gAreaData[i].macroObjects = NULL;
203
gAreaData[i].warpNodes = NULL;
204
gAreaData[i].paintingWarpNodes = NULL;
205
gAreaData[i].instantWarps = NULL;
206
gAreaData[i].objectSpawnInfos = NULL;
207
gAreaData[i].camera = NULL;
208
gAreaData[i].unused28 = NULL;
209
gAreaData[i].whirlpools[0] = NULL;
210
gAreaData[i].whirlpools[1] = NULL;
211
gAreaData[i].dialog[0] = DIALOG_NONE;
212
gAreaData[i].dialog[1] = DIALOG_NONE;
213
gAreaData[i].musicParam = 0;
214
gAreaData[i].musicParam2 = 0;
215
}
216
}
217
218
void clear_area_graph_nodes(void) {
219
s32 i;
220
221
if (gCurrentArea != NULL) {
222
geo_call_global_function_nodes(&gCurrentArea->unk04->node, GEO_CONTEXT_AREA_UNLOAD);
223
gCurrentArea = NULL;
224
gWarpTransition.isActive = FALSE;
225
}
226
227
for (i = 0; i < 8; i++) {
228
if (gAreaData[i].unk04 != NULL) {
229
geo_call_global_function_nodes(&gAreaData[i].unk04->node, GEO_CONTEXT_AREA_INIT);
230
gAreaData[i].unk04 = NULL;
231
}
232
}
233
}
234
235
void load_area(s32 index) {
236
if (gCurrentArea == NULL && gAreaData[index].unk04 != NULL) {
237
gCurrentArea = &gAreaData[index];
238
gCurrAreaIndex = gCurrentArea->index;
239
240
if (gCurrentArea->terrainData != NULL) {
241
load_area_terrain(index, gCurrentArea->terrainData, gCurrentArea->surfaceRooms,
242
gCurrentArea->macroObjects);
243
}
244
245
if (gCurrentArea->objectSpawnInfos != NULL) {
246
spawn_objects_from_info(0, gCurrentArea->objectSpawnInfos);
247
}
248
249
load_obj_warp_nodes();
250
geo_call_global_function_nodes(&gCurrentArea->unk04->node, GEO_CONTEXT_AREA_LOAD);
251
}
252
}
253
254
void unload_area(void) {
255
if (gCurrentArea != NULL) {
256
unload_objects_from_area(0, gCurrentArea->index);
257
geo_call_global_function_nodes(&gCurrentArea->unk04->node, GEO_CONTEXT_AREA_UNLOAD);
258
259
gCurrentArea->flags = 0;
260
gCurrentArea = NULL;
261
gWarpTransition.isActive = FALSE;
262
}
263
}
264
265
void load_mario_area(void) {
266
stop_sounds_in_continuous_banks();
267
load_area(gMarioSpawnInfo->areaIndex);
268
269
hide_you_got_a_star();
270
gMarioWillDie = FALSE;
271
272
if (gCurrDemoInput == NULL)
273
gCanMirror = 1;
274
if (configEncoreMode)
275
gReimportTextures = 1;
276
277
if (gCurrentArea->index == gMarioSpawnInfo->areaIndex) {
278
gCurrentArea->flags |= 0x01;
279
spawn_objects_from_info(0, gMarioSpawnInfo);
280
}
281
282
if (configGreenDemon > 1 && gCurrLevelNum != LEVEL_CASTLE && gCurrLevelNum != LEVEL_CASTLE_COURTYARD && gCurrLevelNum != LEVEL_CASTLE_GROUNDS && gCurrLevelNum != LEVEL_BOWSER_1 && gCurrLevelNum != LEVEL_BOWSER_2 && gCurrLevelNum != LEVEL_BOWSER_3) {
283
spawn_object_relative(0, 0, 256, 0, gMarioObject, MODEL_1UP, bhvGreenDemon);
284
}
285
}
286
287
void unload_mario_area(void) {
288
if (gCurrentArea != NULL && (gCurrentArea->flags & 0x01)) {
289
unload_objects_from_area(0, gMarioSpawnInfo->activeAreaIndex);
290
291
gCurrentArea->flags &= ~0x01;
292
if (gCurrentArea->flags == 0) {
293
unload_area();
294
}
295
}
296
}
297
298
void change_area(s32 index) {
299
s32 areaFlags = gCurrentArea->flags;
300
301
if (gCurrAreaIndex != index) {
302
unload_area();
303
load_area(index);
304
305
gCurrentArea->flags = areaFlags;
306
gMarioObject->oActiveParticleFlags = 0;
307
}
308
309
if (areaFlags & 0x01) {
310
gMarioObject->header.gfx.areaIndex = index, gMarioSpawnInfo->areaIndex = index;
311
}
312
}
313
314
void area_update_objects(void) {
315
gAreaUpdateCounter++;
316
update_objects(0);
317
}
318
319
/*
320
* Sets up the information needed to play a warp transition, including the
321
* transition type, time in frames, and the RGB color that will fill the screen.
322
*/
323
void play_transition(s16 transType, s16 time, u8 red, u8 green, u8 blue) {
324
gWarpTransition.isActive = TRUE;
325
gWarpTransition.type = transType;
326
gWarpTransition.time = time;
327
gWarpTransition.pauseRendering = FALSE;
328
329
// The lowest bit of transType determines if the transition is fading in or out.
330
if (transType & 1) {
331
set_warp_transition_rgb(red, green, blue);
332
} else {
333
red = gWarpTransRed, green = gWarpTransGreen, blue = gWarpTransBlue;
334
}
335
336
if (transType < 8) { // if transition is RGB
337
gWarpTransition.data.red = red;
338
gWarpTransition.data.green = green;
339
gWarpTransition.data.blue = blue;
340
} else { // if transition is textured
341
gWarpTransition.data.red = red;
342
gWarpTransition.data.green = green;
343
gWarpTransition.data.blue = blue;
344
345
// Both the start and end textured transition are always located in the middle of the screen.
346
// If you really wanted to, you could place the start at one corner and the end at
347
// the opposite corner. This will make the transition image look like it is moving
348
// across the screen.
349
gWarpTransition.data.startTexX = SCREEN_WIDTH / 2;
350
gWarpTransition.data.startTexY = SCREEN_HEIGHT / 2;
351
gWarpTransition.data.endTexX = SCREEN_WIDTH / 2;
352
gWarpTransition.data.endTexY = SCREEN_HEIGHT / 2;
353
354
gWarpTransition.data.texTimer = 0;
355
356
if (transType & 1) // Is the image fading in?
357
{
358
gWarpTransition.data.startTexRadius = GFX_DIMENSIONS_FULL_RADIUS;
359
if (transType >= 0x0F) {
360
gWarpTransition.data.endTexRadius = 16;
361
} else {
362
gWarpTransition.data.endTexRadius = 0;
363
}
364
} else // The image is fading out. (Reverses start & end circles)
365
{
366
if (transType >= 0x0E) {
367
gWarpTransition.data.startTexRadius = 16;
368
} else {
369
gWarpTransition.data.startTexRadius = 0;
370
}
371
gWarpTransition.data.endTexRadius = GFX_DIMENSIONS_FULL_RADIUS;
372
}
373
}
374
}
375
376
/*
377
* Sets up the information needed to play a warp transition, including the
378
* transition type, time in frames, and the RGB color that will fill the screen.
379
* The transition will play only after a number of frames specified by 'delay'
380
*/
381
void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 blue, s16 delay) {
382
gWarpTransDelay = delay; // Number of frames to delay playing the transition.
383
play_transition(transType, time, red, green, blue);
384
}
385
386
void render_game(void) {
387
if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
388
geo_process_root(gCurrentArea->unk04, D_8032CE74, D_8032CE78, gFBSetColor);
389
390
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&D_8032CF00));
391
392
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
393
SCREEN_HEIGHT - BORDER_HEIGHT);
394
if (!configHideHud) {
395
render_hud();
396
}
397
398
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
399
render_text_labels();
400
do_cutscene_handler();
401
print_displaying_credits_entry();
402
403
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
404
SCREEN_HEIGHT - BORDER_HEIGHT);
405
gMenuOptSelectIndex = render_menus_and_dialogs();
406
if (gMenuOptSelectIndex != MENU_OPT_NONE) {
407
gSaveOptSelectIndex = gMenuOptSelectIndex;
408
}
409
410
if (D_8032CE78 != NULL) {
411
make_viewport_clip_rect(D_8032CE78);
412
} else {
413
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
414
SCREEN_HEIGHT - BORDER_HEIGHT);
415
}
416
417
if (gWarpTransition.isActive) {
418
if (gWarpTransDelay == 0) {
419
gWarpTransition.isActive = !render_screen_transition(0, gWarpTransition.type, gWarpTransition.time,
420
&gWarpTransition.data);
421
if (!gWarpTransition.isActive) {
422
if (gWarpTransition.type & 1) {
423
gWarpTransition.pauseRendering = TRUE;
424
} else {
425
set_warp_transition_rgb(0, 0, 0);
426
}
427
}
428
} else {
429
gWarpTransDelay--;
430
}
431
}
432
} else {
433
render_text_labels();
434
if (D_8032CE78 != NULL) {
435
clear_viewport(D_8032CE78, gWarpTransFBSetColor);
436
} else {
437
clear_frame_buffer(gWarpTransFBSetColor);
438
}
439
}
440
441
D_8032CE74 = NULL;
442
D_8032CE78 = NULL;
443
}
444