From 57b507ba1d7b35f87f11b8b2daa9900340ecbf2c Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 31 May 2022 08:44:18 -0700 Subject: [PATCH] Consider all walls when doing quarter steps (fixCollisionBugs) --- autogen/lua_definitions/functions.lua | 5 +- docs/lua/functions-3.md | 7 +- src/engine/surface_collision.c | 11 +-- src/game/interaction.c | 8 ++- src/game/mario.c | 31 ++++----- src/game/mario.h | 3 +- src/game/mario_actions_automatic.c | 7 +- src/game/mario_actions_cutscene.c | 3 +- src/game/mario_actions_submerged.c | 6 +- src/game/mario_step.c | 99 +++++++++++++++++++-------- src/pc/lua/smlua_functions_autogen.c | 6 +- 11 files changed, 120 insertions(+), 66 deletions(-) diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 6bd70d75..fca7f5be 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -4182,8 +4182,9 @@ end --- @param pos Vec3f --- @param offset number --- @param radius number ---- @return Surface -function resolve_and_return_wall_collisions(pos, offset, radius) +--- @param collisionData WallCollisionData +--- @return nil +function resolve_and_return_wall_collisions(pos, offset, radius, collisionData) -- ... end diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index f2742567..b81657c1 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -3915,7 +3915,7 @@ ## [resolve_and_return_wall_collisions](#resolve_and_return_wall_collisions) ### Lua Example -`local SurfaceValue = resolve_and_return_wall_collisions(pos, offset, radius)` +`resolve_and_return_wall_collisions(pos, offset, radius, collisionData)` ### Parameters | Field | Type | @@ -3923,12 +3923,13 @@ | pos | [Vec3f](structs.md#Vec3f) | | offset | `number` | | radius | `number` | +| collisionData | [WallCollisionData](structs.md#WallCollisionData) | ### Returns -[Surface](structs.md#Surface) +- None ### C Prototype -`struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius);` +`void resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius, struct WallCollisionData* collisionData);` [:arrow_up_small:](#) diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 36523618..4a6fa962 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -146,8 +146,13 @@ 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); + if (gServerSettings.fixCollisionBugs) { + x = data->x; + z = data->z; + } //! (Unreferenced Walls) Since this only returns the first four walls, // this can lead to wall interaction being missed. Typically unreferenced walls @@ -305,10 +310,8 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 // as interacting with a ceiling, ceilings far below can cause // "invisible walls" that are really just exposed ceilings. // - if (gServerSettings.fixCollisionBugs) { - if (y > height) { continue; } - } else { - if (y - (height - -78.0f) > 0.0f) { continue; } + if (y - (height - -78.0f) > 0.0f) { + continue; } *pheight = height; diff --git a/src/game/interaction.c b/src/game/interaction.c index d51d99ec..4ba3e0cb 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -736,8 +736,12 @@ void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 paddin if (floor != NULL) { //! Doesn't update Mario's referenced floor (allows oob death when // an object pushes you into a steep slope while in a ground action) + // m->pos[0] = newMarioX; m->pos[2] = newMarioZ; + if (gServerSettings.fixCollisionBugs) { + m->floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &m->floor); + } } } } @@ -2138,7 +2142,9 @@ void check_kick_or_punch_wall(struct MarioState *m) { detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]); detector[1] = m->pos[1]; - if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) { + struct WallCollisionData wcd = { 0 }; + resolve_and_return_wall_collisions(detector, 80.0f, 5.0f, &wcd); + if (wcd.numWalls > 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 926185c2..3b8df2b8 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -591,27 +591,20 @@ u32 mario_get_terrain_sound_addend(struct MarioState *m) { /** * 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; +void resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius, struct WallCollisionData* collisionData) { + if (!collisionData || !pos) { return; } - collisionData.x = pos[0]; - collisionData.y = pos[1]; - collisionData.z = pos[2]; - collisionData.radius = radius; - collisionData.offsetY = offset; + 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]; - } + find_wall_collisions(collisionData); - 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; + pos[0] = collisionData->x; + pos[1] = collisionData->y; + pos[2] = collisionData->z; } /** @@ -630,7 +623,7 @@ f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil) { // Prevent exposed ceilings f32 vec3f_mario_ceil(Vec3f pos, f32 height, struct Surface **ceil) { if (gServerSettings.fixCollisionBugs) { - height = MAX(height, pos[1]) + 3.0f; + height = MAX(height, pos[1]); } return vec3f_find_ceil(pos, height, ceil); } diff --git a/src/game/mario.h b/src/game/mario.h index 5435be70..d4573745 100644 --- a/src/game/mario.h +++ b/src/game/mario.h @@ -7,6 +7,7 @@ #include "types.h" extern u16 gLocalBubbleCounter; +struct WallCollisionData; s32 is_anim_at_end(struct MarioState *m); s32 is_anim_past_end(struct MarioState *m); @@ -32,7 +33,7 @@ void mario_set_bubbled(struct MarioState* m); 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(Vec3f pos, f32 offset, f32 radius, struct WallCollisionData* collisionData); f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil); f32 vec3f_mario_ceil(Vec3f pos, f32 height, struct Surface **ceil); s32 mario_facing_downhill(struct MarioState *m, s32 turnYaw); diff --git a/src/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c index 59f95271..b23b7071 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -331,7 +331,12 @@ s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) { smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m); - m->wall = resolve_and_return_wall_collisions(nextPos, 50.0f, 50.0f); + struct WallCollisionData wcd = { 0 }; + resolve_and_return_wall_collisions(nextPos, 50.0f, 50.0f, &wcd); + m->wall = (wcd.numWalls > 0) + ? wcd.walls[wcd.numWalls - 1] + : NULL; + floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 54cf3851..207eb3e5 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -637,7 +637,8 @@ s32 act_debug_free_move(struct MarioState *m) { pos[2] += 26.0f * speed * coss(m->intendedYaw); } - resolve_and_return_wall_collisions(pos, 60.0f, 50.0f); + struct WallCollisionData wcd = { 0 }; + resolve_and_return_wall_collisions(pos, 60.0f, 50.0f, &wcd); struct Surface *surf = NULL; f32 floorHeight = find_floor(pos[0], pos[1], pos[2], &surf); diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index 6aa863b1..3953c0e1 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -81,13 +81,13 @@ static f32 get_buoyancy(struct MarioState *m) { } u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) { - struct Surface *wall; + struct WallCollisionData wcd = { 0 }; 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, &wcd); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); @@ -101,7 +101,7 @@ u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) { m->floor = floor; m->floorHeight = floorHeight; - if (wall != NULL) { + if (wcd.numWalls > 0) { return WATER_STEP_HIT_WALL; } else { return WATER_STEP_NONE; diff --git a/src/game/mario_step.c b/src/game/mario_step.c index 6fb6c20a..583eab0d 100644 --- a/src/game/mario_step.c +++ b/src/game/mario_step.c @@ -258,23 +258,25 @@ 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 lowerWcd = { 0 }; + struct WallCollisionData upperWcd = { 0 }; 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); + resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f, &lowerWcd); + resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f, &upperWcd); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); waterLevel = find_water_level(nextPos[0], nextPos[2]); - m->wall = upperWall; + m->wall = (upperWcd.numWalls > 0) + ? upperWcd.walls[upperWcd.numWalls - 1] + : NULL; if (floor == NULL) { return GROUND_STEP_HIT_WALL_STOP_QSTEPS; @@ -305,17 +307,22 @@ 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 (upperWcd.numWalls > 0) { + for (u8 i = 0; i < upperWcd.numWalls; i++) { + if (!gServerSettings.fixCollisionBugs) { + i = (upperWcd.numWalls - 1); + } + struct Surface* wall = upperWcd.walls[i]; + s16 wallDYaw = atan2s(wall->normal.z, wall->normal.x) - m->faceAngle[1]; - if (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) { - return GROUND_STEP_NONE; + if (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) { + // nothing + } else if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) { + // nothing + } else { + return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS; + } } - if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) { - return GROUND_STEP_NONE; - } - - return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS; } return GROUND_STEP_NONE; @@ -392,8 +399,8 @@ u32 check_ledge_grab(struct MarioState *m, struct Surface *wall, Vec3f intendedP s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepArg) { s16 wallDYaw; Vec3f nextPos; - struct Surface *upperWall; - struct Surface *lowerWall; + struct WallCollisionData lowerWcd = { 0 }; + struct WallCollisionData upperWcd = { 0 }; struct Surface *ceil; struct Surface *floor; f32 ceilHeight; @@ -402,8 +409,8 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr 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, &upperWcd); + resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f, &lowerWcd); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); @@ -472,9 +479,15 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr //! 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) && upperWcd.numWalls == 0 && lowerWcd.numWalls > 0) { + for (u8 i = 0; i < lowerWcd.numWalls; i++) { + if (!gServerSettings.fixCollisionBugs) { + i = (lowerWcd.numWalls - 1); + } + struct Surface* wall = lowerWcd.walls[i]; + if (check_ledge_grab(m, wall, intendedPos, nextPos)) { + return AIR_STEP_GRABBED_LEDGE; + } } vec3f_copy(m->pos, nextPos); @@ -487,17 +500,45 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr 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]; + if (upperWcd.numWalls > 0) { + for (u8 i = 0; i < upperWcd.numWalls; i++) { + if (!gServerSettings.fixCollisionBugs) { + i = (upperWcd.numWalls - 1); + } - if (m->wall->type == SURFACE_BURNING) { - return AIR_STEP_HIT_LAVA_WALL; + struct Surface* wall = upperWcd.walls[i]; + m->wall = wall; + + wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + + 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; + } } + } else if (lowerWcd.numWalls > 0) { + for (u8 i = 0; i < lowerWcd.numWalls; i++) { + if (!gServerSettings.fixCollisionBugs) { + i = (lowerWcd.numWalls - 1); + } - if (wallDYaw < -0x6000 || wallDYaw > 0x6000) { - m->flags |= MARIO_UNKNOWN_30; - return AIR_STEP_HIT_WALL; + struct Surface* wall = lowerWcd.walls[i]; + m->wall = wall; + + wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + + 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; + } } } diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 9b0e9923..e336c4a1 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -8383,7 +8383,7 @@ int smlua_func_play_sound_if_no_flag(lua_State* L) { } int smlua_func_resolve_and_return_wall_collisions(lua_State* L) { - if(!smlua_functions_valid_param_count(L, 3)) { return 0; } + if(!smlua_functions_valid_param_count(L, 4)) { return 0; } f32* pos = smlua_get_vec3f_from_buffer(); @@ -8395,8 +8395,10 @@ int smlua_func_resolve_and_return_wall_collisions(lua_State* L) { if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } f32 radius = smlua_to_number(L, 3); if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + struct WallCollisionData* collisionData = (struct WallCollisionData*)smlua_to_cobject(L, 4, LOT_WALLCOLLISIONDATA); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 4"); return 0; } - smlua_push_object(L, LOT_SURFACE, resolve_and_return_wall_collisions(pos, offset, radius)); + resolve_and_return_wall_collisions(pos, offset, radius, collisionData); smlua_push_number_field(1, "x", pos[0]); smlua_push_number_field(1, "y", pos[1]);