Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/envfx_snow.c
7858 views
1
#include <ultra64.h>
2
3
#include "sm64.h"
4
#include "dialog_ids.h"
5
#include "game_init.h"
6
#include "memory.h"
7
#include "ingame_menu.h"
8
#include "envfx_snow.h"
9
#include "envfx_bubbles.h"
10
#include "engine/surface_collision.h"
11
#include "engine/math_util.h"
12
#include "engine/behavior_script.h"
13
#include "audio/external.h"
14
#include "obj_behaviors.h"
15
16
/**
17
* This file contains the function that handles 'environment effects',
18
* which are particle effects related to the level type that, unlike
19
* object-based particle effects, are rendered more efficiently by manually
20
* generating display lists instead of drawing each particle separately.
21
* This file implements snow effects, while in 'envfx_bubbles.c' the
22
* implementation for flowers (unused), lava bubbles and jet stream bubbles
23
* can be found.
24
* The main entry point for envfx is at the bottom of this file, which is
25
* called from geo_envfx_main in level_geo.c
26
*/
27
28
// Might be duplicate
29
struct SnowFlakeVertex {
30
s16 x;
31
s16 y;
32
s16 z;
33
};
34
35
struct EnvFxParticle *gEnvFxBuffer;
36
Vec3i gSnowCylinderLastPos;
37
s16 gSnowParticleCount;
38
s16 gSnowParticleMaxCount;
39
40
/* DATA */
41
s8 gEnvFxMode = 0;
42
UNUSED s32 D_80330644 = 0;
43
44
/// Template for a snow particle triangle
45
Vtx gSnowTempVtx[3] = { { { { -5, 5, 0 }, 0, { 0, 0 }, { 0x7F, 0x7F, 0x7F, 0xFF } } },
46
{ { { -5, -5, 0 }, 0, { 0, 960 }, { 0x7F, 0x7F, 0x7F, 0xFF } } },
47
{ { { 5, 5, 0 }, 0, { 960, 0 }, { 0x7F, 0x7F, 0x7F, 0xFF } } } };
48
49
// Change these to make snowflakes smaller or bigger
50
struct SnowFlakeVertex gSnowFlakeVertex1 = { -5, 5, 0 };
51
struct SnowFlakeVertex gSnowFlakeVertex2 = { -5, -5, 0 };
52
struct SnowFlakeVertex gSnowFlakeVertex3 = { 5, 5, 0 };
53
54
extern void *tiny_bubble_dl_0B006AB0;
55
extern void *tiny_bubble_dl_0B006A50;
56
extern void *tiny_bubble_dl_0B006CD8;
57
58
static struct {
59
Gfx *pos;
60
Vtx vertices[15];
61
} sPrevSnowVertices[140 / 5];
62
static s16 sPrevSnowParticleCount;
63
static u32 sPrevSnowTimestamp;
64
65
void patch_interpolated_snow_particles(void) {
66
int i;
67
68
if (gGlobalTimer != sPrevSnowTimestamp + 1) {
69
return;
70
}
71
72
for (i = 0; i < sPrevSnowParticleCount; i += 5) {
73
gSPVertex(sPrevSnowVertices[i / 5].pos,
74
VIRTUAL_TO_PHYSICAL(sPrevSnowVertices[i / 5].vertices), 15, 0);
75
}
76
}
77
78
/**
79
* Initialize snow particles by allocating a buffer for storing their state
80
* and setting a start amount.
81
*/
82
s32 envfx_init_snow(s32 mode) {
83
switch (mode) {
84
case ENVFX_MODE_NONE:
85
return 0;
86
87
case ENVFX_SNOW_NORMAL:
88
gSnowParticleMaxCount = 140;
89
gSnowParticleCount = 5;
90
break;
91
92
case ENVFX_SNOW_WATER:
93
gSnowParticleMaxCount = 30;
94
gSnowParticleCount = 30;
95
break;
96
97
case ENVFX_SNOW_BLIZZARD:
98
gSnowParticleMaxCount = 140;
99
gSnowParticleCount = 140;
100
break;
101
}
102
103
gEnvFxBuffer = mem_pool_alloc(gEffectsMemoryPool, gSnowParticleMaxCount * sizeof(struct EnvFxParticle));
104
if (!gEnvFxBuffer) {
105
return 0;
106
}
107
108
bzero(gEnvFxBuffer, gSnowParticleMaxCount * sizeof(struct EnvFxParticle));
109
110
gEnvFxMode = mode;
111
return 1;
112
}
113
114
/**
115
* Update the amount of snow particles on screen.
116
* Normal snow starts with few flakes and slowly increases to the maximum.
117
* For water snow, this is dependent on how deep underwater you are.
118
* Blizzard snows starts at the maximum amount and doesn't change.
119
*/
120
void envfx_update_snowflake_count(s32 mode, Vec3s marioPos) {
121
s32 timer = gGlobalTimer;
122
f32 waterLevel;
123
switch (mode) {
124
case ENVFX_SNOW_NORMAL:
125
if (gSnowParticleMaxCount > gSnowParticleCount) {
126
if ((timer & 0x3F) == 0) {
127
gSnowParticleCount += 5;
128
}
129
}
130
break;
131
132
case ENVFX_SNOW_WATER:
133
waterLevel = find_water_level(marioPos[0], marioPos[2]);
134
135
gSnowParticleCount =
136
(((s32)((waterLevel - 400.f - (f32) marioPos[1]) * 1.0e-3) << 0x10) >> 0x10) * 5;
137
138
if (gSnowParticleCount < 0) {
139
gSnowParticleCount = 0;
140
}
141
142
if (gSnowParticleCount > gSnowParticleMaxCount) {
143
gSnowParticleCount = gSnowParticleMaxCount;
144
}
145
146
break;
147
148
case ENVFX_SNOW_BLIZZARD:
149
break;
150
}
151
}
152
153
/**
154
* Deallocate the buffer storing snow particles and set the environment effect
155
* to none.
156
*/
157
void envfx_cleanup_snow(void *snowParticleArray) {
158
if (gEnvFxMode) {
159
if (snowParticleArray) {
160
mem_pool_free(gEffectsMemoryPool, snowParticleArray);
161
}
162
gEnvFxMode = ENVFX_MODE_NONE;
163
}
164
}
165
166
/**
167
* Given two points, return the vector from one to the other represented
168
* as Euler angles and a length
169
*/
170
void orbit_from_positions(Vec3s from, Vec3s to, s16 *radius, s16 *pitch, s16 *yaw) {
171
f32 dx = to[0] - from[0];
172
f32 dy = to[1] - from[1];
173
f32 dz = to[2] - from[2];
174
175
*radius = (s16) sqrtf(dx * dx + dy * dy + dz * dz);
176
*pitch = atan2s(sqrtf(dx * dx + dz * dz), dy);
177
*yaw = atan2s(dz, dx);
178
}
179
180
/**
181
* Calculate the 'result' vector as the position of the 'origin' vector
182
* with a vector added represented by radius, pitch and yaw.
183
*/
184
void pos_from_orbit(Vec3s origin, Vec3s result, s16 radius, s16 pitch, s16 yaw) {
185
result[0] = origin[0] + radius * coss(pitch) * sins(yaw);
186
result[1] = origin[1] + radius * sins(pitch);
187
result[2] = origin[2] + radius * coss(pitch) * coss(yaw);
188
}
189
190
/**
191
* Check whether the snowflake with the given index is inside view, where
192
* 'view' is a cylinder of radius 300 and height 400 centered at the input
193
* x, y and z.
194
*/
195
s32 envfx_is_snowflake_alive(s32 index, s32 snowCylinderX, s32 snowCylinderY, s32 snowCylinderZ) {
196
s32 x = (gEnvFxBuffer + index)->xPos;
197
s32 y = (gEnvFxBuffer + index)->yPos;
198
s32 z = (gEnvFxBuffer + index)->zPos;
199
200
if (sqr(x - snowCylinderX) + sqr(z - snowCylinderZ) > sqr(300)) {
201
return 0;
202
}
203
204
if ((y < snowCylinderY - 201) || (snowCylinderY + 201 < y)) {
205
return 0;
206
}
207
208
return 1;
209
}
210
211
/**
212
* Update the position of each snowflake. Snowflakes wiggle by having a
213
* random value added to their position each frame. If snowflakes get out
214
* of view (where view = a small cylinder in front of the camera) their
215
* position is reset to somewhere in view.
216
* Since the cylinder of snow is so close to the camera, snow flakes would
217
* move out of view very quickly when the camera moves. To mitigate this,
218
* a portion of the difference between the previous and current snowCylinder
219
* position is added to snowflakes to keep them in view for longer. That's
220
* why the snow looks a bit off in 3d, it's a lot closer than you'd think
221
* but appears to be further by means of hacky position updates. This might
222
* have been done because larger, further away snowflakes are occluded easily
223
* by level geometry, wasting many particles.
224
*/
225
void envfx_update_snow_normal(s32 snowCylinderX, s32 snowCylinderY, s32 snowCylinderZ) {
226
s32 i;
227
s32 deltaX = snowCylinderX - gSnowCylinderLastPos[0];
228
s32 deltaY = snowCylinderY - gSnowCylinderLastPos[1];
229
s32 deltaZ = snowCylinderZ - gSnowCylinderLastPos[2];
230
231
for (i = 0; i < gSnowParticleCount; i++) {
232
(gEnvFxBuffer + i)->isAlive =
233
envfx_is_snowflake_alive(i, snowCylinderX, snowCylinderY, snowCylinderZ);
234
if ((gEnvFxBuffer + i)->isAlive == 0) {
235
(gEnvFxBuffer + i)->xPos =
236
400.0f * random_float() - 200.0f + snowCylinderX + (s16)(deltaX * 2);
237
(gEnvFxBuffer + i)->zPos =
238
400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2);
239
(gEnvFxBuffer + i)->yPos = 200.0f * random_float() + snowCylinderY;
240
(gEnvFxBuffer + i)->isAlive = 1;
241
(gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer;
242
} else {
243
(gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2);
244
(gEnvFxBuffer + i)->yPos -= 2 -(s16)(deltaY * 0.8);
245
(gEnvFxBuffer + i)->zPos += random_float() * 2 - 1.0f + (s16)(deltaZ / 1.2);
246
}
247
}
248
249
gSnowCylinderLastPos[0] = snowCylinderX;
250
gSnowCylinderLastPos[1] = snowCylinderY;
251
gSnowCylinderLastPos[2] = snowCylinderZ;
252
}
253
254
/**
255
* Unused function. Basically a copy-paste of envfx_update_snow_normal,
256
* but an extra 20 units is added to each snowflake x and snowflakes can
257
* respawn in y-range [-200, 200] instead of [0, 200] relative to snowCylinderY
258
* They also fall a bit faster (with vertical speed -5 instead of -2).
259
*/
260
void envfx_update_snow_blizzard(s32 snowCylinderX, s32 snowCylinderY, s32 snowCylinderZ) {
261
s32 i;
262
s32 deltaX = snowCylinderX - gSnowCylinderLastPos[0];
263
s32 deltaY = snowCylinderY - gSnowCylinderLastPos[1];
264
s32 deltaZ = snowCylinderZ - gSnowCylinderLastPos[2];
265
266
for (i = 0; i < gSnowParticleCount; i++) {
267
(gEnvFxBuffer + i)->isAlive =
268
envfx_is_snowflake_alive(i, snowCylinderX, snowCylinderY, snowCylinderZ);
269
if ((gEnvFxBuffer + i)->isAlive == 0) {
270
(gEnvFxBuffer + i)->xPos =
271
400.0f * random_float() - 200.0f + snowCylinderX + (s16)(deltaX * 2);
272
(gEnvFxBuffer + i)->zPos =
273
400.0f * random_float() - 200.0f + snowCylinderZ + (s16)(deltaZ * 2);
274
(gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY;
275
(gEnvFxBuffer + i)->isAlive = 1;
276
(gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer;
277
} else {
278
(gEnvFxBuffer + i)->xPos += random_float() * 2 - 1.0f + (s16)(deltaX / 1.2) + 20.0f;
279
(gEnvFxBuffer + i)->yPos -= 5 -(s16)(deltaY * 0.8);
280
(gEnvFxBuffer + i)->zPos += random_float() * 2 - 1.0f + (s16)(deltaZ / 1.2);
281
}
282
}
283
284
gSnowCylinderLastPos[0] = snowCylinderX;
285
gSnowCylinderLastPos[1] = snowCylinderY;
286
gSnowCylinderLastPos[2] = snowCylinderZ;
287
}
288
289
/*! Unused function. Checks whether a position is laterally within 3000 units
290
* to the point (x: 3380, z: -520). Considering there is an unused blizzard
291
* snow mode, this could have been used to check whether Mario is in a
292
* 'blizzard area'. In Cool Cool Mountain and Snowman's Land the area lies
293
* near the starting point and doesn't seem meaningful. Notably, the point is
294
* close to the entrance of SL, so maybe there were plans for an extra hint to
295
* find it. The radius of 3000 units is quite large for that though, covering
296
* more than half of the mirror room.
297
*/
298
UNUSED static s32 is_in_mystery_snow_area(s32 x, UNUSED s32 y, s32 z) {
299
if (sqr(x - 3380) + sqr(z + 520) < sqr(3000)) {
300
return 1;
301
}
302
return 0;
303
}
304
305
/**
306
* Update the position of underwater snow particles. Since they are stationary,
307
* they merely jump back into view when they are out of view.
308
*/
309
void envfx_update_snow_water(s32 snowCylinderX, s32 snowCylinderY, s32 snowCylinderZ) {
310
s32 i;
311
312
for (i = 0; i < gSnowParticleCount; i++) {
313
(gEnvFxBuffer + i)->isAlive =
314
envfx_is_snowflake_alive(i, snowCylinderX, snowCylinderY, snowCylinderZ);
315
if ((gEnvFxBuffer + i)->isAlive == 0) {
316
(gEnvFxBuffer + i)->xPos = 400.0f * random_float() - 200.0f + snowCylinderX;
317
(gEnvFxBuffer + i)->zPos = 400.0f * random_float() - 200.0f + snowCylinderZ;
318
(gEnvFxBuffer + i)->yPos = 400.0f * random_float() - 200.0f + snowCylinderY;
319
(gEnvFxBuffer + i)->isAlive = 1;
320
(gEnvFxBuffer + i)->spawnTimestamp = gGlobalTimer;
321
}
322
}
323
}
324
325
/**
326
* Rotates the input vertices according to the give pitch and yaw. This
327
* is needed for billboarding of particles.
328
*/
329
void rotate_triangle_vertices(Vec3s vertex1, Vec3s vertex2, Vec3s vertex3, s16 pitch, s16 yaw) {
330
f32 cosPitch = coss(pitch);
331
f32 sinPitch = sins(pitch);
332
f32 cosMYaw = coss(-yaw);
333
f32 sinMYaw = sins(-yaw);
334
335
Vec3f v1, v2, v3;
336
337
v1[0] = vertex1[0];
338
v1[1] = vertex1[1];
339
v1[2] = vertex1[2];
340
341
v2[0] = vertex2[0];
342
v2[1] = vertex2[1];
343
v2[2] = vertex2[2];
344
345
v3[0] = vertex3[0];
346
v3[1] = vertex3[1];
347
v3[2] = vertex3[2];
348
349
vertex1[0] = v1[0] * cosMYaw + v1[1] * (sinPitch * sinMYaw) + v1[2] * (-sinMYaw * cosPitch);
350
vertex1[1] = v1[1] * cosPitch + v1[2] * sinPitch;
351
vertex1[2] = v1[0] * sinMYaw + v1[1] * (-sinPitch * cosMYaw) + v1[2] * (cosPitch * cosMYaw);
352
353
vertex2[0] = v2[0] * cosMYaw + v2[1] * (sinPitch * sinMYaw) + v2[2] * (-sinMYaw * cosPitch);
354
vertex2[1] = v2[1] * cosPitch + v2[2] * sinPitch;
355
vertex2[2] = v2[0] * sinMYaw + v2[1] * (-sinPitch * cosMYaw) + v2[2] * (cosPitch * cosMYaw);
356
357
vertex3[0] = v3[0] * cosMYaw + v3[1] * (sinPitch * sinMYaw) + v3[2] * (-sinMYaw * cosPitch);
358
vertex3[1] = v3[1] * cosPitch + v3[2] * sinPitch;
359
vertex3[2] = v3[0] * sinMYaw + v3[1] * (-sinPitch * cosMYaw) + v3[2] * (cosPitch * cosMYaw);
360
}
361
362
/**
363
* Append 15 vertices to 'gfx', which is enough for 5 snowflakes starting at
364
* 'index' in the buffer. The 3 input vertices represent the rotated triangle
365
* around (0,0,0) that will be translated to snowflake positions to draw the
366
* snowflake image.
367
*/
368
void append_snowflake_vertex_buffer(Gfx *gfx, s32 index, Vec3s vertex1, Vec3s vertex2, Vec3s vertex3) {
369
s32 i = 0;
370
Vtx *vertBuf = (Vtx *) alloc_display_list(15 * sizeof(Vtx));
371
Vtx *vertBufInterpolated = (Vtx *) alloc_display_list(15 * sizeof(Vtx));
372
Vtx *v;
373
#ifdef VERSION_EU
374
Vtx *p;
375
#endif
376
377
if (vertBuf == NULL) {
378
return;
379
}
380
381
for (i = 0; i < 15; i += 3) {
382
vertBuf[i] = gSnowTempVtx[0];
383
(vertBuf + i)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex1[0];
384
(vertBuf + i)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex1[1];
385
(vertBuf + i)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex1[2];
386
387
vertBuf[i + 1] = gSnowTempVtx[1];
388
(vertBuf + i + 1)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex2[0];
389
(vertBuf + i + 1)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex2[1];
390
(vertBuf + i + 1)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex2[2];
391
392
vertBuf[i + 2] = gSnowTempVtx[2];
393
(vertBuf + i + 2)->v.ob[0] = (gEnvFxBuffer + (index + i / 3))->xPos + vertex3[0];
394
(vertBuf + i + 2)->v.ob[1] = (gEnvFxBuffer + (index + i / 3))->yPos + vertex3[1];
395
(vertBuf + i + 2)->v.ob[2] = (gEnvFxBuffer + (index + i / 3))->zPos + vertex3[2];
396
}
397
398
for (i = 0; i < 15; i++) {
399
v = &sPrevSnowVertices[index / 5].vertices[i];
400
vertBufInterpolated[i] = gSnowTempVtx[i % 3];
401
if (index < sPrevSnowParticleCount && gGlobalTimer == sPrevSnowTimestamp + 1 &&
402
gGlobalTimer != gEnvFxBuffer[index + i / 3].spawnTimestamp) {
403
vertBufInterpolated[i].v.ob[0] = (v->v.ob[0] + vertBuf[i].v.ob[0]) / 2;
404
vertBufInterpolated[i].v.ob[1] = (v->v.ob[1] + vertBuf[i].v.ob[1]) / 2;
405
vertBufInterpolated[i].v.ob[2] = (v->v.ob[2] + vertBuf[i].v.ob[2]) / 2;
406
} else {
407
vertBufInterpolated[i].v.ob[0] = vertBuf[i].v.ob[0];
408
vertBufInterpolated[i].v.ob[1] = vertBuf[i].v.ob[1];
409
vertBufInterpolated[i].v.ob[2] = vertBuf[i].v.ob[2];
410
}
411
*v = vertBuf[i];
412
}
413
sPrevSnowVertices[index / 5].pos = gfx;
414
gSPVertex(gfx, VIRTUAL_TO_PHYSICAL(vertBufInterpolated), 15, 0);
415
}
416
417
/**
418
* Updates positions of snow particles and returns a pointer to a display list
419
* drawing all snowflakes.
420
*/
421
Gfx *envfx_update_snow(s32 snowMode, Vec3s marioPos, Vec3s camFrom, Vec3s camTo) {
422
s32 i;
423
s16 radius, pitch, yaw;
424
Vec3s snowCylinderPos;
425
struct SnowFlakeVertex vertex1, vertex2, vertex3;
426
Gfx *gfxStart;
427
Gfx *gfx;
428
429
vertex1 = gSnowFlakeVertex1;
430
vertex2 = gSnowFlakeVertex2;
431
vertex3 = gSnowFlakeVertex3;
432
433
gfxStart = (Gfx *) alloc_display_list((gSnowParticleCount * 6 + 3) * sizeof(Gfx));
434
gfx = gfxStart;
435
436
if (gfxStart == NULL) {
437
return NULL;
438
}
439
440
envfx_update_snowflake_count(snowMode, marioPos);
441
442
// Note: to and from are inverted here, so the resulting vector goes towards the camera
443
orbit_from_positions(camTo, camFrom, &radius, &pitch, &yaw);
444
445
switch (snowMode) {
446
case ENVFX_SNOW_NORMAL:
447
// ensure the snow cylinder is no further than 250 units in front
448
// of the camera, and no closer than 1 unit.
449
if (radius > 250) {
450
radius -= 250;
451
} else {
452
radius = 1;
453
}
454
455
pos_from_orbit(camTo, snowCylinderPos, radius, pitch, yaw);
456
envfx_update_snow_normal(snowCylinderPos[0], snowCylinderPos[1], snowCylinderPos[2]);
457
break;
458
459
case ENVFX_SNOW_WATER:
460
if (radius > 500) {
461
radius -= 500;
462
} else {
463
radius = 1;
464
}
465
466
pos_from_orbit(camTo, snowCylinderPos, radius, pitch, yaw);
467
envfx_update_snow_water(snowCylinderPos[0], snowCylinderPos[1], snowCylinderPos[2]);
468
break;
469
case ENVFX_SNOW_BLIZZARD:
470
if (radius > 250) {
471
radius -= 250;
472
} else {
473
radius = 1;
474
}
475
476
pos_from_orbit(camTo, snowCylinderPos, radius, pitch, yaw);
477
envfx_update_snow_blizzard(snowCylinderPos[0], snowCylinderPos[1], snowCylinderPos[2]);
478
break;
479
}
480
481
rotate_triangle_vertices((s16 *) &vertex1, (s16 *) &vertex2, (s16 *) &vertex3, pitch, yaw);
482
483
if (snowMode == ENVFX_SNOW_NORMAL || snowMode == ENVFX_SNOW_BLIZZARD) {
484
gSPDisplayList(gfx++, &tiny_bubble_dl_0B006A50); // snowflake with gray edge
485
} else if (snowMode == ENVFX_SNOW_WATER) {
486
gSPDisplayList(gfx++, &tiny_bubble_dl_0B006CD8); // snowflake with blue edge
487
}
488
489
for (i = 0; i < gSnowParticleCount; i += 5) {
490
append_snowflake_vertex_buffer(gfx++, i, (s16 *) &vertex1, (s16 *) &vertex2, (s16 *) &vertex3);
491
492
gSP1Triangle(gfx++, 0, 1, 2, 0);
493
gSP1Triangle(gfx++, 3, 4, 5, 0);
494
gSP1Triangle(gfx++, 6, 7, 8, 0);
495
gSP1Triangle(gfx++, 9, 10, 11, 0);
496
gSP1Triangle(gfx++, 12, 13, 14, 0);
497
}
498
sPrevSnowParticleCount = gSnowParticleCount;
499
sPrevSnowTimestamp = gGlobalTimer;
500
501
gSPDisplayList(gfx++, &tiny_bubble_dl_0B006AB0) gSPEndDisplayList(gfx++);
502
503
return gfxStart;
504
}
505
506
/**
507
* Updates the environment effects (snow, flowers, bubbles)
508
* and returns a display list drawing them.
509
*/
510
Gfx *envfx_update_particles(s32 mode, Vec3s marioPos, Vec3s camTo, Vec3s camFrom) {
511
Gfx *gfx;
512
513
if (get_dialog_id() != DIALOG_NONE) {
514
return NULL;
515
}
516
517
if (gEnvFxMode != 0 && mode != gEnvFxMode) {
518
mode = 0;
519
}
520
521
if (mode >= ENVFX_BUBBLE_START) {
522
gfx = envfx_update_bubbles(mode, marioPos, camTo, camFrom);
523
return gfx;
524
}
525
526
if (gEnvFxMode == 0 && envfx_init_snow(mode) == 0) {
527
return NULL;
528
}
529
530
switch (mode) {
531
case ENVFX_MODE_NONE:
532
envfx_cleanup_snow(gEnvFxBuffer);
533
return NULL;
534
535
case ENVFX_SNOW_NORMAL:
536
gfx = envfx_update_snow(1, marioPos, camFrom, camTo);
537
break;
538
539
case ENVFX_SNOW_WATER:
540
gfx = envfx_update_snow(2, marioPos, camFrom, camTo);
541
break;
542
543
case ENVFX_SNOW_BLIZZARD:
544
gfx = envfx_update_snow(3, marioPos, camFrom, camTo);
545
break;
546
547
default:
548
return NULL;
549
}
550
551
return gfx;
552
}
553
554