diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 8b28e708..bdb73c18 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -18,6 +18,8 @@ */ 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; @@ -27,6 +29,13 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, 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; // Max collision radius = 200 @@ -46,67 +55,67 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset; - if (offset < -radius || offset > radius) { + if (offset < /*-radius*/ 0 || offset > radius) { continue; } - px = x; - pz = z; + // 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]; + // //! (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.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; - } - } - } + // 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) { @@ -137,9 +146,121 @@ 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); + // 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(d00 * d00 + d01 * 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(d00 * d00 + d01 * 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(d00 * d00 + d01 * 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. @@ -150,6 +271,8 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, numCols++; } + data->x = x; + data->z = z; return numCols; } @@ -220,23 +343,43 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { * CEILINGS * **************************************************/ +void add_ceil_margin(s32 *x, s32 *z, Vec3s target1, Vec3s target2, f32 margin) { + register f32 diff_x, diff_z, invDenom; + f32 newX = (f32)*x; + f32 newZ = (f32)*z; + + diff_x = target1[0] - newX + target2[0] - newX; + diff_z = target1[2] - newZ + target2[2] - newZ; + invDenom = margin / sqrtf(diff_x * diff_x + diff_z * 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; struct Surface *ceil = NULL; *pheight = CELL_HEIGHT_LIMIT; + f32 newHeight; + const f32 margin = 1.5f; + + ceil = NULL; + // Stay in this loop until out of ceilings. while (surfaceNode != NULL) { surf = surfaceNode->surface; surfaceNode = surfaceNode->next; 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; @@ -244,6 +387,8 @@ 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; } @@ -274,7 +419,7 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 continue; } // Checks for ceiling interaction - if (y > height) { + if (/*y > height*/ y - (height - -78.0f) > 0.0f) { continue; } if (y >= surf->upperY) { diff --git a/src/game/mario.c b/src/game/mario.c index 30706134..a641b619 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -541,6 +541,20 @@ struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 ra return wall; } +void resolve_and_return_wall_collisions_frame(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; +} + /** * Finds the ceiling from a vec3f horizontally and a height (with 80 vertical buffer). */ diff --git a/src/game/mario.h b/src/game/mario.h index 3b050b42..1367138c 100644 --- a/src/game/mario.h +++ b/src/game/mario.h @@ -29,6 +29,7 @@ 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); +void resolve_and_return_wall_collisions_frame(Vec3f pos, f32 offset, f32 radius, struct WallCollisionData *collisionData); 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); diff --git a/src/game/mario_step.c b/src/game/mario_step.c index 9604febe..54bb40f2 100644 --- a/src/game/mario_step.c +++ b/src/game/mario_step.c @@ -256,23 +256,32 @@ 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 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; + + // lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f); + // upperWall = resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f); + resolve_and_return_wall_collisions_frame(nextPos, 30.0f, 24.0f, &lowerWall); + resolve_and_return_wall_collisions_frame(nextPos, 60.0f, 50.0f, &upperWall); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); waterLevel = find_water_level(nextPos[0], nextPos[2]); - m->wall = upperWall; + //m->wall = upperWall; if (floor == NULL) { return GROUND_STEP_HIT_WALL_STOP_QSTEPS; @@ -303,20 +312,32 @@ 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 (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) { - return GROUND_STEP_NONE; - } - if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) { - return GROUND_STEP_NONE; - } - - return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS; + 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; - return GROUND_STEP_NONE; + 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) { + continue; + } + if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) { + continue; + } + + return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS; + } + + return GROUND_STEP_NONE; } s32 perform_ground_step(struct MarioState *m) { @@ -324,6 +345,7 @@ 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); @@ -345,9 +367,9 @@ 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) { + //struct Surface *ledgeFloor; + //Vec3f ledgePos; f32 displacementX; f32 displacementZ; @@ -355,13 +377,17 @@ 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 @@ -371,35 +397,93 @@ u32 check_ledge_grab(struct MarioState *m, struct Surface *wall, Vec3f intendedP ledgePos[1] = find_floor(ledgePos[0], nextPos[1] + 160.0f, ledgePos[2], &ledgeFloor); if (ledgePos[1] - nextPos[1] <= 100.0f) { - return FALSE; + return grabbedWall; } - vec3f_copy(m->pos, ledgePos); - m->floor = ledgeFloor; - m->floorHeight = ledgePos[1]; + // vec3f_copy(m->pos, ledgePos); + // m->floor = ledgeFloor; + // m->floorHeight = ledgePos[1]; - m->floorAngle = atan2s(ledgeFloor->normal.z, ledgeFloor->normal.x); + // m->floorAngle = atan2s(ledgeFloor->normal.z, ledgeFloor->normal.x); - m->faceAngle[0] = 0; - m->faceAngle[1] = atan2s(wall->normal.z, wall->normal.x) + 0x8000; - return TRUE; + // m->faceAngle[0] = 0; + // m->faceAngle[1] = atan2s(wall->normal.z, wall->normal.x) + 0x8000; + // return TRUE; + + return wall; +} + +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; + + 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 < 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; + // 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); + // 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_frame(nextPos, 150.0f, 50.0f, &upperWall); + resolve_and_return_wall_collisions_frame(nextPos, 30.0f, 50.0f, &lowerWall); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); @@ -463,41 +547,70 @@ 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_WALL; //change to 0 if you dont want ceil bonks } //! 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; - } + // 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; - } + // 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; - } + return bonk_or_hit_lava_wall(m, &lowerWall); - if (wallDYaw < -0x6000 || wallDYaw > 0x6000) { - m->flags |= MARIO_UNKNOWN_30; - return AIR_STEP_HIT_WALL; - } - } + // if (upperWall != NULL || lowerWall != NULL) { + // m->wall = upperWall != NULL ? upperWall : lowerWall; + // wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; - return AIR_STEP_NONE; + // if (m->wall->type == SURFACE_BURNING) { + // return AIR_STEP_HIT_LAVA_WALL; + // } + + // if (wallDYaw < -0x6000 || wallDYaw > 0x6000) { + // m->flags |= MARIO_UNKNOWN_30; + // return AIR_STEP_HIT_WALL; + // } + // } + + // return AIR_STEP_NONE; } void apply_twirl_gravity(struct MarioState *m) {