envfx_bubbles.c (20073B)
1 #include <ultra64.h> 2 3 #include "sm64.h" 4 #include "game_init.h" 5 #include "memory.h" 6 #include "envfx_snow.h" 7 #include "envfx_bubbles.h" 8 #include "engine/surface_collision.h" 9 #include "engine/math_util.h" 10 #include "engine/behavior_script.h" 11 #include "audio/external.h" 12 #include "textures.h" 13 14 /** 15 * This file implements environment effects that are not snow: 16 * Flowers (unused), lava bubbles and jet stream/whirlpool bubbles. 17 * Refer to 'envfx_snow.c' for more info about environment effects. 18 * Note that the term 'bubbles' is used as a collective name for 19 * effects in this file even though flowers aren't bubbles. For the 20 * sake of concise naming, flowers fall under bubbles. 21 */ 22 23 s16 gEnvFxBubbleConfig[10]; 24 static Gfx *sGfxCursor; // points to end of display list for bubble particles 25 static s32 sBubbleParticleCount; 26 static s32 sBubbleParticleMaxCount; 27 28 UNUSED s32 D_80330690 = 0; 29 UNUSED s32 D_80330694 = 0; 30 31 /// Template for a bubble particle triangle 32 Vtx_t gBubbleTempVtx[3] = { 33 { { 0, 0, 0 }, 0, { 1544, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, 34 { { 0, 0, 0 }, 0, { 522, -568 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, 35 { { 0, 0, 0 }, 0, { -498, 964 }, { 0xFF, 0xFF, 0xFF, 0xFF } }, 36 }; 37 38 /** 39 * Check whether the particle with the given index is 40 * laterally within distance of point (x, z). Used to 41 * kill flower and bubble particles. 42 */ 43 s32 particle_is_laterally_close(s32 index, s32 x, s32 z, s32 distance) { 44 s32 xPos = (gEnvFxBuffer + index)->xPos; 45 s32 zPos = (gEnvFxBuffer + index)->zPos; 46 47 if (sqr(xPos - x) + sqr(zPos - z) > sqr(distance)) { 48 return FALSE; 49 } 50 51 return TRUE; 52 } 53 54 /** 55 * Generate a uniform random number in range [-2000, -1000[ or [1000, 2000[ 56 * Used to position flower particles 57 */ 58 s32 random_flower_offset(void) { 59 s32 result = random_float() * 2000.0f - 1000.0f; 60 if (result < 0) { 61 result -= 1000; 62 } else { 63 result += 1000; 64 } 65 66 return result; 67 } 68 69 /** 70 * Update flower particles. Flowers are scattered randomly in front of the 71 * camera, and can land on any ground 72 */ 73 void envfx_update_flower(Vec3s centerPos) { 74 s32 i; 75 struct FloorGeometry *floorGeo; // unused 76 s32 globalTimer = gGlobalTimer; 77 78 s16 centerX = centerPos[0]; 79 UNUSED s16 centerY = centerPos[1]; 80 s16 centerZ = centerPos[2]; 81 82 for (i = 0; i < sBubbleParticleMaxCount; i++) { 83 (gEnvFxBuffer + i)->isAlive = particle_is_laterally_close(i, centerX, centerZ, 3000); 84 if (!(gEnvFxBuffer + i)->isAlive) { 85 (gEnvFxBuffer + i)->xPos = random_flower_offset() + centerX; 86 (gEnvFxBuffer + i)->zPos = random_flower_offset() + centerZ; 87 (gEnvFxBuffer + i)->yPos = find_floor_height_and_data((gEnvFxBuffer + i)->xPos, 10000.0f, 88 (gEnvFxBuffer + i)->zPos, &floorGeo); 89 (gEnvFxBuffer + i)->isAlive = TRUE; 90 (gEnvFxBuffer + i)->animFrame = random_float() * 5.0f; 91 } else if (!(globalTimer & 3)) { 92 (gEnvFxBuffer + i)->animFrame += 1; 93 if ((gEnvFxBuffer + i)->animFrame > 5) { 94 (gEnvFxBuffer + i)->animFrame = 0; 95 } 96 } 97 } 98 } 99 100 /** 101 * Update the position of a lava bubble to be somewhere around centerPos 102 * Uses find_floor to find the height of lava, if no floor or a non-lava 103 * floor is found the bubble y is set to -10000, which is why you can see 104 * occasional lava bubbles far below the course in Lethal Lava Land. 105 * In the second Bowser fight arena, the visual lava is above the lava 106 * floor so lava-bubbles are not normally visible, only if you bring the 107 * camera below the lava plane. 108 */ 109 void envfx_set_lava_bubble_position(s32 index, Vec3s centerPos) { 110 struct Surface *surface; 111 s16 floorY; 112 113 s16 centerX = centerPos[0]; 114 s16 centerY = centerPos[1]; 115 s16 centerZ = centerPos[2]; 116 117 (gEnvFxBuffer + index)->xPos = random_float() * 6000.0f - 3000.0f + centerX; 118 (gEnvFxBuffer + index)->zPos = random_float() * 6000.0f - 3000.0f + centerZ; 119 120 if ((gEnvFxBuffer + index)->xPos > 8000) { 121 (gEnvFxBuffer + index)->xPos = 16000 - (gEnvFxBuffer + index)->xPos; 122 } 123 if ((gEnvFxBuffer + index)->xPos < -8000) { 124 (gEnvFxBuffer + index)->xPos = -16000 - (gEnvFxBuffer + index)->xPos; 125 } 126 127 if ((gEnvFxBuffer + index)->zPos > 8000) { 128 (gEnvFxBuffer + index)->zPos = 16000 - (gEnvFxBuffer + index)->zPos; 129 } 130 if ((gEnvFxBuffer + index)->zPos < -8000) { 131 (gEnvFxBuffer + index)->zPos = -16000 - (gEnvFxBuffer + index)->zPos; 132 } 133 134 floorY = 135 find_floor((gEnvFxBuffer + index)->xPos, centerY + 500, (gEnvFxBuffer + index)->zPos, &surface); 136 if (surface == NULL) { 137 (gEnvFxBuffer + index)->yPos = FLOOR_LOWER_LIMIT_MISC; 138 return; 139 } 140 141 if (surface->type == SURFACE_BURNING) { 142 (gEnvFxBuffer + index)->yPos = floorY; 143 } else { 144 (gEnvFxBuffer + index)->yPos = FLOOR_LOWER_LIMIT_MISC; 145 } 146 } 147 148 /** 149 * Update lava bubble animation and give the bubble a new position if the 150 * animation is over. 151 */ 152 void envfx_update_lava(Vec3s centerPos) { 153 s32 i; 154 s32 globalTimer = gGlobalTimer; 155 s8 chance; 156 157 UNUSED s16 centerX = centerPos[0]; 158 UNUSED s16 centerY = centerPos[1]; 159 UNUSED s16 centerZ = centerPos[2]; 160 161 for (i = 0; i < sBubbleParticleMaxCount; i++) { 162 if (!(gEnvFxBuffer + i)->isAlive) { 163 envfx_set_lava_bubble_position(i, centerPos); 164 (gEnvFxBuffer + i)->isAlive = TRUE; 165 } else if (!(globalTimer & 1)) { 166 (gEnvFxBuffer + i)->animFrame += 1; 167 if ((gEnvFxBuffer + i)->animFrame > 8) { 168 (gEnvFxBuffer + i)->isAlive = FALSE; 169 (gEnvFxBuffer + i)->animFrame = 0; 170 } 171 } 172 } 173 174 if ((chance = (s32)(random_float() * 16.0f)) == 8) { 175 play_sound(SOUND_GENERAL_QUIET_BUBBLE2, gGlobalSoundSource); 176 } 177 } 178 179 /** 180 * Rotate the input x, y and z around the rotation origin of the whirlpool 181 * according to the pitch and yaw of the whirlpool. 182 */ 183 void envfx_rotate_around_whirlpool(s32 *x, s32 *y, s32 *z) { 184 s32 vecX = *x - gEnvFxBubbleConfig[ENVFX_STATE_DEST_X]; 185 s32 vecY = *y - gEnvFxBubbleConfig[ENVFX_STATE_DEST_Y]; 186 s32 vecZ = *z - gEnvFxBubbleConfig[ENVFX_STATE_DEST_Z]; 187 f32 cosPitch = coss(gEnvFxBubbleConfig[ENVFX_STATE_PITCH]); 188 f32 sinPitch = sins(gEnvFxBubbleConfig[ENVFX_STATE_PITCH]); 189 f32 cosMYaw = coss(-gEnvFxBubbleConfig[ENVFX_STATE_YAW]); 190 f32 sinMYaw = sins(-gEnvFxBubbleConfig[ENVFX_STATE_YAW]); 191 192 f32 rotatedX = vecX * cosMYaw - sinMYaw * cosPitch * vecY - sinPitch * sinMYaw * vecZ; 193 f32 rotatedY = vecX * sinMYaw + cosPitch * cosMYaw * vecY - sinPitch * cosMYaw * vecZ; 194 f32 rotatedZ = vecY * sinPitch + cosPitch * vecZ; 195 196 *x = gEnvFxBubbleConfig[ENVFX_STATE_DEST_X] + (s32) rotatedX; 197 *y = gEnvFxBubbleConfig[ENVFX_STATE_DEST_Y] + (s32) rotatedY; 198 *z = gEnvFxBubbleConfig[ENVFX_STATE_DEST_Z] + (s32) rotatedZ; 199 } 200 201 /** 202 * Check whether a whirlpool bubble is alive. A bubble respawns when it is too 203 * low or close to the center. 204 */ 205 s32 envfx_is_whirlpool_bubble_alive(s32 index) { 206 UNUSED u8 filler[4]; 207 208 if ((gEnvFxBuffer + index)->bubbleY < gEnvFxBubbleConfig[ENVFX_STATE_DEST_Y] - 100) { 209 return FALSE; 210 } 211 212 if ((gEnvFxBuffer + index)->angleAndDist[1] < 10) { 213 return FALSE; 214 } 215 216 return TRUE; 217 } 218 219 /** 220 * Update whirlpool particles. Whirlpool particles start high and far from 221 * the center and get sucked into the sink in a spiraling motion. 222 */ 223 void envfx_update_whirlpool(void) { 224 s32 i; 225 226 for (i = 0; i < sBubbleParticleMaxCount; i++) { 227 (gEnvFxBuffer + i)->isAlive = envfx_is_whirlpool_bubble_alive(i); 228 if (!(gEnvFxBuffer + i)->isAlive) { 229 (gEnvFxBuffer + i)->angleAndDist[1] = random_float() * 1000.0f; 230 (gEnvFxBuffer + i)->angleAndDist[0] = random_float() * 65536.0f; 231 (gEnvFxBuffer + i)->xPos = 232 gEnvFxBubbleConfig[ENVFX_STATE_SRC_X] 233 + sins((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; 234 (gEnvFxBuffer + i)->zPos = 235 gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z] 236 + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; 237 (gEnvFxBuffer + i)->bubbleY = 238 gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 100.0f - 50.0f); 239 (gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY; 240 (gEnvFxBuffer + i)->unusedBubbleVar = 0; 241 (gEnvFxBuffer + i)->isAlive = TRUE; 242 243 envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos, 244 &(gEnvFxBuffer + i)->zPos); 245 } else { 246 (gEnvFxBuffer + i)->angleAndDist[1] -= 40; 247 (gEnvFxBuffer + i)->angleAndDist[0] += 248 (s16)(3000 - (gEnvFxBuffer + i)->angleAndDist[1] * 2) + 0x400; 249 (gEnvFxBuffer + i)->xPos = 250 gEnvFxBubbleConfig[ENVFX_STATE_SRC_X] 251 + sins((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; 252 (gEnvFxBuffer + i)->zPos = 253 gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z] 254 + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; 255 (gEnvFxBuffer + i)->bubbleY -= 40 - ((s16)(gEnvFxBuffer + i)->angleAndDist[1] / 100); 256 (gEnvFxBuffer + i)->yPos = (i + gEnvFxBuffer)->bubbleY; 257 258 envfx_rotate_around_whirlpool(&(gEnvFxBuffer + i)->xPos, &(gEnvFxBuffer + i)->yPos, 259 &(gEnvFxBuffer + i)->zPos); 260 } 261 } 262 } 263 264 /** 265 * Check whether a jet stream bubble should respawn. Happens if it is laterally 266 * 1000 units away from the source or 1500 units above it. 267 */ 268 s32 envfx_is_jestream_bubble_alive(s32 index) { 269 UNUSED u8 filler[4]; 270 271 if (!particle_is_laterally_close(index, gEnvFxBubbleConfig[ENVFX_STATE_SRC_X], 272 gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z], 1000) 273 || gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + 1500 < (gEnvFxBuffer + index)->yPos) { 274 return FALSE; 275 } 276 277 return TRUE; 278 } 279 280 /** 281 * Update the positions of jet stream bubble particles. 282 * They move up and outwards. 283 */ 284 void envfx_update_jetstream(void) { 285 s32 i; 286 287 for (i = 0; i < sBubbleParticleMaxCount; i++) { 288 (gEnvFxBuffer + i)->isAlive = envfx_is_jestream_bubble_alive(i); 289 if (!(gEnvFxBuffer + i)->isAlive) { 290 (gEnvFxBuffer + i)->angleAndDist[1] = random_float() * 300.0f; 291 (gEnvFxBuffer + i)->angleAndDist[0] = random_u16(); 292 (gEnvFxBuffer + i)->xPos = 293 gEnvFxBubbleConfig[ENVFX_STATE_SRC_X] 294 + sins((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; 295 (gEnvFxBuffer + i)->zPos = 296 gEnvFxBubbleConfig[ENVFX_STATE_SRC_Z] 297 + coss((gEnvFxBuffer + i)->angleAndDist[0]) * (gEnvFxBuffer + i)->angleAndDist[1]; 298 (gEnvFxBuffer + i)->yPos = 299 gEnvFxBubbleConfig[ENVFX_STATE_SRC_Y] + (random_float() * 400.0f - 200.0f); 300 } else { 301 (gEnvFxBuffer + i)->angleAndDist[1] += 10; 302 (gEnvFxBuffer + i)->xPos += sins((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f; 303 (gEnvFxBuffer + i)->zPos += coss((gEnvFxBuffer + i)->angleAndDist[0]) * 10.0f; 304 (gEnvFxBuffer + i)->yPos -= ((gEnvFxBuffer + i)->angleAndDist[1] / 30) - 50; 305 } 306 } 307 } 308 309 /** 310 * Initialize bubble (or flower) effect by allocating a buffer to store 311 * the state of each particle and setting the initial and max count. 312 * Analogous to init_snow_particles, but for bubbles. 313 */ 314 s32 envfx_init_bubble(s32 mode) { 315 s32 i; 316 317 switch (mode) { 318 case ENVFX_MODE_NONE: 319 return FALSE; 320 321 case ENVFX_FLOWERS: 322 sBubbleParticleCount = 30; 323 sBubbleParticleMaxCount = 30; 324 break; 325 326 case ENVFX_LAVA_BUBBLES: 327 sBubbleParticleCount = 15; 328 sBubbleParticleMaxCount = 15; 329 break; 330 331 case ENVFX_WHIRLPOOL_BUBBLES: 332 sBubbleParticleCount = 60; 333 break; 334 335 case ENVFX_JETSTREAM_BUBBLES: 336 sBubbleParticleCount = 60; 337 break; 338 } 339 340 gEnvFxBuffer = mem_pool_alloc(gEffectsMemoryPool, 341 sBubbleParticleCount * sizeof(struct EnvFxParticle)); 342 if (gEnvFxBuffer == NULL) { 343 return FALSE; 344 } 345 346 bzero(gEnvFxBuffer, sBubbleParticleCount * sizeof(struct EnvFxParticle)); 347 bzero(gEnvFxBubbleConfig, sizeof(gEnvFxBubbleConfig)); 348 349 switch (mode) { 350 case ENVFX_LAVA_BUBBLES: 351 for (i = 0; i < sBubbleParticleCount; i++) { 352 (gEnvFxBuffer + i)->animFrame = random_float() * 7.0f; 353 } 354 break; 355 } 356 357 gEnvFxMode = mode; 358 return TRUE; 359 } 360 361 /** 362 * Update particles depending on mode. 363 * Also sets the given vertices to the correct shape for each mode, 364 * though they are not being rotated yet. 365 */ 366 void envfx_bubbles_update_switch(s32 mode, Vec3s camTo, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3) { 367 switch (mode) { 368 case ENVFX_FLOWERS: 369 envfx_update_flower(camTo); 370 vertex1[0] = 50; vertex1[1] = 0; vertex1[2] = 0; 371 vertex2[0] = 0; vertex2[1] = 75; vertex2[2] = 0; 372 vertex3[0] = -50; vertex3[1] = 0; vertex3[2] = 0; 373 break; 374 375 case ENVFX_LAVA_BUBBLES: 376 envfx_update_lava(camTo); 377 vertex1[0] = 100; vertex1[1] = 0; vertex1[2] = 0; 378 vertex2[0] = 0; vertex2[1] = 150; vertex2[2] = 0; 379 vertex3[0] = -100; vertex3[1] = 0; vertex3[2] = 0; 380 break; 381 382 case ENVFX_WHIRLPOOL_BUBBLES: 383 envfx_update_whirlpool(); 384 vertex1[0] = 40; vertex1[1] = 0; vertex1[2] = 0; 385 vertex2[0] = 0; vertex2[1] = 60; vertex2[2] = 0; 386 vertex3[0] = -40; vertex3[1] = 0; vertex3[2] = 0; 387 break; 388 389 case ENVFX_JETSTREAM_BUBBLES: 390 envfx_update_jetstream(); 391 vertex1[0] = 40; vertex1[1] = 0; vertex1[2] = 0; 392 vertex2[0] = 0; vertex2[1] = 60; vertex2[2] = 0; 393 vertex3[0] = -40; vertex3[1] = 0; vertex3[2] = 0; 394 break; 395 } 396 } 397 398 /** 399 * Append 15 vertices to 'gfx', which is enough for 5 bubbles starting at 400 * 'index'. The 3 input vertices represent the rotated triangle around (0,0,0) 401 * that will be translated to bubble positions to draw the bubble image 402 */ 403 void append_bubble_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3, 404 Vtx *template) { 405 s32 i = 0; 406 Vtx *vertBuf = alloc_display_list(15 * sizeof(Vtx)); 407 408 if (vertBuf == NULL) { 409 return; 410 } 411 412 for (i = 0; i < 15; i += 3) { 413 vertBuf[i] = template[0]; 414 (vertBuf + i)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex1[0]; 415 (vertBuf + i)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex1[1]; 416 (vertBuf + i)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex1[2]; 417 418 vertBuf[i + 1] = template[1]; 419 (vertBuf + i + 1)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex2[0]; 420 (vertBuf + i + 1)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex2[1]; 421 (vertBuf + i + 1)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex2[2]; 422 423 vertBuf[i + 2] = template[2]; 424 (vertBuf + i + 2)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex3[0]; 425 (vertBuf + i + 2)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex3[1]; 426 (vertBuf + i + 2)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex3[2]; 427 } 428 429 gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBuf), 15, 0); 430 } 431 432 /** 433 * Appends to the enfvx display list a command setting the appropriate texture 434 * for a specific particle. The display list is not passed as parameter but uses 435 * the global sGfxCursor instead. 436 */ 437 void envfx_set_bubble_texture(s32 mode, s16 index) { 438 void **imageArr; 439 s16 frame = (gEnvFxBuffer + index)->animFrame; 440 441 switch (mode) { 442 case ENVFX_FLOWERS: 443 imageArr = segmented_to_virtual(&flower_bubbles_textures_ptr_0B002008); 444 frame = (gEnvFxBuffer + index)->animFrame; 445 break; 446 447 case ENVFX_LAVA_BUBBLES: 448 imageArr = segmented_to_virtual(&lava_bubble_ptr_0B006020); 449 frame = (gEnvFxBuffer + index)->animFrame; 450 break; 451 452 case ENVFX_WHIRLPOOL_BUBBLES: 453 case ENVFX_JETSTREAM_BUBBLES: 454 imageArr = segmented_to_virtual(&bubble_ptr_0B006848); 455 frame = 0; 456 break; 457 } 458 459 gDPSetTextureImage(sGfxCursor++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, *(imageArr + frame)); 460 gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D68); 461 } 462 463 /** 464 * Updates the bubble particle positions, then generates and returns a display 465 * list drawing them. 466 */ 467 Gfx *envfx_update_bubble_particles(s32 mode, UNUSED Vec3s marioPos, Vec3s camFrom, Vec3s camTo) { 468 s32 i; 469 s16 radius, pitch, yaw; 470 471 Vec3s vertex1; 472 Vec3s vertex2; 473 Vec3s vertex3; 474 475 Gfx *gfxStart = alloc_display_list(((sBubbleParticleMaxCount / 5) * 10 + sBubbleParticleMaxCount + 3) 476 * sizeof(Gfx)); 477 if (gfxStart == NULL) { 478 return NULL; 479 } 480 481 sGfxCursor = gfxStart; 482 483 orbit_from_positions(camTo, camFrom, &radius, &pitch, &yaw); 484 envfx_bubbles_update_switch(mode, camTo, vertex1, vertex2, vertex3); 485 rotate_triangle_vertices(vertex1, vertex2, vertex3, pitch, yaw); 486 487 gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006D38); 488 489 for (i = 0; i < sBubbleParticleMaxCount; i += 5) { 490 gDPPipeSync(sGfxCursor++); 491 envfx_set_bubble_texture(mode, i); 492 append_bubble_vertex_buffer(sGfxCursor++, i, vertex1, vertex2, vertex3, (Vtx *) gBubbleTempVtx); 493 gSP1Triangle(sGfxCursor++, 0, 1, 2, 0); 494 gSP1Triangle(sGfxCursor++, 3, 4, 5, 0); 495 gSP1Triangle(sGfxCursor++, 6, 7, 8, 0); 496 gSP1Triangle(sGfxCursor++, 9, 10, 11, 0); 497 gSP1Triangle(sGfxCursor++, 12, 13, 14, 0); 498 } 499 500 gSPDisplayList(sGfxCursor++, &tiny_bubble_dl_0B006AB0); 501 gSPEndDisplayList(sGfxCursor++); 502 503 return gfxStart; 504 } 505 506 /** 507 * Set the maximum particle count from the gEnvFxBubbleConfig variable, 508 * which is set by the whirlpool or jet stream behavior. 509 */ 510 void envfx_set_max_bubble_particles(s32 mode) { 511 switch (mode) { 512 case ENVFX_WHIRLPOOL_BUBBLES: 513 sBubbleParticleMaxCount = gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT]; 514 break; 515 case ENVFX_JETSTREAM_BUBBLES: 516 sBubbleParticleMaxCount = gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT]; 517 break; 518 } 519 } 520 521 /** 522 * Update bubble-like environment effects. Assumes the mode is larger than 10, 523 * lower modes are snow effects which are updated in a different function. 524 * Returns a display list drawing the particles. 525 */ 526 Gfx *envfx_update_bubbles(s32 mode, Vec3s marioPos, Vec3s camTo, Vec3s camFrom) { 527 Gfx *gfx; 528 529 if (gEnvFxMode == ENVFX_MODE_NONE && !envfx_init_bubble(mode)) { 530 return NULL; 531 } 532 533 envfx_set_max_bubble_particles(mode); 534 535 if (sBubbleParticleMaxCount == 0) { 536 return NULL; 537 } 538 539 switch (mode) { 540 case ENVFX_FLOWERS: 541 gfx = envfx_update_bubble_particles(ENVFX_FLOWERS, marioPos, camFrom, camTo); 542 break; 543 544 case ENVFX_LAVA_BUBBLES: 545 gfx = envfx_update_bubble_particles(ENVFX_LAVA_BUBBLES, marioPos, camFrom, camTo); 546 break; 547 548 case ENVFX_WHIRLPOOL_BUBBLES: 549 gfx = envfx_update_bubble_particles(ENVFX_WHIRLPOOL_BUBBLES, marioPos, camFrom, camTo); 550 break; 551 552 case ENVFX_JETSTREAM_BUBBLES: 553 gfx = envfx_update_bubble_particles(ENVFX_JETSTREAM_BUBBLES, marioPos, camFrom, camTo); 554 break; 555 556 default: 557 return NULL; 558 } 559 560 return gfx; 561 }