diff --git a/src/actors/falling_rock/update.inc.c b/src/actors/falling_rock/update.inc.c index f189aeaa1..b700a4226 100644 --- a/src/actors/falling_rock/update.inc.c +++ b/src/actors/falling_rock/update.inc.c @@ -76,7 +76,7 @@ void update_actor_falling_rocks(struct FallingRock* rock) { rock->respawnTimer -= 1; return; } - if (rock->pos[1] < D_8015F8E4) { + if (rock->pos[1] < CM_GetWaterLevel(rock->pos, NULL)) { func_8029CF0C(sRockSpawnData, rock); } rock->rot[0] += (s16) ((rock->velocity[2] * 5461.0f) / 20.0f); diff --git a/src/code_800029B0.c b/src/code_800029B0.c index 18b3c5f63..73f104ed8 100644 --- a/src/code_800029B0.c +++ b/src/code_800029B0.c @@ -144,8 +144,8 @@ Vec3f D_8015F8D0; s32 D_8015F8DC; s32 D_8015F8E0; -f32 D_8015F8E4; -f32 D_8015F8E8; +f32 gWaterLevel; +f32 gWaterVelocity; s16 gPlayerPositionLUT[8]; // Player index at each position u16 gNumPermanentActors; s32 code_800029B0_bss_pad2[44]; @@ -209,7 +209,11 @@ void setup_race(void) { } else { gNextFreeMemoryAddress = gFreeMemoryCourseAnchor; } - func_802969F8(); + + // Cow related + D_8015F702 = 0; + D_8015F700 = 200; + func_80005310(); func_8003D080(); init_hud(); diff --git a/src/code_800029B0.h b/src/code_800029B0.h index a5f590e08..8556248d8 100644 --- a/src/code_800029B0.h +++ b/src/code_800029B0.h @@ -138,8 +138,8 @@ extern Vec3f D_8015F8D0; extern s32 D_8015F8DC; extern s32 D_8015F8E0; -extern f32 D_8015F8E4; -extern f32 D_8015F8E8; +extern f32 gWaterLevel; +extern f32 gWaterVelocity; extern s16 gPlayerPositionLUT[]; // Player index at each position extern u16 gNumPermanentActors; diff --git a/src/ending/code_80281780.c b/src/ending/code_80281780.c index e7c55de1a..1085fb30c 100644 --- a/src/ending/code_80281780.c +++ b/src/ending/code_80281780.c @@ -117,7 +117,7 @@ void setup_podium_ceremony(void) { set_segment_base_addr(6, (void*) decompress_segments((u8*) &_course_banshee_boardwalk_dl_mio0SegmentRomStart, (u8*) &_course_yoshi_valley_dl_mio0SegmentRomStart)); #endif - D_8015F8E4 = -2000.0f; + gWaterLevel = -2000.0f; gCourseMinX = -0x15A1; gCourseMinY = -0x15A1; diff --git a/src/engine/courses/BansheeBoardwalk.cpp b/src/engine/courses/BansheeBoardwalk.cpp index 1ad03c41a..3f2fd6cf9 100644 --- a/src/engine/courses/BansheeBoardwalk.cpp +++ b/src/engine/courses/BansheeBoardwalk.cpp @@ -132,6 +132,8 @@ BansheeBoardwalk::BansheeBoardwalk() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {0, 0, 0}; Props.Sequence = MusicSeq::MUSIC_SEQ_BANSHEE_BOARDWALK; + + Props.WaterLevel = -80.0f; } void BansheeBoardwalk::Load() { @@ -144,7 +146,6 @@ void BansheeBoardwalk::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_banshee_boardwalk_track_sections)); func_80295C6C(); find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07000878), 128, 0, 0, 0); - D_8015F8E4 = -80.0f; } void BansheeBoardwalk::LoadTextures() { @@ -323,8 +324,6 @@ void BansheeBoardwalk::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_banshee_boardwalk_dl_B308)); } -void BansheeBoardwalk::Collision() {} - void BansheeBoardwalk::ScrollingTextures() { D_802B87BC++; diff --git a/src/engine/courses/BansheeBoardwalk.h b/src/engine/courses/BansheeBoardwalk.h index f1b98538d..4d5fc60cc 100644 --- a/src/engine/courses/BansheeBoardwalk.h +++ b/src/engine/courses/BansheeBoardwalk.h @@ -38,7 +38,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void ScrollingTextures() override; virtual void Waypoints(Player*, int8_t) override; virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection) override; diff --git a/src/engine/courses/BigDonut.cpp b/src/engine/courses/BigDonut.cpp index 530cbdf74..bb4300399 100644 --- a/src/engine/courses/BigDonut.cpp +++ b/src/engine/courses/BigDonut.cpp @@ -101,6 +101,8 @@ BigDonut::BigDonut() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {0, 0, 0}; Props.Sequence = MusicSeq::MUSIC_SEQ_BATTLE_ARENAS; + + Props.WaterLevel = 100.0f; } void BigDonut::Load() { @@ -117,7 +119,6 @@ void BigDonut::Load() { // d_course_big_donut_packed_dl_230 generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual((void*)0x07000230), 6); func_80295C6C(); - D_8015F8E4 = 100.0f; } void BigDonut::LoadTextures() { @@ -185,8 +186,6 @@ void BigDonut::Render(struct UnkStruct_800DC5EC* arg0) { void BigDonut::RenderCredits() {} -void BigDonut::Collision() {} - void BigDonut::Waypoints(Player* player, int8_t playerId) { player->nearestWaypointId = 0; } diff --git a/src/engine/courses/BigDonut.h b/src/engine/courses/BigDonut.h index bc076ecf2..23c1497eb 100644 --- a/src/engine/courses/BigDonut.h +++ b/src/engine/courses/BigDonut.h @@ -36,7 +36,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void Destroy() override; }; diff --git a/src/engine/courses/BlockFort.cpp b/src/engine/courses/BlockFort.cpp index a7efc2ddc..08cf02e6a 100644 --- a/src/engine/courses/BlockFort.cpp +++ b/src/engine/courses/BlockFort.cpp @@ -113,7 +113,7 @@ void BlockFort::Load() { generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual((void*)0x070015C0), 1); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void BlockFort::LoadTextures() { @@ -167,8 +167,6 @@ void BlockFort::Render(struct UnkStruct_800DC5EC* arg0) { void BlockFort::RenderCredits() {} -void BlockFort::Collision() {} - void BlockFort::Waypoints(Player* player, int8_t playerId) { player->nearestWaypointId = 0; } diff --git a/src/engine/courses/BlockFort.h b/src/engine/courses/BlockFort.h index c985b9546..092e325a3 100644 --- a/src/engine/courses/BlockFort.h +++ b/src/engine/courses/BlockFort.h @@ -36,7 +36,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Waypoints(Player*, int8_t) override; virtual void Destroy() override; }; diff --git a/src/engine/courses/BowsersCastle.cpp b/src/engine/courses/BowsersCastle.cpp index 0042ec7be..44a757b1f 100644 --- a/src/engine/courses/BowsersCastle.cpp +++ b/src/engine/courses/BowsersCastle.cpp @@ -134,6 +134,9 @@ BowsersCastle::BowsersCastle() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {0, 0, 0}; Props.Sequence = MusicSeq::MUSIC_SEQ_BOWSERS_CASTLE; + + Props.WaterLevel = -50.0f; + WaterVolumes.push_back({20.0f, 1549.0f, 1859.0f, -1402.0f, -1102.0f}); } void BowsersCastle::Load() { @@ -142,7 +145,6 @@ void BowsersCastle::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_bowsers_castle_addr)); func_80295C6C(); find_vtx_and_set_colours(segmented_gfx_to_virtual(reinterpret_cast(0x07001350)), 0x32, 0, 0, 0); - D_8015F8E4 = -50.0f; } void BowsersCastle::LoadTextures() { @@ -321,8 +323,6 @@ void BowsersCastle::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_bowsers_castle_dl_9148)); } -void BowsersCastle::Collision() {} - void BowsersCastle::SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { func_8003E6EC(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } diff --git a/src/engine/courses/BowsersCastle.h b/src/engine/courses/BowsersCastle.h index 767bbdb8c..a8f6b95c8 100644 --- a/src/engine/courses/BowsersCastle.h +++ b/src/engine/courses/BowsersCastle.h @@ -38,7 +38,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) override; virtual void Waypoints(Player*, int8_t) override; virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection); diff --git a/src/engine/courses/ChocoMountain.cpp b/src/engine/courses/ChocoMountain.cpp index 046f8778d..2a8ed9502 100644 --- a/src/engine/courses/ChocoMountain.cpp +++ b/src/engine/courses/ChocoMountain.cpp @@ -125,6 +125,8 @@ ChocoMountain::ChocoMountain() { Props.Skybox.FloorBottomLeft = {255, 255, 255}; Props.Skybox.FloorTopLeft = {255, 255, 255}; Props.Sequence = MusicSeq::MUSIC_SEQ_CHOCO_MOUNTAIN; + + Props.WaterLevel = -80.0f; } void ChocoMountain::Load() { @@ -157,7 +159,6 @@ void ChocoMountain::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_choco_mountain_addr)); func_802B5CAC(0x238E, 0x31C7, D_8015F590); func_80295C6C(); - D_8015F8E4 = -80.0f; } void ChocoMountain::LoadTextures() { @@ -292,8 +293,6 @@ void ChocoMountain::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_choco_mountain_dl_71B8)); } -void ChocoMountain::Collision() {} - void ChocoMountain::SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { func_8003E37C(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } diff --git a/src/engine/courses/ChocoMountain.h b/src/engine/courses/ChocoMountain.h index 45926ed61..8fd6f6f04 100644 --- a/src/engine/courses/ChocoMountain.h +++ b/src/engine/courses/ChocoMountain.h @@ -35,7 +35,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) override; virtual void Destroy() override; }; diff --git a/src/engine/courses/Course.cpp b/src/engine/courses/Course.cpp index 81e811f82..5b28e6a28 100644 --- a/src/engine/courses/Course.cpp +++ b/src/engine/courses/Course.cpp @@ -241,8 +241,26 @@ void Course::Render(struct UnkStruct_800DC5EC* arg0) { void Course::RenderCredits() { } -void Course::Collision() { + +f32 Course::GetWaterLevel(FVector pos, Collision* collision) { + float highestWater = -FLT_MAX; + bool found = false; + + for (const auto& volume : gWorldInstance.CurrentCourse->WaterVolumes) { + if (pos.x >= volume.MinX && pos.x <= volume.MaxX && + pos.z >= volume.MinZ && pos.z <= volume.MaxZ) { + // Choose the highest water volume the player is over + if (!found || volume.Height > highestWater) { + highestWater = volume.Height; + found = true; + } + } + } + + // If player is not over-top of a water volume then return the courses default water level + return found ? highestWater : gWorldInstance.CurrentCourse->Props.WaterLevel; } + void Course::ScrollingTextures() { } void Course::DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, diff --git a/src/engine/courses/Course.h b/src/engine/courses/Course.h index e7e070535..a547b35b4 100644 --- a/src/engine/courses/Course.h +++ b/src/engine/courses/Course.h @@ -34,6 +34,16 @@ typedef struct SkyboxColours { RGB8 FloorTopLeft; } SkyboxColours; +// Extends infinitely in the Y direction +// If a player is overtop of a water volume then it should use its height +struct WaterVolume { + float Height; // Y coordinate of the Water level + float MinX; + float MaxX; + float MinZ; + float MaxZ; +}; + typedef struct Properties { const char* Id; char Name[128]; @@ -63,6 +73,7 @@ typedef struct Properties { SkyboxColours Skybox; const course_texture *textures; enum MusicSeq Sequence; + float WaterLevel; // Used for effects, and Lakitu pick up height. Not necessarily the visual water model height. const char* TrackModel; #ifdef __cplusplus @@ -209,6 +220,10 @@ class Course { public: Properties Props; + // This allows multiple water levels in a map. + // Ex. DK Jungle where there's a waterfall and you can drive above and below it. + std::vector WaterVolumes; + const char* vtx = nullptr; const char* gfx = nullptr; size_t gfxSize = 0; @@ -245,7 +260,7 @@ public: virtual void Render(struct UnkStruct_800DC5EC*); virtual void RenderCredits(); virtual void Waypoints(Player* player, int8_t playerId); - virtual void Collision(); + virtual f32 GetWaterLevel(FVector pos, Collision* collision); virtual void ScrollingTextures(); virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection); virtual void Destroy(); diff --git a/src/engine/courses/DKJungle.cpp b/src/engine/courses/DKJungle.cpp index 9d4d0674a..e47d86989 100644 --- a/src/engine/courses/DKJungle.cpp +++ b/src/engine/courses/DKJungle.cpp @@ -14,6 +14,7 @@ extern "C" { #include "main.h" + #include "common_structs.h" #include "camera.h" #include "course_offsets.h" #include "code_800029B0.h" @@ -132,6 +133,8 @@ DKJungle::DKJungle() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {22, 145, 22}; Props.Sequence = MusicSeq::MUSIC_SEQ_DK_JUNGLE; + + Props.WaterLevel = -475.0f; } void DKJungle::Load() { @@ -139,11 +142,41 @@ void DKJungle::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_dks_jungle_parkway_addr)); func_80295C6C(); - D_8015F8E4 = -475.0f; // d_course_dks_jungle_parkway_packed_dl_3FA8 find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07003FA8), 120, 255, 255, 255); } +f32 DKJungle::GetWaterLevel(FVector pos, Collision* collision) { + int32_t temp_v1 = get_track_section_id(collision->meshIndexZX) & 0xFF; + + if (temp_v1 == 0xFF) { + if ((get_surface_type(collision->meshIndexZX) & 0xFF) == CAVE) { + return -475.0f; + } + if (pos.x > -478.0f) { + return -33.9f; + } + if (pos.x < -838.0f) { + return -475.0f; + } + if (pos.z > -436.0f) { + return -475.0f; + } + if (pos.z < -993.0f) { + return -33.9f; + } + if (pos.z < pos.x) { + return -475.0f; + } + + return -33.9f; + } + if (temp_v1 >= 0x14) { + return -475.0f; + } + return -33.9f; +} + void DKJungle::LoadTextures() { dma_textures(gTextureDksJungleParkwayKiwanoFruit1, 0x0000032FU, 0x00000400U); dma_textures(gTextureDksJungleParkwayKiwanoFruit2, 0x00000369U, 0x00000400U); @@ -286,8 +319,6 @@ void DKJungle::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_dks_jungle_parkway_dl_13C30)); } -void DKJungle::Collision() {} - void DKJungle::SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { func_8003F138(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } diff --git a/src/engine/courses/DKJungle.h b/src/engine/courses/DKJungle.h index 71cc1e582..158be2185 100644 --- a/src/engine/courses/DKJungle.h +++ b/src/engine/courses/DKJungle.h @@ -26,6 +26,7 @@ public: // course_texture* textures, const char* displaylists, size_t dlSize); virtual void Load() override; virtual void LoadTextures() override; + virtual f32 GetWaterLevel(FVector pos, Collision* collision) override; virtual void BeginPlay() override; //virtual void InitClouds() override; virtual void MinimapSettings() override; @@ -38,7 +39,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void ScrollingTextures() override; diff --git a/src/engine/courses/DoubleDeck.cpp b/src/engine/courses/DoubleDeck.cpp index 236f9b4d5..788ae80cf 100644 --- a/src/engine/courses/DoubleDeck.cpp +++ b/src/engine/courses/DoubleDeck.cpp @@ -112,7 +112,7 @@ void DoubleDeck::Load() { generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual((void*)0x07000738), 1); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void DoubleDeck::LoadTextures() { @@ -168,8 +168,6 @@ void DoubleDeck::Render(struct UnkStruct_800DC5EC* arg0) { void DoubleDeck::RenderCredits() {} -void DoubleDeck::Collision() {} - void DoubleDeck::Waypoints(Player* player, int8_t playerId) { player->nearestWaypointId = 0; } diff --git a/src/engine/courses/DoubleDeck.h b/src/engine/courses/DoubleDeck.h index d821187c8..5d2787a56 100644 --- a/src/engine/courses/DoubleDeck.h +++ b/src/engine/courses/DoubleDeck.h @@ -36,7 +36,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void Destroy() override; }; diff --git a/src/engine/courses/FrappeSnowland.cpp b/src/engine/courses/FrappeSnowland.cpp index 4885d1f53..ab85c7270 100644 --- a/src/engine/courses/FrappeSnowland.cpp +++ b/src/engine/courses/FrappeSnowland.cpp @@ -112,6 +112,8 @@ FrappeSnowland::FrappeSnowland() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {0, 99, 164}; Props.Sequence = MusicSeq::MUSIC_SEQ_FRAPPE_SNOWLAND; + + Props.WaterLevel = -50.0f; } void FrappeSnowland::Load() { @@ -119,7 +121,6 @@ void FrappeSnowland::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_frappe_snowland_addr)); func_80295C6C(); - D_8015F8E4 = -50.0f; } void FrappeSnowland::LoadTextures() { @@ -246,8 +247,6 @@ void FrappeSnowland::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_frappe_snowland_dl_76A0)); } -void FrappeSnowland::Collision() {} - void FrappeSnowland::Waypoints(Player* player, int8_t playerId) { s16 waypoint = gNearestWaypointByPlayerId[playerId]; diff --git a/src/engine/courses/FrappeSnowland.h b/src/engine/courses/FrappeSnowland.h index 8ecbdbf46..0611bf5a5 100644 --- a/src/engine/courses/FrappeSnowland.h +++ b/src/engine/courses/FrappeSnowland.h @@ -39,7 +39,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void ScrollingTextures() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void Destroy() override; diff --git a/src/engine/courses/Harbour.cpp b/src/engine/courses/Harbour.cpp index 6653f6f0a..4d5a831a7 100644 --- a/src/engine/courses/Harbour.cpp +++ b/src/engine/courses/Harbour.cpp @@ -611,7 +611,7 @@ void Harbour::Load() { parse_course_displaylists((TrackSectionsI*)harbour_surfaces); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void Harbour::LoadTextures() { @@ -840,8 +840,6 @@ void Harbour::Render(struct UnkStruct_800DC5EC* arg0) { void Harbour::RenderCredits() { } -void Harbour::Collision() {} - void Harbour::Destroy() { } bool Harbour::IsMod() { diff --git a/src/engine/courses/Harbour.h b/src/engine/courses/Harbour.h index dadb78f5a..54e39084b 100644 --- a/src/engine/courses/Harbour.h +++ b/src/engine/courses/Harbour.h @@ -35,7 +35,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Destroy() override; virtual bool IsMod() override; }; diff --git a/src/engine/courses/KalimariDesert.cpp b/src/engine/courses/KalimariDesert.cpp index 20f06df71..c0d48561e 100644 --- a/src/engine/courses/KalimariDesert.cpp +++ b/src/engine/courses/KalimariDesert.cpp @@ -124,7 +124,7 @@ void KalimariDesert::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_kalimari_desert_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void KalimariDesert::LoadTextures() { @@ -291,6 +291,4 @@ void KalimariDesert::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_kalimari_desert_dl_22E00)); } -void KalimariDesert::Collision() {} - void KalimariDesert::Destroy() { } diff --git a/src/engine/courses/KalimariDesert.h b/src/engine/courses/KalimariDesert.h index 0a07fcbb3..61d717231 100644 --- a/src/engine/courses/KalimariDesert.h +++ b/src/engine/courses/KalimariDesert.h @@ -37,7 +37,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Destroy() override; private: diff --git a/src/engine/courses/KoopaTroopaBeach.cpp b/src/engine/courses/KoopaTroopaBeach.cpp index f9a081097..8435e220f 100644 --- a/src/engine/courses/KoopaTroopaBeach.cpp +++ b/src/engine/courses/KoopaTroopaBeach.cpp @@ -123,6 +123,10 @@ KoopaTroopaBeach::KoopaTroopaBeach() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {48, 152, 120}; Props.Sequence = MusicSeq::MUSIC_SEQ_KOOPA_TROOPA_BEACH; + + Props.WaterLevel = 0.0f; + gWaterVelocity = -0.1f; + WaterVolumes.push_back({0.8f, 67.0f, 239.0f, 2233.0f, 2405.0f}); } void KoopaTroopaBeach::Load() { @@ -264,21 +268,20 @@ void KoopaTroopaBeach::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_koopa_troopa_beach_dl_18D68)); } -void KoopaTroopaBeach::Collision() {} - void KoopaTroopaBeach::SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { func_8003E37C(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } void KoopaTroopaBeach::ScrollingTextures() { // clang-format off - if (D_8015F8E8 < 0.0f) { - if (D_8015F8E4 < -20.0f) { D_8015F8E8 *= -1.0f; } + // This flips the velocity from 0.1f to -0.1f + if (gWaterVelocity < 0.0f) { + if (Props.WaterLevel < -20.0f) { gWaterVelocity *= -1.0f; } } else { - if (D_8015F8E4 > 0.0f) { D_8015F8E8 *= -1.0f; } + if (Props.WaterLevel > 0.0f) { gWaterVelocity *= -1.0f; } } // clang-format on - D_8015F8E4 += D_8015F8E8; + Props.WaterLevel += gWaterVelocity; D_802B87BC += 9; if (D_802B87BC > 255) { @@ -330,7 +333,7 @@ void KoopaTroopaBeach::DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pat break; } vector[0] = 0.0f; - vector[1] = D_8015F8E4; + vector[1] = Props.WaterLevel; vector[2] = 0.0f; mtxf_translate(matrix, vector); render_set_position(matrix, 0); diff --git a/src/engine/courses/KoopaTroopaBeach.h b/src/engine/courses/KoopaTroopaBeach.h index 490dfe8ab..cff01b2a2 100644 --- a/src/engine/courses/KoopaTroopaBeach.h +++ b/src/engine/courses/KoopaTroopaBeach.h @@ -40,7 +40,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void SomeCollisionThing(Player *player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) override; virtual void ScrollingTextures() override; virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection) override; diff --git a/src/engine/courses/LuigiRaceway.cpp b/src/engine/courses/LuigiRaceway.cpp index 07ea87dff..f2e32634b 100644 --- a/src/engine/courses/LuigiRaceway.cpp +++ b/src/engine/courses/LuigiRaceway.cpp @@ -156,7 +156,7 @@ void LuigiRaceway::Load() { parse_course_displaylists((TrackSectionsI*) LOAD_ASSET_RAW(d_course_luigi_raceway_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void LuigiRaceway::LoadTextures() { @@ -444,9 +444,6 @@ void LuigiRaceway::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*) (d_course_luigi_raceway_dl_FD40)); } -void LuigiRaceway::Collision() { -} - void LuigiRaceway::SomeCollisionThing(Player* player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { func_8003E9EC(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); diff --git a/src/engine/courses/LuigiRaceway.h b/src/engine/courses/LuigiRaceway.h index c5e089d81..7ba5d2d7e 100644 --- a/src/engine/courses/LuigiRaceway.h +++ b/src/engine/courses/LuigiRaceway.h @@ -40,7 +40,6 @@ class LuigiRaceway : public Course { virtual void SetStaffGhost() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void SomeCollisionThing(Player* player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) override; virtual void Destroy() override; diff --git a/src/engine/courses/MarioRaceway.cpp b/src/engine/courses/MarioRaceway.cpp index e54714d77..238ce9154 100644 --- a/src/engine/courses/MarioRaceway.cpp +++ b/src/engine/courses/MarioRaceway.cpp @@ -158,7 +158,7 @@ void MarioRaceway::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_mario_raceway_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void MarioRaceway::LoadTextures() { @@ -390,8 +390,6 @@ void MarioRaceway::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_mario_raceway_dl_9348)); } -void MarioRaceway::Collision() {} - void MarioRaceway::CreditsSpawnActors() { dma_textures(gTextureTrees1, 0x35B, 0x800); spawn_foliage((struct ActorSpawnData*)LOAD_ASSET_RAW(d_course_mario_raceway_tree_spawns)); diff --git a/src/engine/courses/MarioRaceway.h b/src/engine/courses/MarioRaceway.h index a91226dbe..f03d18667 100644 --- a/src/engine/courses/MarioRaceway.h +++ b/src/engine/courses/MarioRaceway.h @@ -36,7 +36,6 @@ public: virtual void SetStaffGhost() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void CreditsSpawnActors() override; virtual void Destroy() override; }; diff --git a/src/engine/courses/MooMooFarm.cpp b/src/engine/courses/MooMooFarm.cpp index 4b5fdea67..71ac6b467 100644 --- a/src/engine/courses/MooMooFarm.cpp +++ b/src/engine/courses/MooMooFarm.cpp @@ -142,7 +142,7 @@ void MooMooFarm::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_moo_moo_farm_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void MooMooFarm::LoadTextures() { @@ -445,8 +445,6 @@ void MooMooFarm::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_moo_moo_farm_dl_14088)); } -void MooMooFarm::Collision() {} - void MooMooFarm::CreditsSpawnActors() { dma_textures(gTextureTrees4Left, 0x3E8, 0x800); dma_textures(gTextureTrees4Right, 0x3E8, 0x800); diff --git a/src/engine/courses/MooMooFarm.h b/src/engine/courses/MooMooFarm.h index 8b8b1b811..ec69f47dc 100644 --- a/src/engine/courses/MooMooFarm.h +++ b/src/engine/courses/MooMooFarm.h @@ -36,7 +36,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void CreditsSpawnActors() override; virtual void Destroy() override; }; diff --git a/src/engine/courses/PodiumCeremony.cpp b/src/engine/courses/PodiumCeremony.cpp index e461a2636..830182aa4 100644 --- a/src/engine/courses/PodiumCeremony.cpp +++ b/src/engine/courses/PodiumCeremony.cpp @@ -147,6 +147,8 @@ PodiumCeremony::PodiumCeremony() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {255, 224, 240}; Props.Sequence = MusicSeq::MUSIC_SEQ_UNKNOWN; + + Props.WaterLevel = -60.0f; } void PodiumCeremony::Load() { @@ -154,7 +156,6 @@ void PodiumCeremony::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_royal_raceway_addr)); func_80295C6C(); - D_8015F8E4 = -60.0f; } void PodiumCeremony::LoadTextures() { @@ -296,6 +297,4 @@ void PodiumCeremony::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_royal_raceway_dl_D8E8)); } -void PodiumCeremony::Collision() {} - void PodiumCeremony::Destroy() { } diff --git a/src/engine/courses/PodiumCeremony.h b/src/engine/courses/PodiumCeremony.h index 140d95889..cbafffec7 100644 --- a/src/engine/courses/PodiumCeremony.h +++ b/src/engine/courses/PodiumCeremony.h @@ -37,6 +37,5 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Destroy() override; }; diff --git a/src/engine/courses/RainbowRoad.cpp b/src/engine/courses/RainbowRoad.cpp index 2f6e2e070..54f063824 100644 --- a/src/engine/courses/RainbowRoad.cpp +++ b/src/engine/courses/RainbowRoad.cpp @@ -109,6 +109,8 @@ RainbowRoad::RainbowRoad() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {0, 0, 0}; Props.Sequence = MusicSeq::MUSIC_SEQ_RAINBOW_ROAD; + + Props.WaterLevel = 0.0f; } void RainbowRoad::Load() { @@ -117,7 +119,6 @@ void RainbowRoad::Load() { D_800DC5C8 = 1; parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_rainbow_road_addr)); func_80295C6C(); - D_8015F8E4 = 0.0f; // d_course_rainbow_road_packed_dl_2068 find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07002068), -0x6A, 255, 255, 255); // d_course_rainbow_road_packed_dl_1E18 @@ -224,8 +225,6 @@ void RainbowRoad::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_rainbow_road_dl_16220)); } -void RainbowRoad::Collision() {} - void RainbowRoad::Waypoints(Player* player, int8_t playerId) { player->nearestWaypointId = gCopyNearestWaypointByPlayerId[playerId]; } diff --git a/src/engine/courses/RainbowRoad.h b/src/engine/courses/RainbowRoad.h index e5262f170..ed467a6a1 100644 --- a/src/engine/courses/RainbowRoad.h +++ b/src/engine/courses/RainbowRoad.h @@ -39,7 +39,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection) override; virtual void CreditsSpawnActors() override; diff --git a/src/engine/courses/RoyalRaceway.cpp b/src/engine/courses/RoyalRaceway.cpp index 1394cc981..80428f7d1 100644 --- a/src/engine/courses/RoyalRaceway.cpp +++ b/src/engine/courses/RoyalRaceway.cpp @@ -146,6 +146,8 @@ RoyalRaceway::RoyalRaceway() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {255, 224, 240}; Props.Sequence = MusicSeq::MUSIC_SEQ_RACEWAYS_WARIO_STADIUM; + + Props.WaterLevel = -60.0f; } void RoyalRaceway::Load() { @@ -153,7 +155,6 @@ void RoyalRaceway::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_royal_raceway_addr)); func_80295C6C(); - D_8015F8E4 = -60.0f; } void RoyalRaceway::LoadTextures() { @@ -298,8 +299,6 @@ void RoyalRaceway::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_royal_raceway_dl_D8E8)); } -void RoyalRaceway::Collision() {} - void RoyalRaceway::Waypoints(Player* player, int8_t playerId) { s16 waypoint = gNearestWaypointByPlayerId[playerId]; if ((waypoint >= 0x258) && (waypoint < 0x2A4)) { diff --git a/src/engine/courses/RoyalRaceway.h b/src/engine/courses/RoyalRaceway.h index 2a3095bc8..9419bc891 100644 --- a/src/engine/courses/RoyalRaceway.h +++ b/src/engine/courses/RoyalRaceway.h @@ -36,7 +36,6 @@ public: virtual void SetStaffGhost() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void ScrollingTextures() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void Destroy() override; diff --git a/src/engine/courses/SherbetLand.cpp b/src/engine/courses/SherbetLand.cpp index 5c0fa7f8c..a47bed6e7 100644 --- a/src/engine/courses/SherbetLand.cpp +++ b/src/engine/courses/SherbetLand.cpp @@ -111,6 +111,8 @@ SherbetLand::SherbetLand() { Props.Skybox.FloorBottomLeft = {128, 184, 248}; Props.Skybox.FloorTopLeft = {216, 232, 248}; Props.Sequence = MusicSeq::MUSIC_SEQ_FRAPPE_SNOWLAND; + + Props.WaterLevel = -18.0f; } void SherbetLand::Load() { @@ -118,13 +120,19 @@ void SherbetLand::Load() { parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_sherbet_land_addr)); func_80295C6C(); - D_8015F8E4 = -18.0f; // d_course_sherbet_land_packed_dl_1EB8 find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07001EB8), -0x4C, 255, 255, 255); // d_course_sherbet_land_packed_dl_2308 find_vtx_and_set_colours(segmented_gfx_to_virtual((void*)0x07002308), -0x6A, 255, 255, 255); } +f32 SherbetLand::GetWaterLevel(FVector pos, Collision* collision) { + if ((get_surface_type(collision->meshIndexZX) & 0xFF) == SNOW) { + return (f32) (gCourseMinY - 0xA); + } + return Props.WaterLevel; +} + void SherbetLand::LoadTextures() { } @@ -256,8 +264,6 @@ void SherbetLand::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_sherbet_land_dl_9AE8)); } -void SherbetLand::Collision() {} - void SherbetLand::DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection) { Mat4 matrix; @@ -274,7 +280,7 @@ void SherbetLand::DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCoun render_course_segments(sherbet_land_dls_2, screen); gDPSetAlphaCompare(gDisplayListHead++, G_AC_NONE); - if ((func_80290C20(screen->camera) == 1) && (func_802AAB4C(screen->player) < screen->player->pos[1])) { + if ((func_80290C20(screen->camera) == 1) && (get_water_level(screen->player) < screen->player->pos[1])) { gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER); gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2); diff --git a/src/engine/courses/SherbetLand.h b/src/engine/courses/SherbetLand.h index 13e41e592..e6c25df1b 100644 --- a/src/engine/courses/SherbetLand.h +++ b/src/engine/courses/SherbetLand.h @@ -25,6 +25,7 @@ public: // virtual void Load(const char* courseVtx, // course_texture* textures, const char* displaylists, size_t dlSize); virtual void Load() override; + virtual f32 GetWaterLevel(FVector pos, Collision* collision) override; virtual void LoadTextures() override; virtual void BeginPlay() override; virtual void MinimapSettings() override; @@ -37,7 +38,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, uint16_t playerDirection) override; virtual void CreditsSpawnActors() override; virtual void Destroy() override; diff --git a/src/engine/courses/Skyscraper.cpp b/src/engine/courses/Skyscraper.cpp index e87dfa280..9f3af6746 100644 --- a/src/engine/courses/Skyscraper.cpp +++ b/src/engine/courses/Skyscraper.cpp @@ -125,6 +125,8 @@ Skyscraper::Skyscraper() { Props.Skybox.FloorBottomLeft = {0, 0, 0}; Props.Skybox.FloorTopLeft = {0, 0, 0}; Props.Sequence = MusicSeq::MUSIC_SEQ_BATTLE_ARENAS; + + Props.WaterLevel = -480.0f; } void Skyscraper::Load() { @@ -135,8 +137,6 @@ void Skyscraper::Load() { // d_course_skyscraper_packed_dl_258 generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual((void*)0x07000258), 1); func_80295C6C(); - - D_8015F8E4 = -480.0f; } void Skyscraper::LoadTextures() { @@ -204,8 +204,6 @@ void Skyscraper::Render(struct UnkStruct_800DC5EC* arg0) { void Skyscraper::RenderCredits() {} -void Skyscraper::Collision() {} - void Skyscraper::Waypoints(Player* player, int8_t playerId) { player->nearestWaypointId = 0; } diff --git a/src/engine/courses/Skyscraper.h b/src/engine/courses/Skyscraper.h index b9b998f73..774603c0e 100644 --- a/src/engine/courses/Skyscraper.h +++ b/src/engine/courses/Skyscraper.h @@ -36,7 +36,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void Destroy() override; }; diff --git a/src/engine/courses/TestCourse.cpp b/src/engine/courses/TestCourse.cpp index 1d35cfcc4..54ac990bc 100644 --- a/src/engine/courses/TestCourse.cpp +++ b/src/engine/courses/TestCourse.cpp @@ -137,7 +137,7 @@ void TestCourse::Load() { parse_course_displaylists((TrackSectionsI*)test_course_addr); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void TestCourse::LoadTextures() { @@ -365,8 +365,6 @@ void TestCourse::Render(struct UnkStruct_800DC5EC* arg0) { void TestCourse::RenderCredits() { } -void TestCourse::Collision() {} - void TestCourse::Destroy() { } bool TestCourse::IsMod() { diff --git a/src/engine/courses/TestCourse.h b/src/engine/courses/TestCourse.h index 3421304e2..192400252 100644 --- a/src/engine/courses/TestCourse.h +++ b/src/engine/courses/TestCourse.h @@ -35,7 +35,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Destroy() override; virtual bool IsMod() override; }; diff --git a/src/engine/courses/ToadsTurnpike.cpp b/src/engine/courses/ToadsTurnpike.cpp index 38e659a26..5060c3f0c 100644 --- a/src/engine/courses/ToadsTurnpike.cpp +++ b/src/engine/courses/ToadsTurnpike.cpp @@ -150,7 +150,7 @@ void ToadsTurnpike::Load() { D_802B87B4 = 1000; parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_toads_turnpike_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void ToadsTurnpike::LoadTextures() { @@ -289,6 +289,4 @@ void ToadsTurnpike::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_toads_turnpike_dl_23930)); } -void ToadsTurnpike::Collision() {} - void ToadsTurnpike::Destroy() { } diff --git a/src/engine/courses/ToadsTurnpike.h b/src/engine/courses/ToadsTurnpike.h index a73e3829f..310fe0d17 100644 --- a/src/engine/courses/ToadsTurnpike.h +++ b/src/engine/courses/ToadsTurnpike.h @@ -36,7 +36,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void Destroy() override; private: size_t _numTrucks = 7; diff --git a/src/engine/courses/WarioStadium.cpp b/src/engine/courses/WarioStadium.cpp index 71c1b1e28..99cb0678a 100644 --- a/src/engine/courses/WarioStadium.cpp +++ b/src/engine/courses/WarioStadium.cpp @@ -141,7 +141,7 @@ void WarioStadium::Load() { parse_course_displaylists((TrackSectionsI*) LOAD_ASSET_RAW(d_course_wario_stadium_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; // d_course_wario_stadium_packed_dl_C50 find_vtx_and_set_colours(segmented_gfx_to_virtual((void*) 0x07000C50), 100, 255, 255, 255); // d_course_wario_stadium_packed_dl_BD8 @@ -365,9 +365,6 @@ void WarioStadium::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*) (d_course_wario_stadium_dl_CA78)); } -void WarioStadium::Collision() { -} - void WarioStadium::SomeCollisionThing(Player* player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) { func_8003EE2C(player, arg1, arg2, arg3, arg4, arg5, arg6, arg7); diff --git a/src/engine/courses/WarioStadium.h b/src/engine/courses/WarioStadium.h index 72f796cc7..1cc62433d 100644 --- a/src/engine/courses/WarioStadium.h +++ b/src/engine/courses/WarioStadium.h @@ -39,7 +39,6 @@ class WarioStadium : public Course { virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void SomeCollisionThing(Player* player, Vec3f arg1, Vec3f arg2, Vec3f arg3, f32* arg4, f32* arg5, f32* arg6, f32* arg7) override; virtual void DrawWater(struct UnkStruct_800DC5EC* screen, uint16_t pathCounter, uint16_t cameraRot, diff --git a/src/engine/courses/YoshiValley.cpp b/src/engine/courses/YoshiValley.cpp index a328678bd..41cca62b6 100644 --- a/src/engine/courses/YoshiValley.cpp +++ b/src/engine/courses/YoshiValley.cpp @@ -130,7 +130,7 @@ void YoshiValley::Load() { set_track_light_direction(&lights4, -0x38F0, 0x1C70, 1); parse_course_displaylists((TrackSectionsI*)LOAD_ASSET_RAW(d_course_yoshi_valley_addr)); func_80295C6C(); - D_8015F8E4 = gCourseMinY - 10.0f; + Props.WaterLevel = gCourseMinY - 10.0f; } void YoshiValley::LoadTextures() { @@ -237,8 +237,6 @@ void YoshiValley::RenderCredits() { gSPDisplayList(gDisplayListHead++, (Gfx*)(d_course_yoshi_valley_dl_18020)); } -void YoshiValley::Collision() {} - void YoshiValley::Waypoints(Player* player, int8_t playerId) { player->nearestWaypointId = gCopyNearestWaypointByPlayerId[playerId]; } diff --git a/src/engine/courses/YoshiValley.h b/src/engine/courses/YoshiValley.h index a986cd902..fcad27672 100644 --- a/src/engine/courses/YoshiValley.h +++ b/src/engine/courses/YoshiValley.h @@ -37,7 +37,6 @@ public: virtual void MinimapFinishlinePosition() override; virtual void Render(struct UnkStruct_800DC5EC*) override; virtual void RenderCredits() override; - virtual void Collision() override; virtual void ScrollingTextures() override; virtual void Waypoints(Player* player, int8_t playerId) override; virtual void CreditsSpawnActors() override; diff --git a/src/engine/editor/Editor.cpp b/src/engine/editor/Editor.cpp index ce3e7c7ad..7be5c7840 100644 --- a/src/engine/editor/Editor.cpp +++ b/src/engine/editor/Editor.cpp @@ -7,6 +7,7 @@ #include "Editor.h" #include "Collision.h" #include "Light.h" +#include "Water.h" #include "port/Engine.h" #include @@ -117,6 +118,11 @@ namespace Editor { eGameObjects.push_back(new LightObject(name, pos, rot)); } + WaterVolumeObject* Editor::AddWaterVolume(const char* name, FVector* pos) { + eGameObjects.push_back(new WaterVolumeObject(name, pos)); + return reinterpret_cast(&eGameObjects.back()); + } + void Editor::ClearObjects() { for (auto& obj : eGameObjects) { delete obj; diff --git a/src/engine/editor/Editor.h b/src/engine/editor/Editor.h index 6466ad922..3a2d1b524 100644 --- a/src/engine/editor/Editor.h +++ b/src/engine/editor/Editor.h @@ -4,6 +4,7 @@ #include #include #include "GameObject.h" +#include "Water.h" #ifdef __cplusplus @@ -23,6 +24,7 @@ public: void Load(); GameObject* AddObject(const char* name, FVector* pos, IRotator* rot, FVector* scale, Gfx* model, float collScale, GameObject::CollisionType collision, float boundingBoxSize, int32_t* despawnFlag, int32_t despawnValue); void AddLight(const char* name, FVector* pos, s8* rot); + WaterVolumeObject* AddWaterVolume(const char* name, FVector* pos); void ClearObjects(); void RemoveObject(); void SelectObjectFromSceneExplorer(GameObject* object); @@ -38,7 +40,6 @@ private: s32 Inverse(MtxF* src, MtxF* dest); void Copy(MtxF* src, MtxF* dest); void Clear(MtxF* mf); - }; } #endif diff --git a/src/engine/editor/Water.cpp b/src/engine/editor/Water.cpp new file mode 100644 index 000000000..954efdab8 --- /dev/null +++ b/src/engine/editor/Water.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include "Water.h" +#include "../CoreMath.h" +#include +#include "../World.h" + +#include "port/Engine.h" + +#include "engine/actors/Ship.h" +#include "port/Game.h" +#include "Gizmo.h" + +#include "EditorMath.h" + +extern "C" { +#include "main.h" +#include "common_structs.h" +#include "defines.h" +#include "actors.h" +#include "camera.h" +#include "math_util.h" +#include "math_util_2.h" +} + +namespace Editor { + + size_t WaterVolumeObject::NumWaterVolumes = 0; + + WaterVolumeObject::WaterVolumeObject(const char* name, FVector* pos) : GameObject() { + Name = name; + Pos = &WaterPos; + Rot = &WaterRot; + Scale = &WaterScale; + + DespawnFlag = &_despawnFlag; + DespawnValue = -1; + + Collision = CollisionType::VTX_INTERSECT; + + NumWaterVolumes += 1; + } + + void WaterVolumeObject::Load() { + } + + void WaterVolumeObject::Tick() { + } + void WaterVolumeObject::Draw() { + Mat4 mtx; + gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH); + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); + IRotator rot = WaterRot; + rot.yaw += 0x4000; + ApplyMatrixTransformations(mtx, WaterPos, rot, WaterScale); + Editor_AddMatrix(mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gDisplayListHead++, (Gfx*)"__OTR__editor/water/water_plane"); + } +} diff --git a/src/engine/editor/Water.h b/src/engine/editor/Water.h new file mode 100644 index 000000000..edd57ded5 --- /dev/null +++ b/src/engine/editor/Water.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "CoreMath.h" +#include "GameObject.h" + +namespace Editor { + +class WaterVolumeObject : public GameObject { +public: + WaterVolumeObject(const char* name, FVector* pos); + + virtual void Draw() override; + virtual void Tick() override; + virtual void Load() override; + + static size_t NumWaterVolumes; + FVector WaterPos = FVector(0, 100, 0); + IRotator WaterRot = IRotator(0, 0, 0); + FVector WaterScale = FVector(0.1, 0.1, 0.1); + s32 _despawnFlag = 0; +}; +} diff --git a/src/player_controller.c b/src/player_controller.c index f5a269527..f5bbba70b 100644 --- a/src/player_controller.c +++ b/src/player_controller.c @@ -1762,7 +1762,7 @@ void func_8002C17C(Player* player, s8 playerId) { } void func_8002C4F8(Player* player, s8 arg1) { - D_801652A0[arg1] = func_802AAB4C(player); + D_801652A0[arg1] = get_water_level(player); if (player->pos[1] <= D_801652A0[arg1]) { player->unk_0DE |= 0x0002; } else { diff --git a/src/port/Game.cpp b/src/port/Game.cpp index 29327c704..9aee19046 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -48,6 +48,7 @@ #include "engine/editor/Editor.h" #include "engine/editor/EditorMath.h" #include "engine/editor/SceneManager.h" +#include "engine/editor/Water.h" extern "C" { #include "main.h" @@ -393,13 +394,16 @@ void CM_BeginPlay() { Course* course = gWorldInstance.CurrentCourse; if (course) { - // Do not spawn finishline in credits or battle mode. And if bSpawnFinishline. if ((gGamestate != CREDITS_SEQUENCE) && (gGamestate != BATTLE) && (course->bSpawnFinishline)) { gWorldInstance.AddActor(new AFinishline(course->FinishlineSpawnPoint)); } gEditor.AddLight("Sun", nullptr, D_800DC610[1].l->l.dir); + Editor::WaterVolumeObject* volume = gEditor.AddWaterVolume("Water", nullptr); + f32 waterLevel = gWorldInstance.CurrentCourse->Props.WaterLevel; + volume->WaterPos = FVector(0, waterLevel, 0); + course->BeginPlay(); } } @@ -730,6 +734,11 @@ void CM_ActorCollision(Player* player, Actor* actor) { } } +f32 CM_GetWaterLevel(Vec3f pos, Collision* collision) { + FVector fPos = {pos[0], pos[1], pos[2]}; + return gWorldInstance.CurrentCourse->GetWaterLevel(fPos, collision); +} + void* GetMarioRaceway(void) { return gMarioRaceway; } diff --git a/src/port/Game.h b/src/port/Game.h index 0cfdb9a9f..c21cb0326 100644 --- a/src/port/Game.h +++ b/src/port/Game.h @@ -157,6 +157,8 @@ size_t CM_FindActorIndex(struct Actor* actor); void CM_ActorCollision(Player* player, struct Actor* actor); void CM_CleanWorld(void); +f32 CM_GetWaterLevel(Vec3f pos, Collision* collision); + void* GetMarioRaceway(void); void* GetLuigiRaceway(void); diff --git a/src/port/ui/ContentBrowser.cpp b/src/port/ui/ContentBrowser.cpp index 2cba6086c..8ce6c2f3f 100644 --- a/src/port/ui/ContentBrowser.cpp +++ b/src/port/ui/ContentBrowser.cpp @@ -158,6 +158,13 @@ namespace Editor { FVector pos = GetPositionAheadOfCamera(300.0f); size_t i_actor = 0; + std::string volumeLabel = fmt::format("{}##{}", "Water Volume", i_actor); + if (ImGui::Button(volumeLabel.c_str())) { + auto* volume = gEditor.AddWaterVolume("Water Volume", nullptr); + volume->WaterPos = pos; + i_actor += 1; + } + for (const auto& actor : ActorList) { if ((i_actor != 0) && (i_actor % 10 == 0)) { } else { @@ -166,7 +173,7 @@ namespace Editor { std::string label = fmt::format("{}##{}", actor.first, i_actor); if (ImGui::Button(label.c_str())) { - gWorldInstance.AddActor(actor.second(pos)); // Pass pos to the lambda + gWorldInstance.AddActor(actor.second(pos)); } i_actor += 1; } @@ -184,7 +191,7 @@ namespace Editor { std::string label = fmt::format("{}##{}", object.first, i_object); if (ImGui::Button(label.c_str())) { - gWorldInstance.AddObject(object.second(pos)); // Pass pos to the lambda + gWorldInstance.AddObject(object.second(pos)); } i_object += 1; } diff --git a/src/racing/collision.c b/src/racing/collision.c index c3e210c9a..751a49a03 100644 --- a/src/racing/collision.c +++ b/src/racing/collision.c @@ -38,76 +38,8 @@ void func_802AAAAC(Collision* collision) { vec3f_set(collision->orientationVector, 0.0f, 1.0f, 0.0f); } -f32 func_802AAB4C(Player* player) { - f32 playerX; - f32 playerZ; - s32 temp_v1; - - playerX = player->pos[0]; - playerZ = player->pos[2]; - if (GetCourse() == GetBowsersCastle()) { - if (playerX > 1859.0f) { - return D_8015F8E4; - } - if (playerX < 1549.0f) { - return D_8015F8E4; - } - if (playerZ > -1102.0f) { - return D_8015F8E4; - } - if (playerZ < -1402.0f) { - return D_8015F8E4; - } - return 20.0f; - } else if (GetCourse() == GetKoopaTroopaBeach()) { - if (playerX > 239.0f) { - return D_8015F8E4; - } - if (playerX < 67.0f) { - return D_8015F8E4; - } - if (playerZ > 2405.0f) { - return D_8015F8E4; - } - if (playerZ < 2233.0f) { - return D_8015F8E4; - } - return 0.8f; - } else if (GetCourse() == GetSherbetLand()) { - if ((get_surface_type(player->collision.meshIndexZX) & 0xFF) == SNOW) { - return (f32) (gCourseMinY - 0xA); - } - return D_8015F8E4; - } else if (GetCourse() == GetDkJungle()) { - temp_v1 = get_track_section_id(player->collision.meshIndexZX) & 0xFF; - if (temp_v1 == 0xFF) { - if ((get_surface_type(player->collision.meshIndexZX) & 0xFF) == CAVE) { - return -475.0f; - } - if (playerX > -478.0f) { - return -33.9f; - } - if (playerX < -838.0f) { - return -475.0f; - } - if (playerZ > -436.0f) { - return -475.0f; - } - if (playerZ < -993.0f) { - return -33.9f; - } - if (playerZ < playerX) { - return -475.0f; - } - return -33.9f; - } - if (temp_v1 >= 0x14) { - return -475.0f; - } - return -33.9f; - } else { - return D_8015F8E4; - } +f32 get_water_level(Player* player) { + return CM_GetWaterLevel(player->pos, &player->collision); } s32 check_collision_zx(Collision* collision, f32 boundingBoxSize, f32 posX, f32 posY, f32 posZ, u16 index) { diff --git a/src/racing/collision.h b/src/racing/collision.h index a6c3aca29..4720daf55 100644 --- a/src/racing/collision.h +++ b/src/racing/collision.h @@ -8,7 +8,7 @@ void nullify_displaylist(uintptr_t); void func_802AAAAC(Collision*); -f32 func_802AAB4C(Player*); +f32 get_water_level(Player*); s32 check_collision_zx(Collision*, f32, f32, f32, f32, u16); s32 check_collision_yx(Collision*, f32, f32, f32, f32, u16); s32 check_collision_zy(Collision*, f32, f32, f32, f32, u16); diff --git a/src/racing/render_courses.c b/src/racing/render_courses.c index dde191561..5f5940904 100644 --- a/src/racing/render_courses.c +++ b/src/racing/render_courses.c @@ -299,7 +299,7 @@ void func_8029122C(struct UnkStruct_800DC5EC* arg0, s32 playerId) { // break; // } // vector[0] = 0.0f; - // vector[1] = D_8015F8E4; + // vector[1] = gWaterLevel; // vector[2] = 0.0f; // mtxf_translate(matrix, vector); // render_set_position(matrix, 0); @@ -328,7 +328,7 @@ void func_8029122C(struct UnkStruct_800DC5EC* arg0, s32 playerId) { // render_course_segments(sherbet_land_dls_2, arg0); // gDPSetAlphaCompare(gDisplayListHead++, G_AC_NONE); - // if ((func_80290C20(arg0->camera) == 1) && (func_802AAB4C(player) < player->pos[1])) { + // if ((func_80290C20(arg0->camera) == 1) && (get_water_level(player) < player->pos[1])) { // gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER); // gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE); // gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2); @@ -1521,7 +1521,7 @@ void course_init(void) { // // parse_course_displaylists(d_course_mario_raceway_addr); // // func_80295C6C(); - // // D_8015F8E4 = gCourseMinY - 10.0f; + // // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_CHOCO_MOUNTAIN: // D_800DC5BC = 1; @@ -1552,13 +1552,13 @@ void course_init(void) { // parse_course_displaylists(d_course_choco_mountain_addr); // func_802B5CAC(0x238E, 0x31C7, D_8015F590); // func_80295C6C(); - // D_8015F8E4 = -80.0f; + // gWaterLevel = -80.0f; // break; // case COURSE_BOWSER_CASTLE: // parse_course_displaylists(d_course_bowsers_castle_addr); // func_80295C6C(); // find_vtx_and_set_colours(segmented_gfx_to_virtual(0x07001350), 0x32, 0, 0, 0); - // D_8015F8E4 = -50.0f; + // gWaterLevel = -50.0f; // break; // case COURSE_BANSHEE_BOARDWALK: // // D_800DC5BC = 1; @@ -1568,20 +1568,20 @@ void course_init(void) { // // parse_course_displaylists(d_course_banshee_boardwalk_track_sections); // // func_80295C6C(); // // find_vtx_and_set_colours(segmented_gfx_to_virtual(0x07000878), 128, 0, 0, 0); - // // D_8015F8E4 = -80.0f; + // // gWaterLevel = -80.0f; // break; // case COURSE_YOSHI_VALLEY: { // Lights1 lights4 = gdSPDefLights1(100, 100, 100, 255, 254, 254, 0, 0, 120); // set_track_light_direction(&lights4, -0x38F0, 0x1C70, 1); // parse_course_displaylists(d_course_yoshi_valley_addr); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // } // case COURSE_FRAPPE_SNOWLAND: // parse_course_displaylists(d_course_frappe_snowland_addr); // func_80295C6C(); - // D_8015F8E4 = -50.0f; + // gWaterLevel = -50.0f; // break; // case COURSE_KOOPA_BEACH: // parse_course_displaylists(d_course_koopa_troopa_beach_addr); @@ -1594,17 +1594,17 @@ void course_init(void) { // case COURSE_ROYAL_RACEWAY: // parse_course_displaylists(d_course_royal_raceway_addr); // func_80295C6C(); - // D_8015F8E4 = -60.0f; + // gWaterLevel = -60.0f; // break; // case COURSE_LUIGI_RACEWAY: // parse_course_displaylists(d_course_luigi_raceway_addr); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_MOO_MOO_FARM: // parse_course_displaylists(d_course_moo_moo_farm_addr); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_TOADS_TURNPIKE: // D_801625EC = 43; @@ -1614,17 +1614,17 @@ void course_init(void) { // D_802B87B4 = 1000; // parse_course_displaylists(d_course_toads_turnpike_addr); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_KALIMARI_DESERT: // parse_course_displaylists(d_course_kalimari_desert_addr); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_SHERBET_LAND: // parse_course_displaylists(d_course_sherbet_land_addr); // func_80295C6C(); - // D_8015F8E4 = -18.0f; + // gWaterLevel = -18.0f; // // d_course_sherbet_land_packed_dl_1EB8 // find_vtx_and_set_colours(segmented_gfx_to_virtual(0x07001EB8), -0x4C, 255, 255, 255); // // d_course_sherbet_land_packed_dl_2308 @@ -1634,7 +1634,7 @@ void course_init(void) { // D_800DC5C8 = 1; // parse_course_displaylists(d_course_rainbow_road_addr); // func_80295C6C(); - // D_8015F8E4 = 0.0f; + // gWaterLevel = 0.0f; // // d_course_rainbow_road_packed_dl_2068 // find_vtx_and_set_colours(segmented_gfx_to_virtual(0x07002068), -0x6A, 255, 255, 255); // // d_course_rainbow_road_packed_dl_1E18 @@ -1649,7 +1649,7 @@ void course_init(void) { // case COURSE_WARIO_STADIUM: // parse_course_displaylists(d_course_wario_stadium_addr); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // // d_course_wario_stadium_packed_dl_C50 // find_vtx_and_set_colours(segmented_gfx_to_virtual(0x07000C50), 100, 255, 255, 255); // // d_course_wario_stadium_packed_dl_BD8 @@ -1671,7 +1671,7 @@ void course_init(void) { // // d_course_block_fort_packed_dl_15C0 // generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual(0x070015C0), 1); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_SKYSCRAPER: // // d_course_skyscraper_packed_dl_1110 @@ -1680,18 +1680,18 @@ void course_init(void) { // generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual(0x07000258), 1); // func_80295C6C(); - // D_8015F8E4 = -480.0f; + // gWaterLevel = -480.0f; // break; // case COURSE_DOUBLE_DECK: // // d_course_double_deck_packed_dl_738 // generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual(0x07000738), 1); // func_80295C6C(); - // D_8015F8E4 = gCourseMinY - 10.0f; + // gWaterLevel = gCourseMinY - 10.0f; // break; // case COURSE_DK_JUNGLE: // parse_course_displaylists(d_course_dks_jungle_parkway_addr); // func_80295C6C(); - // D_8015F8E4 = -475.0f; + // gWaterLevel = -475.0f; // // d_course_dks_jungle_parkway_packed_dl_3FA8 // find_vtx_and_set_colours(segmented_gfx_to_virtual(0x07003FA8), 120, 255, 255, 255); // break; @@ -1707,7 +1707,7 @@ void course_init(void) { // // d_course_big_donut_packed_dl_230 // generate_collision_mesh_with_default_section_id((Gfx*) segmented_gfx_to_virtual(0x07000230), 6); // func_80295C6C(); - // D_8015F8E4 = 100.0f; + // gWaterLevel = 100.0f; // break; // } } @@ -1719,13 +1719,13 @@ void func_802966A0(void) { // switch (gCurrentCourseId) { // case COURSE_KOOPA_BEACH: // // clang-format off - // if (D_8015F8E8 < 0.0f) { - // if (D_8015F8E4 < -20.0f) { D_8015F8E8 *= -1.0f; } + // if (gWaterVelocity < 0.0f) { + // if (gWaterLevel < -20.0f) { gWaterVelocity *= -1.0f; } // } else { - // if (D_8015F8E4 > 0.0f) { D_8015F8E8 *= -1.0f; } + // if (gWaterLevel > 0.0f) { gWaterVelocity *= -1.0f; } // } // // clang-format on - // D_8015F8E4 += D_8015F8E8; + // gWaterLevel += gWaterVelocity; // D_802B87BC += 9; // if (D_802B87BC > 255) { @@ -1793,13 +1793,3 @@ void func_802966A0(void) { // break; // } } - -void func_802969F8(void) { - if (GetCourse() == GetMooMooFarm()) { - D_8015F702 = 0; - D_8015F700 = 200; - } else if (GetCourse() == GetKoopaTroopaBeach()) { - D_8015F8E8 = -0.1f; - D_8015F8E4 = 0.0f; - } -} diff --git a/src/racing/render_courses.h b/src/racing/render_courses.h index 26ce0f0dd..5140c2780 100644 --- a/src/racing/render_courses.h +++ b/src/racing/render_courses.h @@ -44,7 +44,6 @@ void func_80295D50(s16, s16); void func_80295D6C(void); void func_80295D88(void); void func_802966A0(void); -void func_802969F8(void); extern s32 D_8015F59C;