Consider all walls when doing quarter steps (fixCollisionBugs)

This commit is contained in:
MysterD 2022-05-31 08:44:18 -07:00
parent 9fd309de56
commit bd7974a544
11 changed files with 120 additions and 66 deletions

View file

@ -4182,8 +4182,9 @@ end
--- @param pos Vec3f --- @param pos Vec3f
--- @param offset number --- @param offset number
--- @param radius number --- @param radius number
--- @return Surface --- @param collisionData WallCollisionData
function resolve_and_return_wall_collisions(pos, offset, radius) --- @return nil
function resolve_and_return_wall_collisions(pos, offset, radius, collisionData)
-- ... -- ...
end end

View file

@ -3915,7 +3915,7 @@
## [resolve_and_return_wall_collisions](#resolve_and_return_wall_collisions) ## [resolve_and_return_wall_collisions](#resolve_and_return_wall_collisions)
### Lua Example ### Lua Example
`local SurfaceValue = resolve_and_return_wall_collisions(pos, offset, radius)` `resolve_and_return_wall_collisions(pos, offset, radius, collisionData)`
### Parameters ### Parameters
| Field | Type | | Field | Type |
@ -3923,12 +3923,13 @@
| pos | [Vec3f](structs.md#Vec3f) | | pos | [Vec3f](structs.md#Vec3f) |
| offset | `number` | | offset | `number` |
| radius | `number` | | radius | `number` |
| collisionData | [WallCollisionData](structs.md#WallCollisionData) |
### Returns ### Returns
[Surface](structs.md#Surface) - None
### C Prototype ### 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:](#) [:arrow_up_small:](#)

View file

@ -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, //! (Wall Overlaps) Because this doesn't update the x and z local variables,
// multiple walls can push mario more than is required. // multiple walls can push mario more than is required.
// <Fixed when gServerSettings.fixCollisionBugs != 0>
data->x += surf->normal.x * (radius - offset); data->x += surf->normal.x * (radius - offset);
data->z += surf->normal.z * (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, //! (Unreferenced Walls) Since this only returns the first four walls,
// this can lead to wall interaction being missed. Typically unreferenced 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 // as interacting with a ceiling, ceilings far below can cause
// "invisible walls" that are really just exposed ceilings. // "invisible walls" that are really just exposed ceilings.
// <Fixed when gServerSettings.fixCollisionBugs != 0> // <Fixed when gServerSettings.fixCollisionBugs != 0>
if (gServerSettings.fixCollisionBugs) { if (y - (height - -78.0f) > 0.0f) {
if (y > height) { continue; } continue;
} else {
if (y - (height - -78.0f) > 0.0f) { continue; }
} }
*pheight = height; *pheight = height;

View file

@ -736,8 +736,12 @@ void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 paddin
if (floor != NULL) { if (floor != NULL) {
//! Doesn't update Mario's referenced floor (allows oob death when //! Doesn't update Mario's referenced floor (allows oob death when
// an object pushes you into a steep slope while in a ground action) // an object pushes you into a steep slope while in a ground action)
// <Fixed when gServerSettings.fixCollisionBugs != 0>
m->pos[0] = newMarioX; m->pos[0] = newMarioX;
m->pos[2] = newMarioZ; 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[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]);
detector[1] = m->pos[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_MOVE_PUNCHING || m->forwardVel >= 0.0f) {
if (m->action == ACT_PUNCHING) { if (m->action == ACT_PUNCHING) {
m->action = ACT_MOVE_PUNCHING; m->action = ACT_MOVE_PUNCHING;

View file

@ -591,27 +591,20 @@ u32 mario_get_terrain_sound_addend(struct MarioState *m) {
/** /**
* Collides with walls and returns the most recent wall. * Collides with walls and returns the most recent wall.
*/ */
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) {
struct WallCollisionData collisionData; if (!collisionData || !pos) { return; }
struct Surface *wall = NULL;
collisionData.x = pos[0]; collisionData->x = pos[0];
collisionData.y = pos[1]; collisionData->y = pos[1];
collisionData.z = pos[2]; collisionData->z = pos[2];
collisionData.radius = radius; collisionData->radius = radius;
collisionData.offsetY = offset; collisionData->offsetY = offset;
if (find_wall_collisions(&collisionData)) { find_wall_collisions(collisionData);
wall = collisionData.walls[collisionData.numWalls - 1];
}
pos[0] = collisionData.x; pos[0] = collisionData->x;
pos[1] = collisionData.y; pos[1] = collisionData->y;
pos[2] = collisionData.z; pos[2] = collisionData->z;
// This only returns the most recent wall and can also return NULL
// there are no wall collisions.
return wall;
} }
/** /**
@ -630,7 +623,7 @@ f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil) {
// Prevent exposed ceilings // Prevent exposed ceilings
f32 vec3f_mario_ceil(Vec3f pos, f32 height, struct Surface **ceil) { f32 vec3f_mario_ceil(Vec3f pos, f32 height, struct Surface **ceil) {
if (gServerSettings.fixCollisionBugs) { if (gServerSettings.fixCollisionBugs) {
height = MAX(height, pos[1]) + 3.0f; height = MAX(height, pos[1]);
} }
return vec3f_find_ceil(pos, height, ceil); return vec3f_find_ceil(pos, height, ceil);
} }

View file

@ -7,6 +7,7 @@
#include "types.h" #include "types.h"
extern u16 gLocalBubbleCounter; extern u16 gLocalBubbleCounter;
struct WallCollisionData;
s32 is_anim_at_end(struct MarioState *m); s32 is_anim_at_end(struct MarioState *m);
s32 is_anim_past_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); void mario_set_forward_vel(struct MarioState *m, f32 speed);
s32 mario_get_floor_class(struct MarioState *m); s32 mario_get_floor_class(struct MarioState *m);
u32 mario_get_terrain_sound_addend(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_find_ceil(Vec3f pos, f32 height, struct Surface **ceil);
f32 vec3f_mario_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); s32 mario_facing_downhill(struct MarioState *m, s32 turnYaw);

View file

@ -331,7 +331,12 @@ s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) {
smlua_call_event_hooks_mario_param(HOOK_BEFORE_PHYS_STEP, m); 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); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil);

View file

@ -637,7 +637,8 @@ s32 act_debug_free_move(struct MarioState *m) {
pos[2] += 26.0f * speed * coss(m->intendedYaw); 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; struct Surface *surf = NULL;
f32 floorHeight = find_floor(pos[0], pos[1], pos[2], &surf); f32 floorHeight = find_floor(pos[0], pos[1], pos[2], &surf);

View file

@ -81,13 +81,13 @@ static f32 get_buoyancy(struct MarioState *m) {
} }
u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) { u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) {
struct Surface *wall; struct WallCollisionData wcd = { 0 };
struct Surface *ceil; struct Surface *ceil;
struct Surface *floor; struct Surface *floor;
f32 ceilHeight; f32 ceilHeight;
f32 floorHeight; 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); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); 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->floor = floor;
m->floorHeight = floorHeight; m->floorHeight = floorHeight;
if (wall != NULL) { if (wcd.numWalls > 0) {
return WATER_STEP_HIT_WALL; return WATER_STEP_HIT_WALL;
} else { } else {
return WATER_STEP_NONE; return WATER_STEP_NONE;

View file

@ -258,23 +258,25 @@ s32 stationary_ground_step(struct MarioState *m) {
} }
static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) { static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) {
UNUSED struct Surface *lowerWall; struct WallCollisionData lowerWcd = { 0 };
struct Surface *upperWall; struct WallCollisionData upperWcd = { 0 };
struct Surface *ceil; struct Surface *ceil;
struct Surface *floor; struct Surface *floor;
f32 ceilHeight; f32 ceilHeight;
f32 floorHeight; f32 floorHeight;
f32 waterLevel; f32 waterLevel;
lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f); resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f, &lowerWcd);
upperWall = resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f); resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f, &upperWcd);
floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil);
waterLevel = find_water_level(nextPos[0], nextPos[2]); 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) { if (floor == NULL) {
return GROUND_STEP_HIT_WALL_STOP_QSTEPS; 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->floor = floor;
m->floorHeight = floorHeight; m->floorHeight = floorHeight;
if (upperWall != NULL) { if (upperWcd.numWalls > 0) {
s16 wallDYaw = atan2s(upperWall->normal.z, upperWall->normal.x) - m->faceAngle[1]; 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) { if (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) {
return GROUND_STEP_NONE; // 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; 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) { s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepArg) {
s16 wallDYaw; s16 wallDYaw;
Vec3f nextPos; Vec3f nextPos;
struct Surface *upperWall; struct WallCollisionData lowerWcd = { 0 };
struct Surface *lowerWall; struct WallCollisionData upperWcd = { 0 };
struct Surface *ceil; struct Surface *ceil;
struct Surface *floor; struct Surface *floor;
f32 ceilHeight; f32 ceilHeight;
@ -402,8 +409,8 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr
vec3f_copy(nextPos, intendedPos); vec3f_copy(nextPos, intendedPos);
upperWall = resolve_and_return_wall_collisions(nextPos, 150.0f, 50.0f); resolve_and_return_wall_collisions(nextPos, 150.0f, 50.0f, &upperWcd);
lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f); resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f, &lowerWcd);
floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor);
ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); 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 //! When the wall is not completely vertical or there is a slight wall
// misalignment, you can activate these conditions in unexpected situations // misalignment, you can activate these conditions in unexpected situations
if ((stepArg & AIR_STEP_CHECK_LEDGE_GRAB) && upperWall == NULL && lowerWall != NULL) { if ((stepArg & AIR_STEP_CHECK_LEDGE_GRAB) && upperWcd.numWalls == 0 && lowerWcd.numWalls > 0) {
if (check_ledge_grab(m, lowerWall, intendedPos, nextPos)) { for (u8 i = 0; i < lowerWcd.numWalls; i++) {
return AIR_STEP_GRABBED_LEDGE; 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); 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->floor = floor;
m->floorHeight = floorHeight; m->floorHeight = floorHeight;
if (upperWall != NULL || lowerWall != NULL) { if (upperWcd.numWalls > 0) {
m->wall = upperWall != NULL ? upperWall : lowerWall; for (u8 i = 0; i < upperWcd.numWalls; i++) {
wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; if (!gServerSettings.fixCollisionBugs) {
i = (upperWcd.numWalls - 1);
}
if (m->wall->type == SURFACE_BURNING) { struct Surface* wall = upperWcd.walls[i];
return AIR_STEP_HIT_LAVA_WALL; 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) { struct Surface* wall = lowerWcd.walls[i];
m->flags |= MARIO_UNKNOWN_30; m->wall = wall;
return AIR_STEP_HIT_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;
}
} }
} }

View file

@ -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) { 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(); 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; } if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; }
f32 radius = smlua_to_number(L, 3); f32 radius = smlua_to_number(L, 3);
if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } 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, "x", pos[0]);
smlua_push_number_field(1, "y", pos[1]); smlua_push_number_field(1, "y", pos[1]);