Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/engine/surface_collision.c
7857 views
1
#include <PR/ultratypes.h>
2
3
#include "sm64.h"
4
#include "game/debug.h"
5
#include "game/level_update.h"
6
#include "game/mario.h"
7
#include "game/object_list_processor.h"
8
#include "surface_collision.h"
9
#include "surface_load.h"
10
#include "game/game_init.h"
11
12
#include "game/settings.h"
13
14
/**************************************************
15
* WALLS *
16
**************************************************/
17
18
/**
19
* Iterate through the list of walls until all walls are checked and
20
* have given their wall push.
21
*/
22
static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode,
23
struct WallCollisionData *data) {
24
register struct Surface *surf;
25
register f32 offset;
26
register f32 radius = data->radius;
27
register f32 x = data->x;
28
register f32 y = data->y + data->offsetY;
29
register f32 z = data->z;
30
register f32 px, pz;
31
register f32 w1, w2, w3;
32
register f32 y1, y2, y3;
33
s32 numCols = 0;
34
35
// Max collision radius = 200
36
if (radius > 200.0f) {
37
radius = 200.0f;
38
}
39
40
// Stay in this loop until out of walls.
41
while (surfaceNode != NULL) {
42
surf = surfaceNode->surface;
43
surfaceNode = surfaceNode->next;
44
45
// Exclude a large number of walls immediately to optimize.
46
if (y < surf->lowerY || y > surf->upperY) {
47
continue;
48
}
49
50
offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset;
51
52
if (offset < -radius || offset > radius) {
53
continue;
54
}
55
56
px = x;
57
pz = z;
58
59
//! (Quantum Tunneling) Due to issues with the vertices walls choose and
60
// the fact they are floating point, certain floating point positions
61
// along the seam of two walls may collide with neither wall or both walls.
62
if (surf->flags & SURFACE_FLAG_X_PROJECTION) {
63
w1 = -surf->vertex1[2]; w2 = -surf->vertex2[2]; w3 = -surf->vertex3[2];
64
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
65
66
if (surf->normal.x > 0.0f) {
67
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) {
68
continue;
69
}
70
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) {
71
continue;
72
}
73
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) {
74
continue;
75
}
76
} else {
77
if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) {
78
continue;
79
}
80
if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) {
81
continue;
82
}
83
if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) {
84
continue;
85
}
86
}
87
} else {
88
w1 = surf->vertex1[0]; w2 = surf->vertex2[0]; w3 = surf->vertex3[0];
89
y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1];
90
91
if (surf->normal.z > 0.0f) {
92
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) {
93
continue;
94
}
95
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) {
96
continue;
97
}
98
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) {
99
continue;
100
}
101
} else {
102
if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) {
103
continue;
104
}
105
if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) {
106
continue;
107
}
108
if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) {
109
continue;
110
}
111
}
112
}
113
114
// Determine if checking for the camera or not.
115
if (gCheckingSurfaceCollisionsForCamera) {
116
if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) {
117
continue;
118
}
119
} else {
120
// Ignore camera only surfaces.
121
if (surf->type == SURFACE_CAMERA_BOUNDARY) {
122
continue;
123
}
124
125
// If an object can pass through a vanish cap wall, pass through.
126
if (surf->type == SURFACE_VANISH_CAP_WALLS) {
127
// If an object can pass through a vanish cap wall, pass through.
128
if (gCurrentObject != NULL
129
&& (gCurrentObject->activeFlags & ACTIVE_FLAG_MOVE_THROUGH_GRATE)) {
130
continue;
131
}
132
133
// If Mario has a vanish cap, pass through the vanish cap wall.
134
if (gCurrentObject != NULL && gCurrentObject == gMarioObject
135
&& (gMarioState->flags & MARIO_VANISH_CAP)) {
136
continue;
137
}
138
}
139
}
140
141
//! (Wall Overlaps) Because this doesn't update the x and z local variables,
142
// multiple walls can push mario more than is required.
143
data->x += surf->normal.x * (radius - offset);
144
if (configApplyBugFixes > 1)
145
x += surf->normal.x * (radius - offset);
146
data->z += surf->normal.z * (radius - offset);
147
if (configApplyBugFixes > 1)
148
z += surf->normal.z * (radius - offset);
149
150
//! (Unreferenced Walls) Since this only returns the first four walls,
151
// this can lead to wall interaction being missed. Typically unreferenced walls
152
// come from only using one wall, however.
153
if (data->numWalls < 4) {
154
data->walls[data->numWalls++] = surf;
155
}
156
157
numCols++;
158
}
159
160
return numCols;
161
}
162
163
/**
164
* Formats the position and wall search for find_wall_collisions.
165
*/
166
s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius) {
167
struct WallCollisionData collision;
168
s32 numCollisions = 0;
169
170
collision.offsetY = offsetY;
171
collision.radius = radius;
172
173
collision.x = *xPtr;
174
collision.y = *yPtr;
175
collision.z = *zPtr;
176
177
collision.numWalls = 0;
178
179
numCollisions = find_wall_collisions(&collision);
180
181
*xPtr = collision.x;
182
*yPtr = collision.y;
183
*zPtr = collision.z;
184
185
return numCollisions;
186
}
187
188
/**
189
* Find wall collisions and receive their push.
190
*/
191
s32 find_wall_collisions(struct WallCollisionData *colData) {
192
struct SurfaceNode *node;
193
s16 cellX, cellZ;
194
s32 numCollisions = 0;
195
s16 x = colData->x;
196
s16 z = colData->z;
197
198
colData->numWalls = 0;
199
200
if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
201
return numCollisions;
202
}
203
if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
204
return numCollisions;
205
}
206
207
// World (level) consists of a 16x16 grid. Find where the collision is on
208
// the grid (round toward -inf)
209
cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
210
cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
211
212
// Check for surfaces belonging to objects.
213
node = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next;
214
numCollisions += find_wall_collisions_from_list(node, colData);
215
216
// Check for surfaces that are a part of level geometry.
217
node = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next;
218
numCollisions += find_wall_collisions_from_list(node, colData);
219
220
// Increment the debug tracker.
221
gNumCalls.wall += 1;
222
223
return numCollisions;
224
}
225
226
/**************************************************
227
* CEILINGS *
228
**************************************************/
229
230
/**
231
* Iterate through the list of ceilings and find the first ceiling over a given point.
232
*/
233
static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) {
234
register struct Surface *surf;
235
register s32 x1, z1, x2, z2, x3, z3;
236
struct Surface *ceil = NULL;
237
238
ceil = NULL;
239
240
if (configApplyBugFixes > 1)
241
*pheight = CELL_HEIGHT_LIMIT;
242
243
// Stay in this loop until out of ceilings.
244
while (surfaceNode != NULL) {
245
surf = surfaceNode->surface;
246
surfaceNode = surfaceNode->next;
247
248
x1 = surf->vertex1[0];
249
z1 = surf->vertex1[2];
250
z2 = surf->vertex2[2];
251
x2 = surf->vertex2[0];
252
253
// Checking if point is in bounds of the triangle laterally.
254
if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) > 0) {
255
continue;
256
}
257
258
// Slight optimization by checking these later.
259
x3 = surf->vertex3[0];
260
z3 = surf->vertex3[2];
261
if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) > 0) {
262
continue;
263
}
264
if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) > 0) {
265
continue;
266
}
267
268
// Determine if checking for the camera or not.
269
if (gCheckingSurfaceCollisionsForCamera != 0) {
270
if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) {
271
continue;
272
}
273
}
274
// Ignore camera only surfaces.
275
else if (surf->type == SURFACE_CAMERA_BOUNDARY) {
276
continue;
277
}
278
279
f32 nx = surf->normal.x;
280
f32 ny = surf->normal.y;
281
f32 nz = surf->normal.z;
282
f32 oo = surf->originOffset;
283
f32 height;
284
285
// If a wall, ignore it. Likely a remnant, should never occur.
286
if (ny == 0.0f) {
287
continue;
288
}
289
290
// Find the ceil height at the specific point.
291
height = -(x * nx + nz * z + oo) / ny;
292
293
if (configApplyBugFixes > 1) {
294
if (height > *pheight) {
295
continue;
296
}
297
if (y > height) {
298
continue;
299
}
300
if (y >= surf->upperY) {
301
continue;
302
}
303
*pheight = height;
304
ceil = surf;
305
if (height == y) {
306
break;
307
}
308
}
309
else {
310
311
// Checks for ceiling interaction with a 78 unit buffer.
312
//! (Exposed Ceilings) Because any point above a ceiling counts
313
// as interacting with a ceiling, ceilings far below can cause
314
// "invisible walls" that are really just exposed ceilings.
315
if (y - (height - -78.0f) > 0.0f) {
316
continue;
317
}
318
319
*pheight = height;
320
ceil = surf;
321
break;
322
}
323
}
324
325
//! (Surface Cucking) Since only the first ceil is returned and not the lowest,
326
// lower ceilings can be "cucked" by higher ceilings.
327
return ceil;
328
}
329
330
/**
331
* Find the lowest ceiling above a given position and return the height.
332
*/
333
f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) {
334
s16 cellZ, cellX;
335
struct Surface *ceil, *dynamicCeil;
336
struct SurfaceNode *surfaceList;
337
f32 height = CELL_HEIGHT_LIMIT;
338
f32 dynamicHeight = CELL_HEIGHT_LIMIT;
339
s16 x, y, z;
340
341
//! (Parallel Universes) Because position is casted to an s16, reaching higher
342
// float locations can return ceilings despite them not existing there.
343
//(Dynamic ceilings will unload due to the range.)
344
x = (s16) posX;
345
y = (s16) posY;
346
z = (s16) posZ;
347
*pceil = NULL;
348
349
if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
350
return height;
351
}
352
if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
353
return height;
354
}
355
356
// Each level is split into cells to limit load, find the appropriate cell.
357
cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
358
cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
359
360
// Check for surfaces belonging to objects.
361
surfaceList = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next;
362
dynamicCeil = find_ceil_from_list(surfaceList, x, y, z, &dynamicHeight);
363
364
// Check for surfaces that are a part of level geometry.
365
surfaceList = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next;
366
ceil = find_ceil_from_list(surfaceList, x, y, z, &height);
367
368
if (dynamicHeight < height) {
369
ceil = dynamicCeil;
370
height = dynamicHeight;
371
}
372
373
*pceil = ceil;
374
375
// Increment the debug tracker.
376
gNumCalls.ceil += 1;
377
378
return height;
379
}
380
381
/**************************************************
382
* FLOORS *
383
**************************************************/
384
385
/**
386
* Find the height of the highest floor below an object.
387
*/
388
f32 unused_obj_find_floor_height(struct Object *obj) {
389
struct Surface *floor;
390
f32 floorHeight = find_floor(obj->oPosX, obj->oPosY, obj->oPosZ, &floor);
391
return floorHeight;
392
}
393
394
/**
395
* Basically a local variable that passes through floor geo info.
396
*/
397
struct FloorGeometry sFloorGeo;
398
399
UNUSED static u8 unused8038BE50[0x40];
400
401
/**
402
* Return the floor height underneath (xPos, yPos, zPos) and populate `floorGeo`
403
* with data about the floor's normal vector and origin offset. Also update
404
* sFloorGeo.
405
*/
406
f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometry **floorGeo) {
407
struct Surface *floor;
408
f32 floorHeight = find_floor(xPos, yPos, zPos, &floor);
409
410
*floorGeo = NULL;
411
412
if (floor != NULL) {
413
sFloorGeo.normalX = floor->normal.x;
414
sFloorGeo.normalY = floor->normal.y;
415
sFloorGeo.normalZ = floor->normal.z;
416
sFloorGeo.originOffset = floor->originOffset;
417
418
*floorGeo = &sFloorGeo;
419
}
420
return floorHeight;
421
}
422
423
u8 gInterpolatingSurfaces;
424
425
/**
426
* Iterate through the list of floors and find the first floor under a given point.
427
*/
428
static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) {
429
register struct Surface *surf;
430
register f32 x1, z1, x2, z2, x3, z3;
431
f32 nx, ny, nz;
432
f32 oo;
433
f32 height;
434
struct Surface *floor = NULL;
435
f32 peak = -11000.0f;
436
s32 interpolate;
437
438
// Iterate through the list of floors until there are no more floors.
439
while (surfaceNode != NULL) {
440
surf = surfaceNode->surface;
441
surfaceNode = surfaceNode->next;
442
interpolate = gInterpolatingSurfaces && surf->modifiedTimestamp == gGlobalTimer;
443
444
x1 = surf->vertex1[0];
445
z1 = surf->vertex1[2];
446
x2 = surf->vertex2[0];
447
z2 = surf->vertex2[2];
448
if (interpolate) {
449
f32 diff = (surf->prevVertex1[0] - x1) * (surf->prevVertex1[0] - x1);
450
diff += (surf->prevVertex1[1] - surf->vertex1[1]) * (surf->prevVertex1[1] - surf->vertex1[1]);
451
diff += (surf->prevVertex1[2] - z1) * (surf->prevVertex1[2] - z1);
452
//printf("%f\n", sqrtf(diff));
453
if (diff > 10000) {
454
interpolate = FALSE;
455
} else {
456
x1 = (surf->prevVertex1[0] + x1) / 2;
457
z1 = (surf->prevVertex1[2] + z1) / 2;
458
x2 = (surf->prevVertex2[0] + x2) / 2;
459
z2 = (surf->prevVertex2[2] + z2) / 2;
460
}
461
}
462
463
// Check that the point is within the triangle bounds.
464
if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) {
465
continue;
466
}
467
468
// To slightly save on computation time, set this later.
469
x3 = surf->vertex3[0];
470
z3 = surf->vertex3[2];
471
472
if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) {
473
continue;
474
}
475
if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) < 0) {
476
continue;
477
}
478
479
// Determine if we are checking for the camera or not.
480
if (gCheckingSurfaceCollisionsForCamera != 0) {
481
if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) {
482
continue;
483
}
484
}
485
// If we are not checking for the camera, ignore camera only floors.
486
else if (surf->type == SURFACE_CAMERA_BOUNDARY) {
487
continue;
488
}
489
490
nx = surf->normal.x;
491
ny = surf->normal.y;
492
nz = surf->normal.z;
493
oo = surf->originOffset;
494
495
// If a wall, ignore it. Likely a remnant, should never occur.
496
if (ny == 0.0f) {
497
continue;
498
}
499
500
// Find the height of the floor at a given location.
501
height = -(x * nx + nz * z + oo) / ny;
502
// Checks for floor interaction with a 78 unit buffer.
503
if (y - (height + -78.0f) < 0.0f) {
504
continue;
505
}
506
507
*pheight = height;
508
floor = surf;
509
break;
510
511
// floor cucking fix, shoutouts to kaze and whoever josh is
512
if (configApplyBugFixes > 1 && peak < height) {
513
peak = height;
514
*pheight = height;
515
floor = surf;
516
}
517
}
518
519
return floor;
520
}
521
522
/**
523
* Find the height of the highest floor below a point.
524
*/
525
f32 find_floor_height(f32 x, f32 y, f32 z) {
526
struct Surface *floor;
527
528
f32 floorHeight = find_floor(x, y, z, &floor);
529
530
return floorHeight;
531
}
532
533
/**
534
* Find the highest dynamic floor under a given position. Perhaps originally static
535
* and dynamic floors were checked separately.
536
*/
537
f32 unused_find_dynamic_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) {
538
struct SurfaceNode *surfaceList;
539
struct Surface *floor;
540
f32 floorHeight = FLOOR_LOWER_LIMIT;
541
542
// Would normally cause PUs, but dynamic floors unload at that range.
543
s16 x = (s16) xPos;
544
s16 y = (s16) yPos;
545
s16 z = (s16) zPos;
546
547
// Each level is split into cells to limit load, find the appropriate cell.
548
s16 cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
549
s16 cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
550
551
surfaceList = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next;
552
floor = find_floor_from_list(surfaceList, x, y, z, &floorHeight);
553
554
*pfloor = floor;
555
556
return floorHeight;
557
}
558
559
/**
560
* Find the highest floor under a given position and return the height.
561
*/
562
f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) {
563
s16 cellZ, cellX;
564
565
struct Surface *floor, *dynamicFloor;
566
struct SurfaceNode *surfaceList;
567
568
f32 height = FLOOR_LOWER_LIMIT;
569
f32 dynamicHeight = FLOOR_LOWER_LIMIT;
570
571
//! (Parallel Universes) Because position is casted to an s16, reaching higher
572
// float locations can return floors despite them not existing there.
573
//(Dynamic floors will unload due to the range.)
574
s16 x = (s16) xPos;
575
s16 y = (s16) yPos;
576
s16 z = (s16) zPos;
577
578
*pfloor = NULL;
579
580
if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) {
581
return height;
582
}
583
if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) {
584
return height;
585
}
586
587
// Each level is split into cells to limit load, find the appropriate cell.
588
cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
589
cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX;
590
591
// Check for surfaces belonging to objects.
592
surfaceList = gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next;
593
dynamicFloor = find_floor_from_list(surfaceList, x, y, z, &dynamicHeight);
594
595
// Check for surfaces that are a part of level geometry.
596
surfaceList = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next;
597
floor = find_floor_from_list(surfaceList, x, y, z, &height);
598
599
// To prevent the Merry-Go-Round room from loading when Mario passes above the hole that leads
600
// there, SURFACE_INTANGIBLE is used. This prevent the wrong room from loading, but can also allow
601
// Mario to pass through.
602
if (!gFindFloorIncludeSurfaceIntangible) {
603
//! (BBH Crash) Most NULL checking is done by checking the height of the floor returned
604
// instead of checking directly for a NULL floor. If this check returns a NULL floor
605
// (happens when there is no floor under the SURFACE_INTANGIBLE floor) but returns the height
606
// of the SURFACE_INTANGIBLE floor instead of the typical -11000 returned for a NULL floor.
607
if (floor != NULL && floor->type == SURFACE_INTANGIBLE) {
608
floor = find_floor_from_list(surfaceList, x, (s32)(height - 200.0f), z, &height);
609
}
610
} else {
611
// To prevent accidentally leaving the floor tangible, stop checking for it.
612
gFindFloorIncludeSurfaceIntangible = FALSE;
613
}
614
615
// If a floor was missed, increment the debug counter.
616
if (floor == NULL) {
617
gNumFindFloorMisses += 1;
618
}
619
620
if (dynamicHeight > height) {
621
floor = dynamicFloor;
622
height = dynamicHeight;
623
}
624
625
*pfloor = floor;
626
627
// Increment the debug tracker.
628
gNumCalls.floor += 1;
629
630
return height;
631
}
632
633
/**************************************************
634
* ENVIRONMENTAL BOXES *
635
**************************************************/
636
637
/**
638
* Finds the height of water at a given location.
639
*/
640
f32 find_water_level(f32 x, f32 z) {
641
s32 i;
642
s32 numRegions;
643
s16 val;
644
f32 loX, hiX, loZ, hiZ;
645
f32 waterLevel = FLOOR_LOWER_LIMIT;
646
s16 *p = gEnvironmentRegions;
647
648
if (p != NULL) {
649
numRegions = *p++;
650
651
for (i = 0; i < numRegions; i++) {
652
val = *p++;
653
loX = *p++;
654
loZ = *p++;
655
hiX = *p++;
656
hiZ = *p++;
657
658
// If the location is within a water box and it is a water box.
659
// Water is less than 50 val only, while above is gas and such.
660
if (loX < x && x < hiX && loZ < z && z < hiZ && val < 50) {
661
// Set the water height. Since this breaks, only return the first height.
662
waterLevel = *p;
663
break;
664
}
665
p++;
666
}
667
}
668
669
return waterLevel;
670
}
671
672
/**
673
* Finds the height of the poison gas (used only in HMC) at a given location.
674
*/
675
f32 find_poison_gas_level(f32 x, f32 z) {
676
s32 i;
677
s32 numRegions;
678
UNUSED s32 unused;
679
s16 val;
680
f32 loX, hiX, loZ, hiZ;
681
f32 gasLevel = FLOOR_LOWER_LIMIT;
682
s16 *p = gEnvironmentRegions;
683
684
if (p != NULL) {
685
numRegions = *p++;
686
687
for (i = 0; i < numRegions; i++) {
688
val = *p;
689
690
if (val >= 50) {
691
loX = p[1];
692
loZ = p[2];
693
hiX = p[3];
694
hiZ = p[4];
695
696
// If the location is within a gas's box and it is a gas box.
697
// Gas has a value of 50, 60, etc.
698
if (loX < x && x < hiX && loZ < z && z < hiZ && val % 10 == 0) {
699
// Set the gas height. Since this breaks, only return the first height.
700
gasLevel = p[5];
701
break;
702
}
703
}
704
705
p += 6;
706
}
707
}
708
709
return gasLevel;
710
}
711
712
/**************************************************
713
* DEBUG *
714
**************************************************/
715
716
/**
717
* Finds the length of a surface list for debug purposes.
718
*/
719
static s32 surface_list_length(struct SurfaceNode *list) {
720
s32 count = 0;
721
722
while (list != NULL) {
723
list = list->next;
724
count++;
725
}
726
727
return count;
728
}
729
730
/**
731
* Print the area,number of walls, how many times they were called,
732
* and some allocation information.
733
*/
734
void debug_surface_list_info(f32 xPos, f32 zPos) {
735
struct SurfaceNode *list;
736
s32 numFloors = 0;
737
s32 numWalls = 0;
738
s32 numCeils = 0;
739
740
s32 cellX = (xPos + LEVEL_BOUNDARY_MAX) / CELL_SIZE;
741
s32 cellZ = (zPos + LEVEL_BOUNDARY_MAX) / CELL_SIZE;
742
743
list = gStaticSurfacePartition[cellZ & NUM_CELLS_INDEX][cellX & NUM_CELLS_INDEX][SPATIAL_PARTITION_FLOORS].next;
744
numFloors += surface_list_length(list);
745
746
list = gDynamicSurfacePartition[cellZ & NUM_CELLS_INDEX][cellX & NUM_CELLS_INDEX][SPATIAL_PARTITION_FLOORS].next;
747
numFloors += surface_list_length(list);
748
749
list = gStaticSurfacePartition[cellZ & NUM_CELLS_INDEX][cellX & NUM_CELLS_INDEX][SPATIAL_PARTITION_WALLS].next;
750
numWalls += surface_list_length(list);
751
752
list = gDynamicSurfacePartition[cellZ & NUM_CELLS_INDEX][cellX & NUM_CELLS_INDEX][SPATIAL_PARTITION_WALLS].next;
753
numWalls += surface_list_length(list);
754
755
list = gStaticSurfacePartition[cellZ & NUM_CELLS_INDEX][cellX & NUM_CELLS_INDEX][SPATIAL_PARTITION_CEILS].next;
756
numCeils += surface_list_length(list);
757
758
list = gDynamicSurfacePartition[cellZ & NUM_CELLS_INDEX][cellX & NUM_CELLS_INDEX][SPATIAL_PARTITION_CEILS].next;
759
numCeils += surface_list_length(list);
760
761
print_debug_top_down_mapinfo("area %x", cellZ * NUM_CELLS + cellX);
762
763
// Names represent ground, walls, and roofs as found in SMS.
764
print_debug_top_down_mapinfo("dg %d", numFloors);
765
print_debug_top_down_mapinfo("dw %d", numWalls);
766
print_debug_top_down_mapinfo("dr %d", numCeils);
767
768
set_text_array_x_y(80, -3);
769
770
print_debug_top_down_mapinfo("%d", gNumCalls.floor);
771
print_debug_top_down_mapinfo("%d", gNumCalls.wall);
772
print_debug_top_down_mapinfo("%d", gNumCalls.ceil);
773
774
set_text_array_x_y(-80, 0);
775
776
// listal- List Allocated?, statbg- Static Background?, movebg- Moving Background?
777
print_debug_top_down_mapinfo("listal %d", gSurfaceNodesAllocated);
778
print_debug_top_down_mapinfo("statbg %d", gNumStaticSurfaces);
779
print_debug_top_down_mapinfo("movebg %d", gSurfacesAllocated - gNumStaticSurfaces);
780
781
gNumCalls.floor = 0;
782
gNumCalls.ceil = 0;
783
gNumCalls.wall = 0;
784
}
785
786
/**
787
* An unused function that finds and interacts with any type of surface.
788
* Perhaps an original implementation of surfaces before they were more specialized.
789
*/
790
s32 unused_resolve_floor_or_ceil_collisions(s32 checkCeil, f32 *px, f32 *py, f32 *pz, f32 radius,
791
struct Surface **psurface, f32 *surfaceHeight) {
792
f32 nx, ny, nz, oo;
793
f32 x = *px;
794
f32 y = *py;
795
f32 z = *pz;
796
f32 offset, distance;
797
798
*psurface = NULL;
799
800
if (checkCeil) {
801
*surfaceHeight = find_ceil(x, y, z, psurface);
802
} else {
803
*surfaceHeight = find_floor(x, y, z, psurface);
804
}
805
806
if (*psurface == NULL) {
807
return -1;
808
}
809
810
nx = (*psurface)->normal.x;
811
ny = (*psurface)->normal.y;
812
nz = (*psurface)->normal.z;
813
oo = (*psurface)->originOffset;
814
815
offset = nx * x + ny * y + nz * z + oo;
816
distance = offset >= 0 ? offset : -offset;
817
818
// Interesting surface interaction that should be surf type independent.
819
if (distance < radius) {
820
*px += nx * (radius - offset);
821
*py += ny * (radius - offset);
822
*pz += nz * (radius - offset);
823
824
return 1;
825
}
826
827
return 0;
828
}
829
830