From 2927db262cdfa758f0d5fe4f45a8be40ebf817d9 Mon Sep 17 00:00:00 2001 From: Arceveti <73617174+Arceveti@users.noreply.github.com> Date: Fri, 17 Sep 2021 19:26:13 -0700 Subject: [PATCH] Add FramePerfection's Better Wall Collision with rounded corners --- README.md | 2 +- include/config.h | 2 + include/sm64.h | 1 + include/surface_terrains.h | 1 - src/engine/surface_collision.c | 303 ++++++++++++++++++++--------- src/engine/surface_collision.h | 4 +- src/engine/surface_load.c | 4 - src/game/camera.c | 2 +- src/game/interaction.c | 12 +- src/game/mario.c | 37 +--- src/game/mario.h | 2 - src/game/mario_actions_automatic.c | 9 +- src/game/mario_actions_cutscene.c | 3 +- src/game/mario_actions_submerged.c | 6 +- src/game/mario_step.c | 196 ++++++++++++------- src/game/puppycam2.c | 5 +- 16 files changed, 369 insertions(+), 220 deletions(-) diff --git a/README.md b/README.md index 75ff3e77..3efc5ad2 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This is a fork of the ultrasm64 repo by CrashOveride which includes the followin - Increased maximum pole length (The game will read bparam1 and bparam2 together as a single value, so you can have a very long pole) * - Platform Displacement 2 by Arthurtilly * - Water Surface Type patch by thecozies -- Rounded corners by FramePerfection, merged by Cheezepin +- Better Wall Collision With Rounded Corners by FramePerfection, merged by Cheezepin & Arceveti - Automatically calculate the optimal collision distance for an object based on its vertices, by Kaze * **Common Hack Changes:** diff --git a/include/config.h b/include/config.h index 5f621693..b9b2808b 100644 --- a/include/config.h +++ b/include/config.h @@ -129,6 +129,8 @@ //#define VISUAL_DEBUG // Number of supported areas per level. #define AREA_COUNT 8 +// Number of walls that can push Mario at once. +#define MAX_REFEREMCED_WALLS 4 // Lightweight directional lighting engine by Fazana. Intended for giving proximity and positional pointlights to small objects. //#define PUPPYLIGHTS // Open all courses and doors. Used for debugging purposes to unlock all content. diff --git a/include/sm64.h b/include/sm64.h index 88446c92..9478273f 100644 --- a/include/sm64.h +++ b/include/sm64.h @@ -81,6 +81,7 @@ #define AIR_STEP_GRABBED_LEDGE 3 #define AIR_STEP_GRABBED_CEILING 4 #define AIR_STEP_HIT_LAVA_WALL 6 +#define AIR_STEP_HIT_CEILING 7 #define WATER_STEP_NONE 0 #define WATER_STEP_HIT_FLOOR 1 diff --git a/include/surface_terrains.h b/include/surface_terrains.h index 6f231e93..6a13429b 100644 --- a/include/surface_terrains.h +++ b/include/surface_terrains.h @@ -166,7 +166,6 @@ #define SURFACE_FLAG_DYNAMIC (1 << 0) #define SURFACE_FLAG_NO_CAM_COLLISION (1 << 1) -#define SURFACE_FLAG_X_PROJECTION (1 << 3) // These are effectively unique "surface" types like those defined higher // And they are used as collision commands to load certain functions diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 69f93561..b5cc1f3a 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -5,6 +5,8 @@ #include "game/level_update.h" #include "game/mario.h" #include "game/object_list_processor.h" +// #include "game/rendering_graph_node.h" +#include "math_util.h" #include "surface_collision.h" #include "surface_load.h" #include "game/puppyprint.h" @@ -17,19 +19,33 @@ * Iterate through the list of walls until all walls are checked and * have given their wall push. */ -static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, - struct WallCollisionData *data) { +static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, struct WallCollisionData *data) { + const f32 corner_threshold = -0.9f; register struct Surface *surf; register f32 offset; register f32 radius = data->radius; register f32 x = data->x; register f32 y = data->y + data->offsetY; register f32 z = data->z; - register f32 px, pz; - register f32 w1, w2, w3; - register f32 y1, y2, y3; + register f32 v0x, v0y, v0z; + register f32 v1x, v1y, v1z; + register f32 v2x, v2y, v2z; + register f32 d00, d01, d11, d20, d21; + register f32 invDenom; + register f32 v, w; + register f32 margin_radius = (radius - 1.0f); + s32 numCols = 0; +// #if EXTENDED_BOUNDS_MODE +// const float down_scale = (1.0f / gWorldScale); +// radius *= down_scale; +// x *= down_scale; +// y *= down_scale; +// z *= down_scale; +// margin_radius *= down_scale; +// #endif + // Max collision radius = 200 if (radius > 200.0f) { radius = 200.0f; @@ -45,70 +61,12 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, continue; } - offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset; + offset = (surf->normal.x * x) + (surf->normal.y * y) + (surf->normal.z * z) + surf->originOffset; if (offset < -radius || offset > radius) { continue; } - px = x; - pz = z; - - //! (Quantum Tunneling) Due to issues with the vertices walls choose and - // the fact they are floating point, certain floating point positions - // along the seam of two walls may collide with neither wall or both walls. - if (surf->flags & SURFACE_FLAG_X_PROJECTION) { - w1 = -surf->vertex1[2]; w2 = -surf->vertex2[2]; w3 = -surf->vertex3[2]; - y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1]; - - if (surf->normal.x > 0.0f) { - if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) { - continue; - } - if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) { - continue; - } - if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) { - continue; - } - } else { - if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) { - continue; - } - if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) { - continue; - } - if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) { - continue; - } - } - } else { - w1 = surf->vertex1[0]; w2 = surf->vertex2[0]; w3 = surf->vertex3[0]; - y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1]; - - if (surf->normal.z > 0.0f) { - if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) { - continue; - } - if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) { - continue; - } - if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) { - continue; - } - } else { - if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) { - continue; - } - if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) { - continue; - } - if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) { - continue; - } - } - } - // Determine if checking for the camera or not. if (gCheckingSurfaceCollisionsForCamera) { if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { @@ -136,21 +94,140 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, } } - //! (Wall Overlaps) Because this doesn't update the x and z local variables, - // multiple walls can push mario more than is required. - data->x += surf->normal.x * (radius - offset); - data->z += surf->normal.z * (radius - offset); + v0x = (f32)(surf->vertex2[0] - surf->vertex1[0]); + v0y = (f32)(surf->vertex2[1] - surf->vertex1[1]); + v0z = (f32)(surf->vertex2[2] - surf->vertex1[2]); + v1x = (f32)(surf->vertex3[0] - surf->vertex1[0]); + v1y = (f32)(surf->vertex3[1] - surf->vertex1[1]); + v1z = (f32)(surf->vertex3[2] - surf->vertex1[2]); + + v2x = x - (f32)surf->vertex1[0]; + v2y = y - (f32)surf->vertex1[1]; + v2z = z - (f32)surf->vertex1[2]; + + // Face + d00 = (v0x * v0x) + (v0y * v0y) + (v0z * v0z); + d01 = (v0x * v1x) + (v0y * v1y) + (v0z * v1z); + d11 = (v1x * v1x) + (v1y * v1y) + (v1z * v1z); + d20 = (v2x * v0x) + (v2y * v0y) + (v2z * v0z); + d21 = (v2x * v1x) + (v2y * v1y) + (v2z * v1z); + invDenom = 1.0f / ((d00 * d11) - (d01 * d01)); + v = ((d11 * d20) - (d01 * d21)) * invDenom; + if (v < 0.0f || v > 1.0f) { + goto edge_1_2; + } + w = (d00 * d21 - d01 * d20) * invDenom; + if (w < 0.0f || w > 1.0f || v + w > 1.0f) { + goto edge_1_2; + } + x += surf->normal.x * (radius - offset); + z += surf->normal.z * (radius - offset); + goto hasCollision; + + edge_1_2: + if (offset < 0) continue; + // Edge 1-2 + if (v0y != 0.0f) { + v = (v2y / v0y); + if (v < 0.0f || v > 1.0f) { + goto edge_1_3; + } + d00 = v0x * v - v2x; + d01 = v0z * v - v2z; + invDenom = sqrtf(sqr(d00) + sqr(d01)); + offset = invDenom - margin_radius; + if (offset > 0.0f) { + goto edge_1_3; + } + invDenom = offset / invDenom; + x += (d00 *= invDenom); + z += (d01 *= invDenom); + margin_radius += 0.01f; + + if ((d00 * surf->normal.x) + (d01 * surf->normal.z) < (corner_threshold * offset)) { + continue; + } else { + goto hasCollision; + } + } + + edge_1_3: + // Edge 1-3 + if (v1y != 0.0f) { + v = (v2y / v1y); + if (v < 0.0f || v > 1.0f) { + goto edge_2_3; + } + d00 = v1x * v - v2x; + d01 = v1z * v - v2z; + invDenom = sqrtf(sqr(d00) + sqr(d01)); + offset = invDenom - margin_radius; + if (offset > 0.0f) { + goto edge_2_3; + } + invDenom = offset / invDenom; + x += (d00 *= invDenom); + z += (d01 *= invDenom); + margin_radius += 0.01f; + + if ((d00 * surf->normal.x) + (d01 * surf->normal.z) < (corner_threshold * offset)) { + continue; + } else { + goto hasCollision; + } + } + + edge_2_3: + // Edge 2-3 + v1x = (f32)(surf->vertex3[0] - surf->vertex2[0]); + v1y = (f32)(surf->vertex3[1] - surf->vertex2[1]); + v1z = (f32)(surf->vertex3[2] - surf->vertex2[2]); + + v2x = x - (f32)surf->vertex2[0]; + v2y = y - (f32)surf->vertex2[1]; + v2z = z - (f32)surf->vertex2[2]; + + if (v1y != 0.0f) { + v = (v2y / v1y); + if (v < 0.0f || v > 1.0f) continue; + d00 = v1x * v - v2x; + d01 = v1z * v - v2z; + invDenom = sqrtf(sqr(d00) + sqr(d01)); + offset = invDenom - margin_radius; + if (offset > 0.0f) continue; + invDenom = offset / invDenom; + x += (d00 *= invDenom); + z += (d01 *= invDenom); + margin_radius += 0.01f; + if ((d00 * surf->normal.x) + (d01 * surf->normal.z) < (corner_threshold * offset)) { + continue; + } else { + goto hasCollision; + } + } else { + continue; + } + hasCollision: //! (Unreferenced Walls) Since this only returns the first four walls, // this can lead to wall interaction being missed. Typically unreferenced walls // come from only using one wall, however. - if (data->numWalls < 4) { + if (data->numWalls < MAX_REFEREMCED_WALLS) { data->walls[data->numWalls++] = surf; } numCols++; } +// #if EXTENDED_BOUNDS_MODE +// x *= gWorldScale; +// y *= gWorldScale; +// z *= gWorldScale; +// #endif + + data->x = x; + data->z = z; + return numCols; } @@ -224,17 +301,44 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { return numCollisions; } +/** + * Collides with walls and returns the most recent wall. + */ +void resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius, struct WallCollisionData *collisionData) { + collisionData->x = pos[0]; + collisionData->y = pos[1]; + collisionData->z = pos[2]; + collisionData->radius = radius; + collisionData->offsetY = offset; + + find_wall_collisions(collisionData); + + pos[0] = collisionData->x; + pos[1] = collisionData->y; + pos[2] = collisionData->z; +} + /************************************************** * CEILINGS * **************************************************/ +void add_ceil_margin(s32 *x, s32 *z, Vec3s target1, Vec3s target2, f32 margin) { + register f32 diff_x, diff_z, invDenom; + diff_x = target1[0] - *x + target2[0] - *x; + diff_z = target1[2] - *z + target2[2] - *z; + invDenom = margin / sqrtf(sqr(diff_x) + sqr(diff_z)); + *x += diff_x * invDenom; + *z += diff_z * invDenom; +} + /** * Iterate through the list of ceilings and find the first ceiling over a given point. */ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) { register struct Surface *surf; - register s32 x1, z1, x2, z2, x3, z3; + s32 x1, z1, x2, z2, x3, z3; f32 nx, ny, nz, oo, height; + const f32 margin = 1.5f; struct Surface *ceil = NULL; *pheight = CELL_HEIGHT_LIMIT; // Stay in this loop until out of ceilings. @@ -245,8 +349,14 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 continue; x1 = surf->vertex1[0]; z1 = surf->vertex1[2]; + if (surf->type != SURFACE_HANGABLE) { + add_ceil_margin(&x1, &z1, surf->vertex2, surf->vertex3, margin); + } z2 = surf->vertex2[2]; x2 = surf->vertex2[0]; + if (surf->type != SURFACE_HANGABLE) { + add_ceil_margin(&x2, &z2, surf->vertex3, surf->vertex1, margin); + } // Checking if point is in bounds of the triangle laterally. if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) > 0) { continue; @@ -254,6 +364,9 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 // Slight optimization by checking these later. x3 = surf->vertex3[0]; z3 = surf->vertex3[2]; + if (surf->type != SURFACE_HANGABLE) { + add_ceil_margin(&x3, &z3, surf->vertex1, surf->vertex2, margin); + } if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) > 0) { continue; } @@ -270,31 +383,31 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 else if (surf->type == SURFACE_CAMERA_BOUNDARY || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { continue; } - nx = surf->normal.x; - ny = surf->normal.y; - nz = surf->normal.z; - oo = surf->originOffset; - // If a wall, ignore it. Likely a remnant, should never occur. - if (ny == 0.0f) { - continue; - } - // Find the ceil height at the specific point. - height = -(x * nx + nz * z + oo) / ny; - if (height > *pheight) { - continue; - } - // Checks for ceiling interaction - if (y > height) { - continue; - } - if (y >= surf->upperY) { - continue; - } - *pheight = height; - ceil = surf; - if (height == y) { - break; - } + nx = surf->normal.x; + ny = surf->normal.y; + nz = surf->normal.z; + oo = surf->originOffset; + // If a wall, ignore it. Likely a remnant, should never occur. + if (ny == 0.0f) { + continue; + } + // Find the ceil height at the specific point. + height = -(x * nx + nz * z + oo) / ny; + if (height > *pheight) { + continue; + } + // Checks for ceiling interaction + if (y > height) { + continue; + } + if (y >= surf->upperY) { + continue; + } + *pheight = height; + ceil = surf; + if (height == y) { + break; + } } return ceil; } diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h index af1a9d1f..56f5abdf 100644 --- a/src/engine/surface_collision.h +++ b/src/engine/surface_collision.h @@ -7,7 +7,6 @@ #include "engine/extended_bounds.h" - #define CELL_HEIGHT_LIMIT 20000 #define FLOOR_LOWER_LIMIT -11000 #define FLOOR_LOWER_LIMIT_MISC (FLOOR_LOWER_LIMIT + 1000) @@ -22,11 +21,12 @@ struct WallCollisionData /*0x10*/ f32 radius; /*0x14*/ s16 unused; /*0x16*/ s16 numWalls; - /*0x18*/ struct Surface *walls[4]; + /*0x18*/ struct Surface *walls[MAX_REFEREMCED_WALLS]; }; s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius); s32 find_wall_collisions(struct WallCollisionData *colData); +void resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius, struct WallCollisionData *collisionData); f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil); f32 find_floor_height(f32 x, f32 y, f32 z); f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor); diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index c3fdb3cc..eae8335b 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -130,10 +130,6 @@ static void add_surface_to_cell(s32 dynamic, s32 cellX, s32 cellZ, struct Surfac } else { listIndex = SPATIAL_PARTITION_WALLS; sortDir = 0; // insertion order - - if (surface->normal.x < -0.707 || surface->normal.x > 0.707) { - surface->flags |= SURFACE_FLAG_X_PROJECTION; - } } //! (Surface Cucking) Surfaces are sorted by the height of their first diff --git a/src/game/camera.c b/src/game/camera.c index 64604a7c..bbe29476 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -4010,7 +4010,7 @@ s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius) { f32 originOffset; f32 offset; f32 offsetAbsolute; - Vec3f newPos[4]; + Vec3f newPos[MAX_REFEREMCED_WALLS]; s32 i; s32 numCollisions = 0; diff --git a/src/game/interaction.c b/src/game/interaction.c index 03f4ceb3..800ad0fb 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -1771,12 +1771,14 @@ u32 interact_text(struct MarioState *m, UNUSED u32 interactType, struct Object * void check_kick_or_punch_wall(struct MarioState *m) { if (m->flags & (MARIO_PUNCHING | MARIO_KICKING | MARIO_TRIPPING)) { - Vec3f detector; - detector[0] = m->pos[0] + 50.0f * sins(m->faceAngle[1]); - detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]); - detector[1] = m->pos[1]; + struct WallCollisionData detector; + detector.x = m->pos[0] + 50.0f * sins(m->faceAngle[1]); + detector.z = m->pos[2] + 50.0f * coss(m->faceAngle[1]); + detector.y = m->pos[1]; + detector.offsetY = 80.0f; + detector.radius = 5.0f; - if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) { + if (find_wall_collisions(&detector) > 0) { if (m->action != ACT_MOVE_PUNCHING || m->forwardVel >= 0.0f) { if (m->action == ACT_PUNCHING) { m->action = ACT_MOVE_PUNCHING; diff --git a/src/game/mario.c b/src/game/mario.c index 582c9b5c..948db0dc 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -515,41 +515,6 @@ u32 mario_get_terrain_sound_addend(struct MarioState *m) { return ret; } -/** - * Collides with walls and returns the most recent wall. - */ -struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius) { - struct WallCollisionData collisionData; - struct Surface *wall = NULL; - - collisionData.x = pos[0]; - collisionData.y = pos[1]; - collisionData.z = pos[2]; - collisionData.radius = radius; - collisionData.offsetY = offset; - - if (find_wall_collisions(&collisionData)) { - wall = collisionData.walls[collisionData.numWalls - 1]; - } - - pos[0] = collisionData.x; - pos[1] = collisionData.y; - pos[2] = collisionData.z; - - // This only returns the most recent wall and can also return NULL - // there are no wall collisions. - return wall; -} - -/** - * Finds the ceiling from a vec3f horizontally and a height (with 80 vertical buffer). - */ -f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil) { - UNUSED f32 unused; - - return find_ceil(pos[0], height + 3.0f, pos[2], ceil); -} - /** * Determines if Mario is facing "downhill." */ @@ -1370,7 +1335,7 @@ void update_mario_geometry_inputs(struct MarioState *m) { m->floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &m->floor); } - m->ceilHeight = vec3f_find_ceil(m->pos, m->pos[1], &m->ceil); + m->ceilHeight = find_ceil(m->pos[0], m->pos[1] + 3.0f, m->pos[2], &m->ceil); gasLevel = find_poison_gas_level(m->pos[0], m->pos[2]); m->waterLevel = find_water_level(m->pos[0], m->pos[2]); diff --git a/src/game/mario.h b/src/game/mario.h index 3b050b42..48d28655 100644 --- a/src/game/mario.h +++ b/src/game/mario.h @@ -28,8 +28,6 @@ void play_mario_sound(struct MarioState *m, s32 primarySoundBits, s32 scondarySo void mario_set_forward_vel(struct MarioState *m, f32 speed); s32 mario_get_floor_class(struct MarioState *m); u32 mario_get_terrain_sound_addend(struct MarioState *m); -struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius); -f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil); s32 mario_facing_downhill(struct MarioState *m, s32 turnYaw); u32 mario_floor_is_slippery(struct MarioState *m); s32 mario_floor_is_slope(struct MarioState *m); diff --git a/src/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c index 47e62040..e61cef20 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -81,7 +81,7 @@ s32 set_pole_position(struct MarioState *m, f32 offsetY) { collided = f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 60.0f, 50.0f); collided |= f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 30.0f, 24.0f); - ceilHeight = vec3f_find_ceil(m->pos, m->pos[1], &ceil); + ceilHeight = find_ceil(m->pos[0], m->pos[1] + 3.0f, m->pos[2], &ceil); if (m->pos[1] > ceilHeight - 160.0f) { m->pos[1] = ceilHeight - 160.0f; marioObj->oMarioPolePos = m->pos[1] - m->usedObj->oPosY; @@ -307,10 +307,13 @@ s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) { f32 ceilHeight; f32 floorHeight; f32 ceilOffset; + struct WallCollisionData wallCollisionData; + + resolve_and_return_wall_collisions(nextPos, 50.0f, 50.0f, &wallCollisionData); + m->wall = wallCollisionData.numWalls == 0 ? NULL : wallCollisionData.walls[0]; - m->wall = resolve_and_return_wall_collisions(nextPos, 50.0f, 50.0f); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); + ceilHeight = find_ceil(nextPos[0], nextPos[1] + 3.0f, nextPos[2], &ceil); if (floor == NULL) { return HANG_HIT_CEIL_OR_OOB; diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index fb05f35f..634ab10d 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -534,6 +534,7 @@ s32 act_reading_sign(struct MarioState *m) { } s32 act_debug_free_move(struct MarioState *m) { + struct WallCollisionData wallData; struct Surface *surf; f32 floorHeight; Vec3f pos; @@ -561,7 +562,7 @@ s32 act_debug_free_move(struct MarioState *m) { pos[2] += 32.0f * speed * coss(m->intendedYaw); } - resolve_and_return_wall_collisions(pos, 60.0f, 50.0f); + resolve_and_return_wall_collisions(pos, 60.0f, 50.0f, &wallData); floorHeight = find_floor(pos[0], pos[1], pos[2], &surf); if (surf != NULL) { diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index 680ee8d1..1414241e 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -68,15 +68,17 @@ static f32 get_buoyancy(struct MarioState *m) { } static u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) { + struct WallCollisionData wallData; struct Surface *wall; struct Surface *ceil; struct Surface *floor; f32 ceilHeight; f32 floorHeight; - wall = resolve_and_return_wall_collisions(nextPos, 10.0f, 110.0f); + resolve_and_return_wall_collisions(nextPos, 10.0f, 110.0f, &wallData); + wall = wallData.numWalls == 0 ? NULL : wallData.walls[0]; floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); + ceilHeight = find_ceil(nextPos[0], nextPos[1] + 3.0f, nextPos[2], &ceil); if (floor == NULL) { return WATER_STEP_CANCELLED; diff --git a/src/game/mario_step.c b/src/game/mario_step.c index 210b19bd..1f03992a 100644 --- a/src/game/mario_step.c +++ b/src/game/mario_step.c @@ -73,7 +73,7 @@ void transfer_bully_speed(struct BullyCollisionData *obj1, struct BullyCollision //! Bully battery } -BAD_RETURN(s32) init_bully_collision_data(struct BullyCollisionData *data, f32 posX, f32 posZ, +void init_bully_collision_data(struct BullyCollisionData *data, f32 posX, f32 posZ, f32 forwardVel, s16 yaw, f32 conversionRatio, f32 radius) { if (forwardVel < 0.0f) { forwardVel *= -1.0f; @@ -258,24 +258,27 @@ s32 stationary_ground_step(struct MarioState *m) { } static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) { - UNUSED struct Surface *lowerWall; - struct Surface *upperWall; + struct WallCollisionData lowerWall; + struct WallCollisionData upperWall; struct Surface *ceil; struct Surface *floor; f32 ceilHeight; f32 floorHeight; f32 waterLevel; - lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f); - upperWall = resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f); + s16 i; + s16 wallDYaw; + s32 oldWallDYaw; + s32 absWallDYaw; + + resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f, &lowerWall); + resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f, &upperWall); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); + ceilHeight = find_ceil(nextPos[0], nextPos[1] + 3.0f, nextPos[2], &ceil); waterLevel = find_water_level(nextPos[0], nextPos[2]); - m->wall = upperWall; - if (floor == NULL) { return GROUND_STEP_HIT_WALL_STOP_QSTEPS; } @@ -305,14 +308,25 @@ static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) { m->floor = floor; m->floorHeight = floorHeight; - if (upperWall != NULL) { - s16 wallDYaw = atan2s(upperWall->normal.z, upperWall->normal.x) - m->faceAngle[1]; + if (m->wall != NULL) { + oldWallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + oldWallDYaw = oldWallDYaw < 0 ? -oldWallDYaw : oldWallDYaw; + } else { + oldWallDYaw = 0; + } + for (i = 0; i < upperWall.numWalls; i++) { + wallDYaw = atan2s(upperWall.walls[i]->normal.z, upperWall.walls[i]->normal.x) - m->faceAngle[1]; + absWallDYaw = wallDYaw < 0 ? -wallDYaw : wallDYaw; + if (absWallDYaw > oldWallDYaw) { + oldWallDYaw = absWallDYaw; + m->wall = upperWall.walls[i]; + } if (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) { - return GROUND_STEP_NONE; + continue; } if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) { - return GROUND_STEP_NONE; + continue; } return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS; @@ -326,6 +340,8 @@ s32 perform_ground_step(struct MarioState *m) { u32 stepResult; Vec3f intendedPos; + m->wall = NULL; + for (i = 0; i < 4; i++) { intendedPos[0] = m->pos[0] + m->floor->normal.y * (m->vel[0] / 4.0f); intendedPos[2] = m->pos[2] + m->floor->normal.y * (m->vel[2] / 4.0f); @@ -347,9 +363,7 @@ s32 perform_ground_step(struct MarioState *m) { return stepResult; } -u32 check_ledge_grab(struct MarioState *m, struct Surface *wall, Vec3f intendedPos, Vec3f nextPos) { - struct Surface *ledgeFloor; - Vec3f ledgePos; +struct Surface *check_ledge_grab(struct MarioState *m, struct Surface *grabbedWall, struct Surface *wall, Vec3f intendedPos, Vec3f nextPos, Vec3f ledgePos, struct Surface **ledgeFloor) { f32 displacementX; f32 displacementZ; @@ -357,54 +371,99 @@ u32 check_ledge_grab(struct MarioState *m, struct Surface *wall, Vec3f intendedP return FALSE; } + // Return the already grabbed wall if Mario is moving into it more than the newly tested wall + if (grabbedWall != NULL && + grabbedWall->normal.x * m->vel[0] + grabbedWall->normal.z * m->vel[2] < wall->normal.x * m->vel[0] + wall->normal.z * m->vel[2]) + return grabbedWall; + displacementX = nextPos[0] - intendedPos[0]; displacementZ = nextPos[2] - intendedPos[2]; // Only ledge grab if the wall displaced Mario in the opposite direction of // his velocity. if (displacementX * m->vel[0] + displacementZ * m->vel[2] > 0.0f) { - return FALSE; + return grabbedWall; } //! Since the search for floors starts at y + 160, we will sometimes grab // a higher ledge than expected (glitchy ledge grab) ledgePos[0] = nextPos[0] - wall->normal.x * 60.0f; ledgePos[2] = nextPos[2] - wall->normal.z * 60.0f; - ledgePos[1] = find_floor(ledgePos[0], nextPos[1] + 160.0f, ledgePos[2], &ledgeFloor); + ledgePos[1] = find_floor(ledgePos[0], nextPos[1] + 160.0f, ledgePos[2], ledgeFloor); if (ledgePos[1] - nextPos[1] <= 100.0f) { - return FALSE; + return grabbedWall; } + return wall; +} - vec3f_copy(m->pos, ledgePos); - m->floor = ledgeFloor; - m->floorHeight = ledgePos[1]; +s32 bonk_or_hit_lava_wall(struct MarioState *m, struct WallCollisionData *wallData) { + s16 i; + s16 wallDYaw; + s32 oldWallDYaw; + s32 absWallDYaw; + s32 result; + result = AIR_STEP_NONE; - m->floorAngle = atan2s(ledgeFloor->normal.z, ledgeFloor->normal.x); + if (m->wall != NULL) { + oldWallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + oldWallDYaw = oldWallDYaw < 0 ? -oldWallDYaw : oldWallDYaw; + } + else + oldWallDYaw = 0; - m->faceAngle[0] = 0; - m->faceAngle[1] = atan2s(wall->normal.z, wall->normal.x) + 0x8000; - return TRUE; + for (i = 0; i < wallData->numWalls; i++) { + if (wallData->walls[i] != NULL) { + wallDYaw = atan2s(wallData->walls[i]->normal.z, wallData->walls[i]->normal.x) - m->faceAngle[1]; + if (wallData->walls[i]->type == SURFACE_BURNING) { + m->wall = wallData->walls[i]; + return AIR_STEP_HIT_LAVA_WALL; + } + + //Update wall reference (bonked wall) only if the new wall has a better facing angle + absWallDYaw = wallDYaw < 0 ? -wallDYaw : wallDYaw; + if (absWallDYaw > oldWallDYaw) { + oldWallDYaw = absWallDYaw; + m->wall = wallData->walls[i]; + + if (wallDYaw < -0x6000 || wallDYaw > 0x6000) { + m->flags |= MARIO_UNKNOWN_30; + result = AIR_STEP_HIT_WALL; + } + } + + + } + } + return result; } s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepArg) { - s16 wallDYaw; + s16 i; + s32 stepResult; + Vec3f nextPos; - struct Surface *upperWall; - struct Surface *lowerWall; + Vec3f ledgePos; + struct WallCollisionData upperWall; + struct WallCollisionData lowerWall; struct Surface *ceil; struct Surface *floor; + struct Surface *grabbedWall; + struct Surface *ledgeFloor; f32 ceilHeight; f32 floorHeight; f32 waterLevel; + grabbedWall = NULL; + stepResult = AIR_STEP_NONE; + vec3f_copy(nextPos, intendedPos); - upperWall = resolve_and_return_wall_collisions(nextPos, 150.0f, 50.0f); - lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f); + resolve_and_return_wall_collisions(nextPos, 150.0f, 50.0f, &upperWall); + resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f, &lowerWall); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); + ceilHeight = find_ceil(nextPos[0], nextPos[1] + 3.0f, nextPos[2], &ceil); waterLevel = find_water_level(nextPos[0], nextPos[2]); @@ -469,44 +528,44 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr } m->pos[1] = nextPos[1]; - return AIR_STEP_HIT_WALL; + return AIR_STEP_HIT_CEILING; } //! When the wall is not completely vertical or there is a slight wall // misalignment, you can activate these conditions in unexpected situations - if ((stepArg & AIR_STEP_CHECK_LEDGE_GRAB) && upperWall == NULL && lowerWall != NULL) { - if (check_ledge_grab(m, lowerWall, intendedPos, nextPos)) { - return AIR_STEP_GRABBED_LEDGE; - } - vec3f_copy(m->pos, nextPos); - m->floor = floor; - m->floorHeight = floorHeight; - return AIR_STEP_NONE; + if ((stepArg & AIR_STEP_CHECK_LEDGE_GRAB) && upperWall.numWalls == 0) { + for (i = 0; i < lowerWall.numWalls; i++) + if ((grabbedWall = check_ledge_grab(m, grabbedWall, lowerWall.walls[i], intendedPos, nextPos, ledgePos, &ledgeFloor))) + stepResult = AIR_STEP_GRABBED_LEDGE; + if (stepResult == AIR_STEP_GRABBED_LEDGE) + { + vec3f_copy(m->pos, ledgePos); + m->floor = ledgeFloor; + m->floorHeight = ledgePos[1]; + + m->floorAngle = atan2s(ledgeFloor->normal.z, ledgeFloor->normal.x); + + m->faceAngle[0] = 0; + m->faceAngle[1] = atan2s(grabbedWall->normal.z, grabbedWall->normal.x) + 0x8000; + } + else { + vec3f_copy(m->pos, nextPos); + m->floor = floor; + m->floorHeight = floorHeight; + } + return stepResult; } vec3f_copy(m->pos, nextPos); m->floor = floor; m->floorHeight = floorHeight; - if (upperWall != NULL || lowerWall != NULL) { - m->wall = upperWall != NULL ? upperWall : lowerWall; - wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + stepResult = bonk_or_hit_lava_wall(m, &upperWall); + if (stepResult != AIR_STEP_NONE) + return stepResult; - if (m->wall->type == SURFACE_BURNING) { - return AIR_STEP_HIT_LAVA_WALL; - } - #ifdef WALLKICKS_46_DEGREES - if (wallDYaw < -0x5F00 || wallDYaw > 0x5700) { - #else - if (wallDYaw < -0x6000 || wallDYaw > 0x6000) { - #endif - m->flags |= MARIO_UNKNOWN_30; - return AIR_STEP_HIT_WALL; - } - } - - return AIR_STEP_NONE; + return bonk_or_hit_lava_wall(m, &lowerWall); } void apply_twirl_gravity(struct MarioState *m) { @@ -550,7 +609,7 @@ void apply_gravity(struct MarioState *m) { m->vel[1] = -75.0f; } } else if (m->action == ACT_LONG_JUMP || m->action == ACT_SLIDE_KICK - || m->action == ACT_BBH_ENTER_SPIN) { + || m->action == ACT_BBH_ENTER_SPIN) { m->vel[1] -= 2.0f; if (m->vel[1] < -75.0f) { m->vel[1] = -75.0f; @@ -617,7 +676,9 @@ void apply_vertical_wind(struct MarioState *m) { } s32 perform_air_step(struct MarioState *m, u32 stepArg) { + // s16 wallDYaw; Vec3f intendedPos; + const f32 numSteps = 4.0f; /* max(4.0f, (s32)(sqrtf(m->vel[0] * m->vel[0] + m->vel[1] * m->vel[1] + m->vel[2] * m->vel[2]) / 50.0f));*/ s32 i; s32 quarterStepResult; s32 stepResult = AIR_STEP_NONE; @@ -625,16 +686,12 @@ s32 perform_air_step(struct MarioState *m, u32 stepArg) { m->wall = NULL; for (i = 0; i < 4; i++) { - intendedPos[0] = m->pos[0] + m->vel[0] / 4.0f; - intendedPos[1] = m->pos[1] + m->vel[1] / 4.0f; - intendedPos[2] = m->pos[2] + m->vel[2] / 4.0f; + intendedPos[0] = m->pos[0] + m->vel[0] / numSteps; + intendedPos[1] = m->pos[1] + m->vel[1] / numSteps; + intendedPos[2] = m->pos[2] + m->vel[2] / numSteps; quarterStepResult = perform_air_quarter_step(m, intendedPos, stepArg); - //! On one qf, hit OOB/ceil/wall to store the 2 return value, and continue - // getting 0s until your last qf. Graze a wall on your last qf, and it will - // return the stored 2 with a sharply angled reference wall. (some gwks) - if (quarterStepResult != AIR_STEP_NONE) { stepResult = quarterStepResult; } @@ -660,6 +717,15 @@ s32 perform_air_step(struct MarioState *m, u32 stepArg) { vec3f_copy(m->marioObj->header.gfx.pos, m->pos); vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + /*if (stepResult == AIR_STEP_HIT_WALL && m->wall != NULL) { + wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + if ((stepArg & AIR_STEP_CHECK_BONK) && (wallDYaw < -0x6000 || wallDYaw > 0x6000)) + { + if (m->forwardVel > 16.0f) + mario_bonk_reflection(m, (stepArg & AIR_STEP_BONK_NEGATE_SPEED), m->wall); + } + }*/ + return stepResult; } diff --git a/src/game/puppycam2.c b/src/game/puppycam2.c index f73f1a1d..6bb81321 100644 --- a/src/game/puppycam2.c +++ b/src/game/puppycam2.c @@ -1446,6 +1446,7 @@ static void puppycam_script(void) //Handles collision detection using ray casting. static void puppycam_collision(void) { + struct WallCollisionData wall0, wall1; struct Surface *surf[2]; Vec3f camdir[2]; Vec3f hitpos[2]; @@ -1475,8 +1476,8 @@ static void puppycam_collision(void) find_surface_on_ray(target[0], camdir[0], &surf[0], hitpos[0], RAYCAST_FIND_FLOOR | RAYCAST_FIND_CEIL | RAYCAST_FIND_WALL); find_surface_on_ray(target[1], camdir[1], &surf[1], hitpos[1], RAYCAST_FIND_FLOOR | RAYCAST_FIND_CEIL | RAYCAST_FIND_WALL); - resolve_and_return_wall_collisions(hitpos[0], 0.0f, 25.0f); - resolve_and_return_wall_collisions(hitpos[1], 0.0f, 25.0f); + resolve_and_return_wall_collisions(hitpos[0], 0.0f, 25.0f, &wall0); + resolve_and_return_wall_collisions(hitpos[1], 0.0f, 25.0f, &wall1); dist[0] = ((target[0][0] - hitpos[0][0]) * (target[0][0] - hitpos[0][0]) + (target[0][1] - hitpos[0][1]) * (target[0][1] - hitpos[0][1]) + (target[0][2] - hitpos[0][2]) * (target[0][2] - hitpos[0][2])); dist[1] = ((target[1][0] - hitpos[1][0]) * (target[1][0] - hitpos[1][0]) + (target[1][1] - hitpos[1][1]) * (target[1][1] - hitpos[1][1]) + (target[1][2] - hitpos[1][2]) * (target[1][2] - hitpos[1][2]));