Merge remote-tracking branch 'thecozies/water-surface-type'

This commit is contained in:
Reonu
2021-04-27 11:52:36 +01:00
16 changed files with 348 additions and 47 deletions

View File

@@ -14,6 +14,7 @@
* linker script syntax.
*/
#define USE_EXT_RAM
#ifndef USE_EXT_RAM
#define RAM_END 0x80400000
#else

View File

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

View File

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

View File

@@ -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++) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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