diff --git a/include/segments.h b/include/segments.h index d354f203..dc01b57e 100644 --- a/include/segments.h +++ b/include/segments.h @@ -14,6 +14,7 @@ * linker script syntax. */ +#define USE_EXT_RAM #ifndef USE_EXT_RAM #define RAM_END 0x80400000 #else diff --git a/include/surface_terrains.h b/include/surface_terrains.h index 669a0ac2..29ccb034 100644 --- a/include/surface_terrains.h +++ b/include/surface_terrains.h @@ -4,6 +4,8 @@ // Surface Types #define SURFACE_DEFAULT 0x0000 // Environment default #define SURFACE_BURNING 0x0001 // Lava / Frostbite (in SL), but is used mostly for Lava +#define SURFACE_NEW_WATER 0x0002 // Custom water surface +#define SURFACE_NEW_WATER_BOTTOM 0x0003 // Custom water surface bottom marker #define SURFACE_0004 0x0004 // Unused, has no function and has parameters #define SURFACE_HANGABLE 0x0005 // Ceiling that Mario can climb on #define SURFACE_SLOW 0x0009 // Slow down Mario, unused diff --git a/src/engine/extended_bounds.h b/src/engine/extended_bounds.h new file mode 100644 index 00000000..e80824e0 --- /dev/null +++ b/src/engine/extended_bounds.h @@ -0,0 +1,80 @@ +#ifndef __EXTENDED_BOUNDS_H__ +#define __EXTENDED_BOUNDS_H__ + +/* + Better Extended Bounds by anonymous_moose + Thanks to someone2639 for the shiftable segments patch + Thanks to Wiseguy for the Surface Pool Full error code and 4x bounds fix + + 0: Regular bounds + Same as vanilla sm64, boundaries are (-8192 to 8191) + 16x16 collision cells. + 1: 2x extended bounds + level boundaries are twice as big (-16384 to 16383) + Collision calculations remain as fast as vanilla, at the cost of using more RAM. + 32x32 collision cells. + 2: Regular bounds (performance) + Same boundaries as vanilla (-8192 to 8191), but with twice the amount of collision cells + Trades more RAM usage for faster collision calculations. + 32x32 collision cells. + 3: 4x extended bounds + level boundaries are 4 times as big (-32768 to 32767) + Collision calculations remain as fast as vanilla, at the cost of using far more RAM (16 times vanilla). + 64x64 collision cells. + + + If you see "SURFACE POOL FULL" or "SURFACE NODE POOL FULL" in game, you should increase + SURFACE_POOL_SIZE or SURFACE_NODE_POOL_SIZE, respectively, or reduce the amount of + collision surfaces in your level. +*/ + +//for the static assert macro +#include "macros.h" + +//set this to the extended bounds mode you want, then do "make clean". +#define EXTENDED_BOUNDS_MODE 1 + +//the maximum amount of collision surfaces (static and dynamic combined) +//8200 should work fine for a 2x extended stage, the vanilla value is 2300 +#define SURFACE_POOL_SIZE 4000 + +//make this approximately (amount of collision cells) + (SURFACE_POOL_SIZE * 3) +//22000 should work fine for a 2x extended stage, the vanilla value is 7000 +#define SURFACE_NODE_POOL_SIZE 12000 + + + + + +//don't touch the stuff past this point unless you know what you're doing! + +//default value to check if the user set a proper extended bounds mode +#define LEVEL_BOUNDARY_MAX 0x0000 + +#if EXTENDED_BOUNDS_MODE == 0 + #undef LEVEL_BOUNDARY_MAX // Undefine the old value to avoid compiler warnings + #define LEVEL_BOUNDARY_MAX 0x2000L + #define CELL_SIZE 0x400 +#elif EXTENDED_BOUNDS_MODE == 1 + #undef LEVEL_BOUNDARY_MAX + #define LEVEL_BOUNDARY_MAX 0x4000L + #define CELL_SIZE 0x400 +#elif EXTENDED_BOUNDS_MODE == 2 + #undef LEVEL_BOUNDARY_MAX + #define LEVEL_BOUNDARY_MAX 0x2000L + #define CELL_SIZE 0x200 +#elif EXTENDED_BOUNDS_MODE == 3 + #undef LEVEL_BOUNDARY_MAX + #define LEVEL_BOUNDARY_MAX 0x8000L + #define CELL_SIZE 0x400 +#endif + +STATIC_ASSERT(LEVEL_BOUNDARY_MAX != 0, "You must set a valid extended bounds mode!"); + +#define NUM_CELLS (2 * LEVEL_BOUNDARY_MAX / CELL_SIZE) + +#define NOT_ENOUGH_ROOM_FOR_SURFACES (1 << 0) +#define NOT_ENOUGH_ROOM_FOR_NODES (1 << 1) + + +#endif // __EXTENDED_BOUNDS_H__ diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 9a01f8bb..cab0a35a 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -110,12 +110,12 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, // Determine if checking for the camera or not. if (gCheckingSurfaceCollisionsForCamera) { - if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) { + if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { continue; } } else { // Ignore camera only surfaces. - if (surf->type == SURFACE_CAMERA_BOUNDARY) { + if (surf->type == SURFACE_CAMERA_BOUNDARY || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { continue; } @@ -257,12 +257,12 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 // Determine if checking for the camera or not. if (gCheckingSurfaceCollisionsForCamera != 0) { - if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) { + if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { continue; } } // Ignore camera only surfaces. - else if (surf->type == SURFACE_CAMERA_BOUNDARY) { + else if (surf->type == SURFACE_CAMERA_BOUNDARY || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { continue; } @@ -432,7 +432,7 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 // Determine if we are checking for the camera or not. if (gCheckingSurfaceCollisionsForCamera != 0) { - if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) { + if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION || surf->type == SURFACE_NEW_WATER || surf->type == SURFACE_NEW_WATER_BOTTOM) { continue; } } @@ -468,6 +468,77 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 return floor; } +static s16 check_within_triangle_bounds(s32 x, s32 z, struct Surface *surf) { + register s32 x1, z1, x2, z2, x3, z3; + x1 = surf->vertex1[0]; + z1 = surf->vertex1[2]; + x2 = surf->vertex2[0]; + z2 = surf->vertex2[2]; + + if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) return FALSE; + + x3 = surf->vertex3[0]; + z3 = surf->vertex3[2]; + + if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) return FALSE; + if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) < 0) return FALSE; + + return TRUE; +} + +// Find the height of the floor at a given location +static f32 get_floor_height_at_location(s32 x, s32 z, struct Surface *surf) { + return -(x * surf->normal.x + surf->normal.z * z + surf->originOffset) / surf->normal.y; +} + +/** + * Iterate through the list of water floors and find the first water floor under a given point. + */ +struct Surface *find_water_floor_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, + f32 *pheight) { + register struct Surface *surf; + struct Surface *floor = NULL; + struct SurfaceNode *topSurfaceNode = surfaceNode; + struct SurfaceNode *bottomSurfaceNode = surfaceNode; + f32 height = FLOOR_LOWER_LIMIT; + f32 bottomHeight = FLOOR_LOWER_LIMIT; + + // Iterate through the list of water floors until there are no more water floors. + while (bottomSurfaceNode != NULL) { + f32 curBottomHeight = FLOOR_LOWER_LIMIT; + surf = bottomSurfaceNode->surface; + bottomSurfaceNode = bottomSurfaceNode->next; + + if (surf->type != SURFACE_NEW_WATER_BOTTOM || !check_within_triangle_bounds(x, z, surf)) continue; + + curBottomHeight = get_floor_height_at_location(x, z, surf); + + if (curBottomHeight < y - 78.0f) continue; + if (curBottomHeight >= y - 78.0f) bottomHeight = curBottomHeight; + } + + // Iterate through the list of water tops until there are no more water tops. + while (topSurfaceNode != NULL) { + f32 curHeight = FLOOR_LOWER_LIMIT; + surf = topSurfaceNode->surface; + topSurfaceNode = topSurfaceNode->next; + + if (surf->type == SURFACE_NEW_WATER_BOTTOM || !check_within_triangle_bounds(x, z, surf)) continue; + + curHeight = get_floor_height_at_location(x, z, surf); + + if (bottomHeight != FLOOR_LOWER_LIMIT && curHeight > bottomHeight) continue; + + if (curHeight > height) { + height = curHeight; + *pheight = curHeight; + floor = surf; + } + } + + return floor; +} + /** * Find the height of the highest floor below a point. */ @@ -579,10 +650,93 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { return height; } +/** + * Find the highest water floor under a given position and return the height. + */ +f32 find_water_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) { + s16 cellZ, cellX; + + struct Surface *floor = NULL; + struct SurfaceNode *surfaceList; + + f32 height = FLOOR_LOWER_LIMIT; + + s16 x = (s16) xPos; + s16 y = (s16) yPos; + s16 z = (s16) zPos; + + if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) { + return height; + } + if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) { + return height; + } + + // Each level is split into cells to limit load, find the appropriate cell. + cellX = ((x + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX; + cellZ = ((z + LEVEL_BOUNDARY_MAX) / CELL_SIZE) & NUM_CELLS_INDEX; + + // Check for surfaces that are a part of level geometry. + surfaceList = gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WATER].next; + floor = find_water_floor_from_list(surfaceList, x, y, z, &height); + + if (floor == NULL) { + height = FLOOR_LOWER_LIMIT; + } else { + *pfloor = floor; + } + + return height; +} + /************************************************** * ENVIRONMENTAL BOXES * **************************************************/ +/** + * Finds the height of water at a given location. + */ +f32 find_water_level_and_floor(f32 x, f32 z, struct Surface **pfloor) { + s32 i; + s32 numRegions; + s16 val; + f32 loX, hiX, loZ, hiZ; + f32 waterLevel = FLOOR_LOWER_LIMIT; + s16 *p = gEnvironmentRegions; + struct Surface *floor = NULL; + + if (gCheckingSurfaceCollisionsForCamera) { + waterLevel = find_water_floor(x, gLakituState.pos[1], z, &floor); + } else { + waterLevel = find_water_floor(x, gMarioState->pos[1], z, &floor); + } + + if (p != NULL && waterLevel == FLOOR_LOWER_LIMIT) { + numRegions = *p++; + + for (i = 0; i < numRegions; i++) { + val = *p++; + loX = *p++; + loZ = *p++; + hiX = *p++; + hiZ = *p++; + + // If the location is within a water box and it is a water box. + // Water is less than 50 val only, while above is gas and such. + if (loX < x && x < hiX && loZ < z && z < hiZ && val < 50) { + // Set the water height. Since this breaks, only return the first height. + waterLevel = *p; + break; + } + p++; + } + } else { + *pfloor = floor; + } + + return waterLevel; +} + /** * Finds the height of water at a given location. */ @@ -593,8 +747,15 @@ f32 find_water_level(f32 x, f32 z) { f32 loX, hiX, loZ, hiZ; f32 waterLevel = FLOOR_LOWER_LIMIT; s16 *p = gEnvironmentRegions; + struct Surface *floor; - if (p != NULL) { + if (gCheckingSurfaceCollisionsForCamera) { + waterLevel = find_water_floor(x, gLakituState.pos[1], z, &floor); + } else { + waterLevel = find_water_floor(x, gMarioState->pos[1], z, &floor); + } + + if (p != NULL && waterLevel == FLOOR_LOWER_LIMIT) { numRegions = *p++; for (i = 0; i < numRegions; i++) { diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h index ad5e6406..86540788 100644 --- a/src/engine/surface_collision.h +++ b/src/engine/surface_collision.h @@ -5,10 +5,8 @@ #include "types.h" -// Range level area is 16384x16384 (-8192 to +8192 in x and z) -#define LEVEL_BOUNDARY_MAX 0x2000 // 8192 +#include "engine/extended_bounds.h" -#define CELL_SIZE (1 << 10) // 0x400 #define CELL_HEIGHT_LIMIT 20000 #define FLOOR_LOWER_LIMIT -11000 @@ -42,6 +40,7 @@ f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil); f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometry **floorGeo); f32 find_floor_height(f32 x, f32 y, f32 z); f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor); +f32 find_water_level_and_floor(f32 x, f32 z, struct Surface **pfloor); f32 find_water_level(f32 x, f32 z); f32 find_poison_gas_level(f32 x, f32 z); void debug_surface_list_info(f32 xPos, f32 zPos); diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index b5113901..d2999892 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -37,6 +37,8 @@ s16 sSurfacePoolSize; u8 unused8038EEA8[0x30]; +u8 gSurfacePoolError = 0; + /** * Allocate the part of the surface node pool to contain a surface node. */ @@ -49,7 +51,8 @@ static struct SurfaceNode *alloc_surface_node(void) { //! A bounds check! If there's more surface nodes than 7000 allowed, // we, um... // Perhaps originally just debug feedback? - if (gSurfaceNodesAllocated >= 7000) { + if (gSurfaceNodesAllocated >= SURFACE_NODE_POOL_SIZE) { + gSurfacePoolError |= NOT_ENOUGH_ROOM_FOR_NODES; } return node; @@ -68,6 +71,7 @@ static struct Surface *alloc_surface(void) { // we, um... // Perhaps originally just debug feedback? if (gSurfacesAllocated >= sSurfacePoolSize) { + gSurfacePoolError |= NOT_ENOUGH_ROOM_FOR_SURFACES; } surface->type = 0; @@ -89,6 +93,7 @@ static void clear_spatial_partition(SpatialPartitionCell *cells) { (*cells)[SPATIAL_PARTITION_FLOORS].next = NULL; (*cells)[SPATIAL_PARTITION_CEILS].next = NULL; (*cells)[SPATIAL_PARTITION_WALLS].next = NULL; + (*cells)[SPATIAL_PARTITION_WATER].next = NULL; cells++; } @@ -115,9 +120,10 @@ static void add_surface_to_cell(s16 dynamic, s16 cellX, s16 cellZ, struct Surfac s16 priority; s16 sortDir; s16 listIndex; + s16 isWater = surface->type == SURFACE_NEW_WATER || surface->type == SURFACE_NEW_WATER_BOTTOM; if (surface->normal.y > 0.01) { - listIndex = SPATIAL_PARTITION_FLOORS; + listIndex = isWater ? SPATIAL_PARTITION_WATER : SPATIAL_PARTITION_FLOORS; sortDir = 1; // highest to lowest, then insertion order } else if (surface->normal.y < -0.01) { listIndex = SPATIAL_PARTITION_CEILS; @@ -197,7 +203,7 @@ static s16 max_3(s16 a0, s16 a1, s16 a2) { * time). This function determines the lower cell for a given x/z position. * @param coord The coordinate to test */ -static s16 lower_cell_index(s16 coord) { +static s16 lower_cell_index(s32 coord) { s16 index; // Move from range [-0x2000, 0x2000) to [0, 0x4000) @@ -229,7 +235,7 @@ static s16 lower_cell_index(s16 coord) { * time). This function determines the upper cell for a given x/z position. * @param coord The coordinate to test */ -static s16 upper_cell_index(s16 coord) { +static s16 upper_cell_index(s32 coord) { s16 index; // Move from range [-0x2000, 0x2000) to [0, 0x4000) @@ -524,8 +530,8 @@ static void load_environmental_regions(s16 **data) { * Allocate some of the main pool for surfaces (2300 surf) and for surface nodes (7000 nodes). */ void alloc_surface_pools(void) { - sSurfacePoolSize = 2300; - sSurfaceNodePool = main_pool_alloc(7000 * sizeof(struct SurfaceNode), MEMORY_POOL_LEFT); + sSurfacePoolSize = SURFACE_POOL_SIZE; + sSurfaceNodePool = main_pool_alloc(SURFACE_NODE_POOL_SIZE * sizeof(struct SurfaceNode), MEMORY_POOL_LEFT); sSurfacePool = main_pool_alloc(sSurfacePoolSize * sizeof(struct Surface), MEMORY_POOL_LEFT); gCCMEnteredSlide = 0; diff --git a/src/engine/surface_load.h b/src/engine/surface_load.h index e41a04a4..d947cf80 100644 --- a/src/engine/surface_load.h +++ b/src/engine/surface_load.h @@ -6,6 +6,8 @@ #include "surface_collision.h" #include "types.h" +extern u8 gSurfacePoolError; + #define NUM_CELLS (2 * LEVEL_BOUNDARY_MAX / CELL_SIZE) #define NUM_CELLS_INDEX (NUM_CELLS - 1) @@ -19,10 +21,11 @@ enum { SPATIAL_PARTITION_FLOORS, SPATIAL_PARTITION_CEILS, - SPATIAL_PARTITION_WALLS + SPATIAL_PARTITION_WALLS, + SPATIAL_PARTITION_WATER }; -typedef struct SurfaceNode SpatialPartitionCell[3]; +typedef struct SurfaceNode SpatialPartitionCell[4]; // Needed for bs bss reordering memes. extern s32 unused8038BE90; diff --git a/src/game/camera.c b/src/game/camera.c index 2ae110ce..3c7649c6 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -874,21 +874,21 @@ void pan_ahead_of_player(struct Camera *c) { vec3f_add(c->focus, pan); } -s16 find_in_bounds_yaw_wdw_bob_thi(Vec3f pos, Vec3f origin, s16 yaw) { - switch (gCurrLevelArea) { - case AREA_WDW_MAIN: - yaw = clamp_positions_and_find_yaw(pos, origin, 4508.f, -3739.f, 4508.f, -3739.f); - break; - case AREA_BOB: - yaw = clamp_positions_and_find_yaw(pos, origin, 8000.f, -8000.f, 7050.f, -8000.f); - break; - case AREA_THI_HUGE: - yaw = clamp_positions_and_find_yaw(pos, origin, 8192.f, -8192.f, 8192.f, -8192.f); - break; - case AREA_THI_TINY: - yaw = clamp_positions_and_find_yaw(pos, origin, 2458.f, -2458.f, 2458.f, -2458.f); - break; - } +s16 find_in_bounds_yaw_wdw_bob_thi(UNUSED Vec3f pos, UNUSED Vec3f origin, s16 yaw) { + // switch (gCurrLevelArea) { + // case AREA_WDW_MAIN: + // yaw = clamp_positions_and_find_yaw(pos, origin, 4508.f, -3739.f, 4508.f, -3739.f); + // break; + // case AREA_BOB: + // yaw = clamp_positions_and_find_yaw(pos, origin, 8000.f, -8000.f, 7050.f, -8000.f); + // break; + // case AREA_THI_HUGE: + // yaw = clamp_positions_and_find_yaw(pos, origin, 8192.f, -8192.f, 8192.f, -8192.f); + // break; + // case AREA_THI_TINY: + // yaw = clamp_positions_and_find_yaw(pos, origin, 2458.f, -2458.f, 2458.f, -2458.f); + // break; + // } return yaw; } diff --git a/src/game/hud.c b/src/game/hud.c index 8d4daa54..a190af08 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -13,6 +13,7 @@ #include "area.h" #include "save_file.h" #include "print.h" +#include "engine/surface_load.h" /* @file hud.c * This file implements HUD rendering and power meter animations. @@ -475,5 +476,14 @@ void render_hud(void) { if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) { render_hud_timer(); } + + if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_SURFACES) + { + print_text(10, 40, "SURFACE POOL FULL"); + } + if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_NODES) + { + print_text(10, 60, "SURFACE NODE POOL FULL"); + } } } diff --git a/src/game/mario.c b/src/game/mario.c index 523e51d0..5fda4a8f 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1167,6 +1167,24 @@ s32 transition_submerged_to_walking(struct MarioState *m) { } } +/** + * Transitions Mario from a submerged action to an airborne action. + * You may want to change these actions to fit your hack + */ +s32 transition_submerged_to_airborne(struct MarioState *m) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + + vec3s_set(m->angleVel, 0, 0, 0); + + if (m->heldObj == NULL) { + if (m->input & INPUT_A_DOWN) return set_mario_action(m, ACT_DIVE, 0); + else return set_mario_action(m, ACT_FREEFALL, 0); + } else { + if (m->input & INPUT_A_DOWN) return set_mario_action(m, ACT_HOLD_JUMP, 0); + else return set_mario_action(m, ACT_HOLD_FREEFALL, 0); + } +} + /** * This is the transition function typically for entering a submerged action for a * non-submerged action. This also applies the water surface camera preset. @@ -1175,7 +1193,8 @@ s32 set_water_plunge_action(struct MarioState *m) { m->forwardVel = m->forwardVel / 4.0f; m->vel[1] = m->vel[1] / 2.0f; - m->pos[1] = m->waterLevel - 100; + // !BUG: Causes waterbox upwarp + // m->pos[1] = m->waterLevel - 100; m->faceAngle[2] = 0; diff --git a/src/game/mario.h b/src/game/mario.h index ad0d005a..3b050b42 100644 --- a/src/game/mario.h +++ b/src/game/mario.h @@ -46,6 +46,7 @@ s32 hurt_and_set_mario_action(struct MarioState *m, u32 action, u32 actionArg, s s32 check_common_action_exits(struct MarioState *m); s32 check_common_hold_action_exits(struct MarioState *m); s32 transition_submerged_to_walking(struct MarioState *m); +s32 transition_submerged_to_airborne(struct MarioState *m); s32 set_water_plunge_action(struct MarioState *m); s32 execute_mario_action(UNUSED struct Object *o); void init_mario(void); diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index 31df38b9..e5598e35 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -1497,7 +1497,8 @@ static s32 act_hold_metal_water_fall_land(struct MarioState *m) { static s32 check_common_submerged_cancels(struct MarioState *m) { if (m->pos[1] > m->waterLevel - 80) { if (m->waterLevel - 80 > m->floorHeight) { - m->pos[1] = m->waterLevel - 80; + // m->pos[1] = m->waterLevel - 80; //! BUG: Downwarp swimming out of waterfalls + return transition_submerged_to_airborne(m); } else { //! If you press B to throw the shell, there is a ~5 frame window // where your held object is the shell, but you are not in the diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 0d86000b..d0891f03 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -1840,12 +1840,10 @@ static s32 cur_obj_within_12k_bounds(void) { } void cur_obj_move_using_vel_and_gravity(void) { - if (cur_obj_within_12k_bounds()) { o->oPosX += o->oVelX; o->oPosZ += o->oVelZ; o->oVelY += o->oGravity; //! No terminal velocity o->oPosY += o->oVelY; - } } void cur_obj_move_using_fvel_and_gravity(void) { diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 58238e83..00f1313c 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -696,7 +696,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { gMatStackFixed[gMatStackIndex] = mtx; if (gShadowAboveWaterOrLava == TRUE) { geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4); - } else if (gMarioOnIceOrCarpet == 1) { + } else if (gMarioOnIceOrCarpet == 1 || gShadowAboveCustomWater == 1) { geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5); } else { geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6); diff --git a/src/game/shadow.c b/src/game/shadow.c index e8605075..8a978777 100644 --- a/src/game/shadow.c +++ b/src/game/shadow.c @@ -102,6 +102,7 @@ shadowRectangle rectangles[2] = { // See shadow.h for documentation. s8 gShadowAboveWaterOrLava; +s8 gShadowAboveCustomWater; s8 gMarioOnIceOrCarpet; s8 sMarioOnFlyingCarpet; s16 sSurfaceTypeBelowShadow; @@ -177,14 +178,15 @@ u8 dim_shadow_with_distance(u8 solidity, f32 distFromFloor) { * Return the water level below a shadow, or 0 if the water level is below * -10,000. */ -f32 get_water_level_below_shadow(struct Shadow *s) { - f32 waterLevel = find_water_level(s->parentX, s->parentZ); +f32 get_water_level_below_shadow(struct Shadow *s, struct Surface **waterFloor) { + f32 waterLevel = find_water_level_and_floor(s->parentX, s->parentZ, waterFloor); if (waterLevel < FLOOR_LOWER_LIMIT_SHADOW) { return 0; } else if (s->parentY >= waterLevel && s->floorHeight <= waterLevel) { gShadowAboveWaterOrLava = TRUE; return waterLevel; } + return waterLevel; //! @bug Missing return statement. This compiles to return `waterLevel` //! incidentally. } @@ -201,6 +203,7 @@ s8 init_shadow(struct Shadow *s, f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, f32 waterLevel; f32 floorSteepness; struct FloorGeometry *floorGeometry; + struct Surface *waterFloor = NULL; s->parentX = xPos; s->parentY = yPos; @@ -208,18 +211,33 @@ s8 init_shadow(struct Shadow *s, f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, s->floorHeight = find_floor_height_and_data(s->parentX, s->parentY, s->parentZ, &floorGeometry); - if (gEnvironmentRegions != 0) { - waterLevel = get_water_level_below_shadow(s); - } + waterLevel = get_water_level_below_shadow(s, &waterFloor); + + // if (gEnvironmentRegions != 0) { + // waterLevel = get_water_level_below_shadow(s); + // } + if (gShadowAboveWaterOrLava) { //! @bug Use of potentially undefined variable `waterLevel` s->floorHeight = waterLevel; - // Assume that the water is flat. - s->floorNormalX = 0; - s->floorNormalY = 1.0; - s->floorNormalZ = 0; - s->floorOriginOffset = -waterLevel; + if (waterFloor != NULL) { + s->floorNormalX = waterFloor->normal.x; + s->floorNormalY = waterFloor->normal.y; + s->floorNormalZ = waterFloor->normal.z; + s->floorOriginOffset = waterFloor->originOffset; + gShadowAboveWaterOrLava = FALSE; + gShadowAboveCustomWater = TRUE; + s->solidity = 200; + } else { + gShadowAboveCustomWater = FALSE; + // Assume that the water is flat. + s->floorNormalX = 0; + s->floorNormalY = 1.0; + s->floorNormalZ = 0; + s->floorOriginOffset = -waterLevel; + } + } else { // Don't draw a shadow if the floor is lower than expected possible, // or if the y-normal is negative (an unexpected result). @@ -853,6 +871,7 @@ Gfx *create_shadow_below_xyz(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 s find_floor(xPos, yPos, zPos, &pfloor); gShadowAboveWaterOrLava = FALSE; + gShadowAboveCustomWater = FALSE; gMarioOnIceOrCarpet = 0; sMarioOnFlyingCarpet = 0; if (pfloor != NULL) { diff --git a/src/game/shadow.h b/src/game/shadow.h index 36b3ef06..528659a2 100644 --- a/src/game/shadow.h +++ b/src/game/shadow.h @@ -38,6 +38,7 @@ extern s16 sSurfaceTypeBelowShadow; * Flag for if the current shadow is above water or lava. */ extern s8 gShadowAboveWaterOrLava; +extern s8 gShadowAboveCustomWater; /** * Flag for if Mario is on ice or a flying carpet.