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