Added water surface type patch

This commit is contained in:
thecozies
2021-03-03 08:05:11 -06:00
parent 545eb58102
commit 571bae013e
11 changed files with 230 additions and 22 deletions

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

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

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

@@ -93,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++;
}
@@ -119,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;

View File

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

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

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