geo_misc.c (7283B)
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 { 51 vtx[n].v.ob[0] = x; 52 vtx[n].v.ob[1] = y; 53 vtx[n].v.ob[2] = z; 54 55 vtx[n].v.flag = 0; 56 57 vtx[n].v.tc[0] = tx; 58 vtx[n].v.tc[1] = ty; 59 60 vtx[n].v.cn[0] = r; 61 vtx[n].v.cn[1] = g; 62 vtx[n].v.cn[2] = b; 63 vtx[n].v.cn[3] = a; 64 } 65 66 /** 67 * Round `num` to the nearest `s16`. 68 */ 69 s16 round_float(f32 num) { 70 // Note that double literals are used here, rather than float literals. 71 if (num >= 0.0) { 72 return num + 0.5; 73 } else { 74 return num - 0.5; 75 } 76 } 77 78 /** 79 * Create a display list for the light in the castle lobby that shows the 80 * player where to look to enter Tower of the Wing Cap. 81 */ 82 Gfx *geo_exec_inside_castle_light(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) { 83 s32 flags; 84 struct GraphNodeGenerated *generatedNode; 85 Gfx *displayListHead = NULL; 86 Gfx *displayList = NULL; 87 88 if (callContext == GEO_CONTEXT_RENDER) { 89 flags = save_file_get_flags(); 90 if (gHudDisplay.stars >= 10 && !(flags & SAVE_FLAG_HAVE_WING_CAP)) { 91 displayList = alloc_display_list(2 * sizeof(*displayList)); 92 93 if (displayList == NULL) { 94 return NULL; 95 } else { 96 displayListHead = displayList; 97 } 98 99 generatedNode = (struct GraphNodeGenerated *) node; 100 generatedNode->fnNode.node.flags = (generatedNode->fnNode.node.flags & 0xFF) | 0x500; 101 102 gSPDisplayList(displayListHead++, dl_castle_lobby_wing_cap_light); 103 gSPEndDisplayList(displayListHead); 104 } 105 } 106 107 return displayList; 108 } 109 110 /** 111 * Update static timer variables that control the flying carpets' ripple effect. 112 */ 113 Gfx *geo_exec_flying_carpet_timer_update(s32 callContext, UNUSED struct GraphNode *node, 114 UNUSED f32 mtx[4][4]) { 115 if (callContext != GEO_CONTEXT_RENDER) { 116 sFlyingCarpetRippleTimer = 0; 117 sPrevAreaTimer = gAreaUpdateCounter - 1; 118 sCurAreaTimer = gAreaUpdateCounter; 119 gFlyingCarpetState = FLYING_CARPET_IDLE; 120 } else { 121 sPrevAreaTimer = sCurAreaTimer; 122 sCurAreaTimer = gAreaUpdateCounter; 123 if (sPrevAreaTimer != sCurAreaTimer) { 124 sFlyingCarpetRippleTimer += 0x400; 125 } 126 } 127 128 return NULL; 129 } 130 131 /** 132 * Create a display list for a flying carpet with dynamic ripples. 133 */ 134 Gfx *geo_exec_flying_carpet_create(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) { 135 s16 n, row, col, x, y, z, tx, ty; 136 Vtx *verts; 137 struct GraphNodeGenerated *generatedNode = (struct GraphNodeGenerated *) node; 138 139 s16 *sp64 = segmented_to_virtual(&flying_carpet_static_vertex_data); 140 Gfx *displayList = NULL; 141 Gfx *displayListHead = NULL; 142 struct Object *curGraphNodeObject; 143 144 if (callContext == GEO_CONTEXT_RENDER) { 145 verts = alloc_display_list(NUM_FLYING_CARPET_VERTICES * sizeof(*verts)); 146 displayList = alloc_display_list(7 * sizeof(*displayList)); 147 displayListHead = displayList; 148 149 if (verts == NULL || displayList == NULL) { 150 return NULL; 151 } 152 153 generatedNode->fnNode.node.flags = (generatedNode->fnNode.node.flags & 0xFF) | 0x100; 154 155 for (n = 0; n <= 20; n++) { 156 row = n / 3; 157 col = n % 3; 158 159 x = sp64[n * 4 + 0]; 160 y = round_float(sins(sFlyingCarpetRippleTimer + (row << 12) + (col << 14)) * 20.0); 161 z = sp64[n * 4 + 1]; 162 tx = sp64[n * 4 + 2]; 163 ty = sp64[n * 4 + 3]; 164 165 make_vertex(verts, n, x, y, z, tx, ty, 0, 127, 0, 255); 166 } 167 168 gSPDisplayList(displayListHead++, dl_flying_carpet_begin); 169 170 // The forward half. 171 gSPVertex(displayListHead++, verts, 12, 0); 172 gSPDisplayList(displayListHead++, dl_flying_carpet_model_half); 173 174 // The back half. 175 gSPVertex(displayListHead++, verts + 9, 12, 0); 176 gSPDisplayList(displayListHead++, dl_flying_carpet_model_half); 177 178 gSPDisplayList(displayListHead++, dl_flying_carpet_end); 179 gSPEndDisplayList(displayListHead); 180 181 curGraphNodeObject = (struct Object *) gCurGraphNodeObject; 182 if (gMarioObject->platform == curGraphNodeObject) { 183 gFlyingCarpetState = FLYING_CARPET_MOVING_WITH_MARIO; 184 } else if (curGraphNodeObject->oForwardVel != 0.0) { 185 gFlyingCarpetState = FLYING_CARPET_MOVING_WITHOUT_MARIO; 186 } else { 187 gFlyingCarpetState = FLYING_CARPET_IDLE; 188 } 189 } 190 191 return displayList; 192 } 193 194 /** 195 * Create a display list for the end screen with Peach's delicious cake. 196 */ 197 Gfx *geo_exec_cake_end_screen(s32 callContext, struct GraphNode *node, UNUSED f32 mtx[4][4]) { 198 struct GraphNodeGenerated *generatedNode = (struct GraphNodeGenerated *) node; 199 Gfx *displayList = NULL; 200 Gfx *displayListHead = NULL; 201 202 if (callContext == GEO_CONTEXT_RENDER) { 203 displayList = alloc_display_list(3 * sizeof(*displayList)); 204 displayListHead = displayList; 205 206 generatedNode->fnNode.node.flags = (generatedNode->fnNode.node.flags & 0xFF) | 0x100; 207 #ifdef VERSION_EU 208 gSPDisplayList(displayListHead++, dl_cake_end_screen); 209 #else 210 gSPDisplayList(displayListHead++, dl_proj_mtx_fullscreen); 211 #endif 212 #ifdef VERSION_EU 213 switch (eu_get_language()) { 214 case LANGUAGE_ENGLISH: 215 gSPDisplayList(displayListHead++, dl_cake_end_screen_eu_070296F8); 216 break; 217 case LANGUAGE_FRENCH: 218 gSPDisplayList(displayListHead++, dl_cake_end_screen_eu_07029768); 219 break; 220 case LANGUAGE_GERMAN: 221 gSPDisplayList(displayListHead++, dl_cake_end_screen_eu_070297D8); 222 break; 223 } 224 #else 225 gSPDisplayList(displayListHead++, dl_cake_end_screen); 226 #endif 227 gSPEndDisplayList(displayListHead); 228 } 229 230 return displayList; 231 }