Code:

#include <ultra64.h> #include "sm64.h" #include "geo_misc.h" #include "area.h" #include "engine/math_util.h" #include "level_update.h" #include "mario_actions_cutscene.h" #include "mario.h" #include "memory.h" #include "rendering_graph_node.h" #include "save_file.h" #include "segment2.h" /** * @file geo_misc.c * This file contains miscellaneous geo_asm scripts. * * In particular, it builds: * - the light that shows the player where to look for Tower of the Wing Cap, * - the flying carpets seen in Rainbow Ride, and * - the end screen displaying Peach's delicious cake. */ #define NUM_FLYING_CARPET_VERTICES 21 extern s16 flying_carpet_static_vertex_data[NUM_FLYING_CARPET_VERTICES]; extern Gfx dl_castle_lobby_wing_cap_light[]; extern Gfx dl_flying_carpet_begin[]; extern Gfx dl_flying_carpet_model_half[]; extern Gfx dl_flying_carpet_end[]; extern Gfx dl_cake_end_screen[]; static s16 sCurAreaTimer = 1; static s16 sPrevAreaTimer = 0; static s16 sFlyingCarpetRippleTimer = 0; s8 gFlyingCarpetState; /** * Create a vertex with the given parameters and insert it into `vtx` at * position `n`. */ 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 ) { vtx[n].v.ob[0] = x; vtx[n].v.ob[1] = y; vtx[n].v.ob[2] = z; vtx[n].v.flag = 0; vtx[n].v.tc[0] = tx; vtx[n].v.tc[1] = ty; vtx[n].v.cn[0] = r; vtx[n].v.cn[1] = g; vtx[n].v.cn[2] = b; vtx[n].v.cn[3] = a; } /** * Round `num` to the nearest `s16`. */ s16 round_float(f32 num) { // Note that double literals are used here, rather than float literals. if (num >= 0.0) { return num + 0.5; } else { return num - 0.5; } } /** * Create a display list for the light in the castle lobby that shows the * player where to look to enter Tower of the Wing Cap. */ Gfx *geo_exec_inside_castle_light( s32 run, struct GraphNode *node, UNUSED f32 mtx[4][4] ) { s32 flags; struct GraphNode12A *generatedNode; Gfx *displayListHead = NULL; Gfx *displayList = NULL; if (run == TRUE) { flags = save_file_get_flags(); if (gDisplayedStars >= 10 && (flags & SAVE_FLAG_HAVE_WING_CAP) == 0) { displayList = alloc_display_list(2 * sizeof(*displayList)); if (displayList == NULL) { return NULL; } else { displayListHead = displayList; } generatedNode = (struct GraphNode12A *) node; generatedNode->fnNode.node.flags = ( generatedNode->fnNode.node.flags & 0xFF ) | 0x500; gSPDisplayList(displayListHead++, dl_castle_lobby_wing_cap_light); gSPEndDisplayList(displayListHead); } } return displayList; } /** * Update static timer variables that control the flying carpets' ripple effect. */ Gfx *geo_exec_flying_carpet_timer_update( s32 run, UNUSED struct GraphNode *node, UNUSED f32 mtx[4][4] ) { if (run != TRUE) { sFlyingCarpetRippleTimer = 0; sPrevAreaTimer = gAreaUpdateCounter - 1; sCurAreaTimer = gAreaUpdateCounter; gFlyingCarpetState = FLYING_CARPET_IDLE; } else { sPrevAreaTimer = sCurAreaTimer; sCurAreaTimer = gAreaUpdateCounter; if (sPrevAreaTimer != sCurAreaTimer) { sFlyingCarpetRippleTimer += 0x400; } } return NULL; } /** * Create a display list for a flying carpet with dynamic ripples. */ Gfx *geo_exec_flying_carpet_create( s32 run, struct GraphNode *node, UNUSED f32 mtx[4][4] ) { s16 n, row, col, x, y, z, tx, ty; Vtx *verts; struct GraphNode12A *generatedNode = (struct GraphNode12A *) node; s16 *sp64 = segmented_to_virtual(&flying_carpet_static_vertex_data); Gfx *displayList = NULL; Gfx *displayListHead = NULL; struct Object *curGraphNodeObject; if (run == TRUE) { verts = alloc_display_list(NUM_FLYING_CARPET_VERTICES * sizeof(*verts)); displayList = alloc_display_list(7 * sizeof(*displayList)); displayListHead = displayList; if (verts == NULL || displayList == NULL) { return NULL; } generatedNode->fnNode.node.flags = ( generatedNode->fnNode.node.flags & 0xFF ) | 0x100; for (n = 0; n <= 20; n++) { row = n / 3; col = n % 3; x = sp64[n * 4 + 0]; y = round_float( sins( sFlyingCarpetRippleTimer + (row << 12) + (col << 14) ) * 20.0 ); z = sp64[n * 4 + 1]; tx = sp64[n * 4 + 2]; ty = sp64[n * 4 + 3]; make_vertex(verts, n, x, y, z, tx, ty, 0, 127, 0, 255); } gSPDisplayList(displayListHead++, dl_flying_carpet_begin); // The forward half. gSPVertex(displayListHead++, verts, 12, 0); gSPDisplayList(displayListHead++, dl_flying_carpet_model_half); // The back half. gSPVertex(displayListHead++, verts + 9, 12, 0); gSPDisplayList(displayListHead++, dl_flying_carpet_model_half); gSPDisplayList(displayListHead++, dl_flying_carpet_end); gSPEndDisplayList(displayListHead); curGraphNodeObject = (struct Object *) D_8032CFA0; if (gMarioObject->platform == curGraphNodeObject) { gFlyingCarpetState = FLYING_CARPET_MOVING_WITH_MARIO; } else if (curGraphNodeObject->oForwardVel != 0.0) { gFlyingCarpetState = FLYING_CARPET_MOVING_WITHOUT_MARIO; } else { gFlyingCarpetState = FLYING_CARPET_IDLE; } } return displayList; } /** * Create a display list for the end screen with Peach's delicious cake. */ Gfx *geo_exec_cake_end_screen( s32 run, struct GraphNode *node, UNUSED f32 mtx[4][4] ) { struct GraphNode12A *generatedNode = (struct GraphNode12A *) node; Gfx *displayList = NULL; Gfx *displayListHead = NULL; if (run == TRUE) { displayList = alloc_display_list(3 * sizeof(*displayList)); displayListHead = displayList; generatedNode->fnNode.node.flags = ( generatedNode->fnNode.node.flags & 0xFF ) | 0x100; gSPDisplayList(displayListHead++, dl_proj_mtx_fullscreen); gSPDisplayList(displayListHead++, dl_cake_end_screen); gSPEndDisplayList(displayListHead); } return displayList; }