Consider all walls when doing quarter steps (fixCollisionBugs)

This commit is contained in:
MysterD 2022-05-31 08:44:18 -07:00
parent 4add33fa19
commit 57b507ba1d
11 changed files with 120 additions and 66 deletions

View File

@ -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

View File

@ -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:](#)

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,
// multiple walls can push mario more than is required.
// <Fixed when gServerSettings.fixCollisionBugs != 0>
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.
// <Fixed when gServerSettings.fixCollisionBugs != 0>
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;

View File

@ -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)
// <Fixed when gServerSettings.fixCollisionBugs != 0>
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;

View File

@ -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);
}

View File

@ -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);

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);
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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}
}

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) {
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]);