From 9fd309de56411976d9776e05e7d8dd814917370d Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 31 May 2022 01:29:48 -0700 Subject: [PATCH] Added server setting toggle for 'surface cucking' and 'exposed ceilings' vanilla bugs --- autogen/lua_definitions/structs.lua | 1 + docs/lua/structs.md | 1 + src/engine/surface_collision.c | 45 ++++++++++++++++++++++---- src/engine/surface_load.c | 7 +++- src/game/mario.c | 14 +++++++- src/game/mario.h | 1 + src/game/mario_actions_automatic.c | 4 +-- src/game/mario_actions_submerged.c | 2 +- src/game/mario_step.c | 4 +-- src/pc/configfile.c | 2 ++ src/pc/configfile.h | 1 + src/pc/djui/djui_panel_host_settings.c | 8 +++-- src/pc/lua/smlua_cobject_autogen.c | 3 +- src/pc/lua/smlua_functions_autogen.c | 26 +++++++++++++++ src/pc/network/network.c | 2 ++ src/pc/network/network.h | 1 + src/pc/network/packets/packet_join.c | 2 ++ 17 files changed, 107 insertions(+), 17 deletions(-) diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 028c0e7e0..1702b5696 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -1573,6 +1573,7 @@ --- @class ServerSettings --- @field public bubbleDeath integer --- @field public enableCheats integer +--- @field public fixCollisionBugs integer --- @field public headlessServer integer --- @field public playerInteractions PlayerInteractions --- @field public playerKnockbackStrength integer diff --git a/docs/lua/structs.md b/docs/lua/structs.md index 46173ce09..4b64d5e98 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -1979,6 +1979,7 @@ | ----- | ---- | ------ | | bubbleDeath | `integer` | | | enableCheats | `integer` | | +| fixCollisionBugs | `integer` | | | headlessServer | `integer` | | | playerInteractions | [enum PlayerInteractions](constants.md#enum-PlayerInteractions) | | | playerKnockbackStrength | `integer` | | diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 39c37a4a5..365236181 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -10,6 +10,7 @@ #include "math_util.h" #include "game/game_init.h" #include "pc/utils/misc.h" +#include "pc/network/network.h" /************************************************** * WALLS * @@ -240,6 +241,11 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 ceil = NULL; + // set pheight to highest value + if (gServerSettings.fixCollisionBugs) { + *pheight = CELL_HEIGHT_LIMIT; + } + // Stay in this loop until out of ceilings. while (surfaceNode != NULL) { surf = surfaceNode->surface; @@ -284,29 +290,39 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 f32 height; // If a wall, ignore it. Likely a remnant, should never occur. - if (ny == 0.0f) { - continue; - } + if (ny == 0.0f) { continue; } // Find the ceil height at the specific point. height = -(x * nx + nz * z + oo) / ny; + // Reject ceilings below previously found ceiling + if (gServerSettings.fixCollisionBugs && (height > *pheight)) { + continue; + } + // Checks for ceiling interaction with a 78 unit buffer. //! (Exposed Ceilings) Because any point above a ceiling counts // as interacting with a ceiling, ceilings far below can cause // "invisible walls" that are really just exposed ceilings. - if (y - (height - -78.0f) > 0.0f) { - continue; + // + if (gServerSettings.fixCollisionBugs) { + if (y > height) { continue; } + } else { + if (y - (height - -78.0f) > 0.0f) { continue; } } *pheight = height; ceil = surf; - break; + + if (!gServerSettings.fixCollisionBugs) { + break; + } } } //! (Surface Cucking) Since only the first ceil is returned and not the lowest, // lower ceilings can be "cucked" by higher ceilings. + // return ceil; } @@ -428,6 +444,11 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 struct Surface *floor = NULL; s32 interpolate; + // set pheight to lowest value + if (gServerSettings.fixCollisionBugs) { + *pheight = FLOOR_LOWER_LIMIT; + } + // Iterate through the list of floors until there are no more floors. while (surfaceNode != NULL) { surf = surfaceNode->surface; @@ -524,6 +545,12 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 // Find the height of the floor at a given location. height = -(x * nx + nz * z + oo) / ny; + + // Find highest floor + if (gServerSettings.fixCollisionBugs && (height < *pheight)) { + continue; + } + // Checks for floor interaction with a 78 unit buffer. if (y - (height + -78.0f) < 0.0f) { continue; @@ -544,11 +571,15 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 } floor = surf; - break; + + if (!gServerSettings.fixCollisionBugs) { + break; + } } //! (Surface Cucking) Since only the first floor is returned and not the highest, // higher floors can be "cucked" by lower floors. + // return floor; } diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index 2c5bbddf6..55c3ded9d 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -17,6 +17,7 @@ #include "game/game_init.h" #include "engine/math_util.h" #include "game/level_update.h" +#include "pc/network/network.h" s32 unused8038BE90; @@ -151,7 +152,11 @@ static void add_surface_to_cell(s16 dynamic, s16 cellX, s16 cellZ, struct Surfac // many functions only use the first triangle in surface order that fits, // missing higher surfaces. // upperY would be a better sort method. - surfacePriority = surface->vertex1[1] * sortDir; + // + + surfacePriority = gServerSettings.fixCollisionBugs + ? (surface->upperY * sortDir) + : (surface->vertex1[1] * sortDir); newNode->surface = surface; diff --git a/src/game/mario.c b/src/game/mario.c index c467bd4fc..926185c2f 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -623,6 +623,18 @@ f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil) { return find_ceil(pos[0], height + 80.0f, pos[2], ceil); } +/** + * Finds the ceiling from a vec3f horizontally and a height (with 80 vertical buffer). + * Prevents exposed ceiling bug + */ +// Prevent exposed ceilings +f32 vec3f_mario_ceil(Vec3f pos, f32 height, struct Surface **ceil) { + if (gServerSettings.fixCollisionBugs) { + height = MAX(height, pos[1]) + 3.0f; + } + return vec3f_find_ceil(pos, height, ceil); +} + /** * Determines if Mario is facing "downhill." */ @@ -1434,7 +1446,7 @@ copyPlayerGoto:; m->floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &m->floor); } - m->ceilHeight = vec3f_find_ceil(&m->pos[0], m->floorHeight, &m->ceil); + m->ceilHeight = vec3f_mario_ceil(&m->pos[0], m->floorHeight, &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 5840e0a9d..5435be705 100644 --- a/src/game/mario.h +++ b/src/game/mario.h @@ -34,6 +34,7 @@ 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); +f32 vec3f_mario_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 e56eb8fd5..59f952712 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -97,7 +97,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 = vec3f_mario_ceil(m->pos, m->pos[1], &ceil); if (m->pos[1] > ceilHeight - 160.0f) { m->pos[1] = ceilHeight - 160.0f; marioObj->oMarioPolePos = m->pos[1] - m->usedObj->oPosY; @@ -333,7 +333,7 @@ s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) { 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, floorHeight, &ceil); + ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); if (floor == NULL) { return HANG_HIT_CEIL_OR_OOB; diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index e9eaaba1b..6aa863b1d 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -89,7 +89,7 @@ u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) { wall = resolve_and_return_wall_collisions(nextPos, 10.0f, 110.0f); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); if (floor == NULL) { return WATER_STEP_CANCELLED; diff --git a/src/game/mario_step.c b/src/game/mario_step.c index 1e0cebaee..6fb6c20a7 100644 --- a/src/game/mario_step.c +++ b/src/game/mario_step.c @@ -270,7 +270,7 @@ static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) { upperWall = resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); waterLevel = find_water_level(nextPos[0], nextPos[2]); @@ -406,7 +406,7 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + ceilHeight = vec3f_mario_ceil(nextPos, floorHeight, &ceil); waterLevel = find_water_level(nextPos[0], nextPos[2]); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 7712c14c1..0a6fd7844 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -116,6 +116,7 @@ bool configSkipIntro = 0; bool configShareLives = 0; bool configEnableCheats = 0; bool configBubbleDeath = true; +bool configFixCollBugs = true; unsigned int configAmountofPlayers = 16; bool configHUD = true; #ifdef DISCORDRPC @@ -204,6 +205,7 @@ static const struct ConfigOption options[] = { {.name = "frame_limit" , .type = CONFIG_TYPE_UINT , .uintValue = &configFrameLimit}, {.name = "amount_of_players", .type = CONFIG_TYPE_UINT , .uintValue = &configAmountofPlayers}, {.name = "bubble_death", .type = CONFIG_TYPE_BOOL , .boolValue = &configBubbleDeath}, + {.name = "fix_collision_bugs", .type = CONFIG_TYPE_BOOL , .boolValue = &configFixCollBugs}, {.name = "coop_draw_distance", .type = CONFIG_TYPE_UINT , .uintValue = &configDrawDistance}, {.name = "coop_host_port", .type = CONFIG_TYPE_UINT , .uintValue = &configHostPort}, {.name = "coop_host_save_slot", .type = CONFIG_TYPE_UINT , .uintValue = &configHostSaveSlot}, diff --git a/src/pc/configfile.h b/src/pc/configfile.h index 77ed5a0d3..fa7e37626 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -72,6 +72,7 @@ extern bool configSkipIntro; extern bool configShareLives; extern bool configEnableCheats; extern bool configBubbleDeath; +extern bool configFixCollBugs; extern unsigned int configAmountofPlayers; #ifdef DISCORDRPC extern bool configDiscordRPC; diff --git a/src/pc/djui/djui_panel_host_settings.c b/src/pc/djui/djui_panel_host_settings.c index 5057f292d..13dfacc5f 100644 --- a/src/pc/djui/djui_panel_host_settings.c +++ b/src/pc/djui/djui_panel_host_settings.c @@ -43,7 +43,7 @@ static void djui_panel_host_player_text_change(struct DjuiBase* caller) { } void djui_panel_host_settings_create(struct DjuiBase* caller) { - f32 bodyHeight = 32 * 8 + 64 * 1 + 16 * 8; + f32 bodyHeight = 32 * 9 + 64 * 1 + 16 * 9; struct DjuiBase* defaultBase = NULL; struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\S\\#1be700\\E\\#00b3ff\\T\\#ffef00\\T\\#ff0800\\I\\#1be700\\N\\#00b3ff\\G\\#ffef00\\S"); @@ -83,7 +83,11 @@ void djui_panel_host_settings_create(struct DjuiBase* caller) { struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Bubble on death", &configBubbleDeath); djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size(&checkbox5->base, 1.0f, 32); - + + struct DjuiCheckbox* checkbox6 = djui_checkbox_create(&body->base, "Fix collision bugs", &configFixCollBugs); + djui_base_set_size_type(&checkbox6->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&checkbox6->base, 1.0f, 32); + struct DjuiRect* rect1 = djui_rect_create(&body->base); djui_base_set_size_type(&rect1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size(&rect1->base, 1.0f, 32); diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index 03a5de2fd..6d7229371 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -1729,10 +1729,11 @@ static struct LuaObjectField sRayIntersectionInfoFields[LUA_RAY_INTERSECTION_INF { "surface", LVT_COBJECT_P, offsetof(struct RayIntersectionInfo, surface), false, LOT_SURFACE }, }; -#define LUA_SERVER_SETTINGS_FIELD_COUNT 8 +#define LUA_SERVER_SETTINGS_FIELD_COUNT 9 static struct LuaObjectField sServerSettingsFields[LUA_SERVER_SETTINGS_FIELD_COUNT] = { { "bubbleDeath", LVT_U8, offsetof(struct ServerSettings, bubbleDeath), false, LOT_NONE }, { "enableCheats", LVT_U8, offsetof(struct ServerSettings, enableCheats), false, LOT_NONE }, + { "fixCollisionBugs", LVT_U8, offsetof(struct ServerSettings, fixCollisionBugs), false, LOT_NONE }, { "headlessServer", LVT_U8, offsetof(struct ServerSettings, headlessServer), false, LOT_NONE }, { "playerInteractions", LVT_S32, offsetof(struct ServerSettings, playerInteractions), false, LOT_NONE }, { "playerKnockbackStrength", LVT_U8, offsetof(struct ServerSettings, playerKnockbackStrength), false, LOT_NONE }, diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 8de909d0a..9b0e99239 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -8606,6 +8606,31 @@ int smlua_func_vec3f_find_ceil(lua_State* L) { return 1; } +*/ + +/* +int smlua_func_vec3f_mario_ceil(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 3)) { return 0; } + + + f32* pos = smlua_get_vec3f_from_buffer(); + pos[0] = smlua_get_number_field(1, "x"); + pos[1] = smlua_get_number_field(1, "y"); + pos[2] = smlua_get_number_field(1, "z"); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + f32 height = smlua_to_number(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } +// struct Surface** ceil = (struct Surface**)smlua_to_cobject(L, 3, LOT_???); <--- UNIMPLEMENTED + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + + lua_pushnumber(L, vec3f_mario_ceil(pos, height, ceil)); + + smlua_push_number_field(1, "x", pos[0]); + smlua_push_number_field(1, "y", pos[1]); + smlua_push_number_field(1, "z", pos[2]); + + return 1; +} */ ////////////////////////////// @@ -17281,6 +17306,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "update_mario_pos_for_anim", smlua_func_update_mario_pos_for_anim); smlua_bind_function(L, "update_mario_sound_and_camera", smlua_func_update_mario_sound_and_camera); //smlua_bind_function(L, "vec3f_find_ceil", smlua_func_vec3f_find_ceil); <--- UNIMPLEMENTED + //smlua_bind_function(L, "vec3f_mario_ceil", smlua_func_vec3f_mario_ceil); <--- UNIMPLEMENTED // mario_actions_airborne.c smlua_bind_function(L, "check_common_airborne_cancels", smlua_func_check_common_airborne_cancels); diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 44839b6e1..03f456e5f 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -56,6 +56,7 @@ struct ServerSettings gServerSettings = { .enableCheats = 0, .bubbleDeath = 1, .headlessServer = 0, + .fixCollisionBugs = 1, }; void network_set_system(enum NetworkSystemType nsType) { @@ -91,6 +92,7 @@ bool network_init(enum NetworkType inNetworkType) { gServerSettings.shareLives = configShareLives; gServerSettings.enableCheats = configEnableCheats; gServerSettings.bubbleDeath = configBubbleDeath; + gServerSettings.fixCollisionBugs = configFixCollBugs; #if defined(RAPI_DUMMY) || defined(WAPI_DUMMY) gServerSettings.headlessServer = (inNetworkType == NT_SERVER); #else diff --git a/src/pc/network/network.h b/src/pc/network/network.h index d0fe5d11d..a6f32dab2 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -99,6 +99,7 @@ struct ServerSettings { u8 enableCheats; u8 bubbleDeath; u8 headlessServer; + u8 fixCollisionBugs; }; // Networking-specific externs diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index b464ba4a4..549187c1c 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -107,6 +107,7 @@ void network_send_join(struct Packet* joinRequestPacket) { packet_write(&p, &gServerSettings.shareLives, sizeof(u8)); packet_write(&p, &gServerSettings.enableCheats, sizeof(u8)); packet_write(&p, &gServerSettings.bubbleDeath, sizeof(u8)); + packet_write(&p, &gServerSettings.fixCollisionBugs, sizeof(u8)); packet_write(&p, &gServerSettings.headlessServer, sizeof(u8)); packet_write(&p, eeprom, sizeof(u8) * 512); @@ -170,6 +171,7 @@ void network_receive_join(struct Packet* p) { packet_read(p, &gServerSettings.shareLives, sizeof(u8)); packet_read(p, &gServerSettings.enableCheats, sizeof(u8)); packet_read(p, &gServerSettings.bubbleDeath, sizeof(u8)); + packet_read(p, &gServerSettings.fixCollisionBugs, sizeof(u8)); packet_read(p, &gServerSettings.headlessServer, sizeof(u8)); packet_read(p, eeprom, sizeof(u8) * 512); packet_read(p, &modCount, sizeof(u8));